Reputation: 1276
I am trying to dynamically allocate a matrix as a continuous memory block
typedef struct
{
uint32_t m_width_u32;
uint32_t m_height_u32;
uint16_t *m_pixels_u16;
}IMG_t;
IMG_t* img = (IMG_t*)malloc(sizeof(IMG_t));
strcpy(lv_path_to_input_image, argv[1]);
FILE* file = fopen(file_name, "r");
fscanf(file, "%d%d", &(img->m_height_u32), &(img->m_width_u32)
img->m_pixels_u16 = malloc( sizeof(*img->m_pixels_u16) * img->m_height_u32 * img->m_width_u32);
for (unsigned int i = 0; i < img->m_height_u32; ++i)
{
for (unsigned int j = 0; j < img->m_width_u32; ++j)
{
fscanf(file, "%d", img->m_pixels_u16 + img->m_height_u32 * i + j);
}
}
fclose(file);
if (img->m_pixels_u16 != NULL)
{
free(img->m_pixels_u16);
}
free(img);
With this allocation I got a HEAP CORRUPTION DETECTED
error, meaning that I use more memory than I allocate.
However, if I extend the allocated memory only with 2 bytes more, heap corruption error is no longer present.
img->m_pixels_u16 = malloc( sizeof(*img->m_pixels_u16) * img->m_height_u32 * img->m_width_u32 +2 );
Can you please help me to understand this behavior?
Upvotes: 1
Views: 68
Reputation: 153417
On 32-bit int
systems, wrong specifier leads to UB.
OP's code is saving to a 4-byte location when it should be 2 byte. This may have benign consequences until the last array element which writes outside the bounds and nicely triggers HEAP CORRUPTION DETECTED
. A +2 allocation hides the issue.
Use matching specifiers and enabled all warnings. A good compiler, well enabled, would have warned about specifier/argument mismatch and saved time.
Also see @Ôrel about wrong index calculation
uint16_t *m_pixels_u16;
...
//fscanf(file, "%d", img->m_pixels_u16 + img->m_height_u32 * i + j);
fscanf(file, "%" SCNu16, img->m_pixels_u16 + img->m_width_u32 * i + j);
// ^^^^^^^^^ ^^^^^^^^^^^
Same issue with (but OP might get "lucky" here)
//fscanf(file, "%d%d", &(img->m_height_u32), &(img->m_width_u32)
fscanf(file, "%" SCNu32 "%" SCNu32, &(img->m_height_u32), &(img->m_width_u32)
Other issues
sizeof(*img->m_pixels_u16) * img->m_height_u32 * img->m_width_u32
may quietly overflow. The below offers overflow detection.
unsigned long long n = 1ULL * img->m_height_u32 * img->m_width_u32;
assert(n <= SIZE_MAX/sizeof(*img->m_pixels_u16));
n *= sizeof(*img->m_pixels_u16);
BTW: good use of p = malloc(sizeof *p * ...)
with
img->m_pixels_u16 = malloc( sizeof(*img->m_pixels_u16) * img->m_height_u32 * img->m_width_u32);
Recommend using that here too.
// IMG_t* img = (IMG_t*)malloc(sizeof(IMG_t));
IMG_t* img = malloc(sizeof *img);
Test not needed. free(NULL)
is OK.
//if (img->m_pixels_u16 != NULL)
//{
free(img->m_pixels_u16);
//}
Yet NULL
test would have been useful right after the malloc()
. Note image size could legitimately be zero.
img->m_pixels_u16 = malloc(n);
if (img->m_pixels_u16 == NULL && n > 0) {
Handle_OutOfMemory();
}
Upvotes: 2
Reputation: 7622
You mix up height and width:
first with i = 0
j
got to img->m_width_u32 - 1
so at the end of the loop you are at
img->m_pixels_u16 + img->m_width_u32 - 1
Next loop with i
set to one
should be
img->m_pixels_u16 + img->m_width_u32 + j
This should be this:
for (unsigned int i = 0; i < img->m_height_u32; ++i)
{
for (unsigned int j = 0; j < img->m_width_u32; ++j)
{
fscanf(file, "%d", img->m_pixels_u16 + img->m_width_u32 * i + j);
}
}
Upvotes: 1