Reading into heap memory I shouldn't have access to? - c

In the example below I have allocated 20 bytes of memory to extend an array by 5 integers. After that I have set the last element to 15 and then reallocated the pointer to 4 bytes (1 integer). Then I print the first 10 elements of the array (it only consists of 6 at this point) and the 9th (which I've previously set to 15) is printed without warnings or errors.
The code :
#include <stdlib.h>
#include <stdio.h>
int main()
{
int arr[5] = {0};
int *ptr = &arr[0];
ptr = malloc(5 * sizeof(int));
arr[9] = 15;
ptr = realloc(ptr, 1 * sizeof(int));
for (int i = 0; i < 10; ++i)
{
printf("%d\n", arr[i]);
}
free(ptr);
return 0;
}
The result after compiling and running :
0
0
0
0
0
32766
681279744
-1123562100
-1261131712
15
My question is as follows : Why is the 9th element of the array still 15? (why am I able to access it?; Shouldn't the allocated memory be at the first free block of memory my compiler finds and not connected to the array's buffer whatsoever?)

The behaviour of malloc() \ realloc() is irrelevant in this case because in the code in the question the content of arr rather than ptr is modified and displayed, and arr is not dynamically allocated or reallocated. So there is no out-of-bounds access in the dynamic memory. The out-of-bounds access to arr[] has undefined behaviour. You will be stomping on memory not allocated to arr. In some cases that will modify adjacent variables, but in this case you have none, so since stacks most often grow downward, you may be modifying the local variables of the calling function or corrupting the return address of the current function - this being main() even that might not cause any noticeable error. In other cases it will lead to a crash.
However, had you modified ptr[15] and reallocated, then displayed the content at ptr it is most likely that you see a similar result because avoid an unnecessary data move, realloc() reuses the same memory block when the allocation is reduced, and simply reduces its size, returning the remainder to the heap.
Returning memory to the heap, does not change its content or make it inaccessible, and C does not perform any bounds checking, so if you code to access memory that is not part of the allocation it will let you. It simply makes the returned block available for allocation.
Strictly it is undefined behaviour, so other behaviour is possible, but generally C does not generate code to do anything other than the bare minimum required - except possibly in some cases to support debugging.

Your description of what the program is doing is all wrong.
In the example below I have allocated 20 bytes of memory to extend an array by 5 integers
No, you don't. You can't extend arr. It's just impossible.
After that I have set the last element to 15
No - because you didn't extend the array so index 9 does not represent the last element. You simply write outside the array.
Look at these lines:
int *ptr = &arr[0];
ptr = malloc(5 * sizeof(int));
First you make ptr point to the first element in arr but rigth after you you make ptr point to some dynamic allocated memory which have absolutely no relation to arr. In other words - the first line can simply be deleted (and probably the compiler will).
In the rest of your program you never use ptr for anything. In other words - you can simply remove all code using ptr. It has no effect.
So the program could simply be:
int main()
{
int arr[5] = {0};
arr[9] = 15;
for (int i = 0; i < 10; ++i)
{
printf("%d\n", arr[i]);
}
return 0;
}
And it has undefined behavior because you access arr out of bounds.

Why is the 9th element of the array still 15?
The "most likely reality" is that the OS provides a way to allocate area/s of virtual pages (which aren't necessarily real memory and should be considered "pretend/fake memory"), and malloc() carves up the allocated "pretend/fake memory" (and allocates more area/s of virtual pages if/when necessary, and deallocates areas of virtual pages if/when convenient).
Freeing "pretend/fake memory that was carved up by malloc()" probably does no more than alter some meta-data used to manage the heap; and is unlikely to cause "pretend/fake memory" to be deallocated (and is even less likely to effect actual real physical RAM).
Of course all of this depends on the environment the software is compiled for, and it can be completely different; so as far as C is concerned (at the "C abstract machine" level) it's all undefined behavior (that might work like I've described, but may not); and even if it does work like I've described there's no guarantee that something you can't know about (e.g. a different thread buried in a shared library) won't allocate the same "pretend/fake memory that was carved up by malloc()" immediately after you free it and won't overwrite the data you left behind.
why am I able to access it?
This is partly because C isn't a managed (or "safe") language - for performance reasons; typically there are no checks for "array index out of bounds" and no checks for "used after it was freed". Instead, bugs cause undefined behavior (and may be critical security vulnerabilities).

int arr[5] = {0}; // these 5 integers are kept on the stack of the function
int *ptr = &arr[0]; // the pointer ptr is also on the stack and points to the address of arr[0]
ptr = malloc(5 * sizeof(int)); // malloc creates heap of size 5 * sizeof int and returns a ptr which points to it
// the ptr now points to the heap and not to the arr[] any more.
arr[9] = 15; //the array is of length 5 and arr[9] is out of the border of maximum arr[4] !
ptr = realloc(ptr, 1 * sizeof(int)); //does nothing here, since the allocated size is already larger than 1 - but it depends on implementation if the rest of 4 x integer will be free'd.
for (int i = 0; i < 10; ++i) // undefined behavior!
{
printf("%d\n", arr[i]);
}
free(ptr);
return 0;`

In short:
Whatever you do with/to a copy of the address of an array inside a pointer variable, it has no influence on the array.
The address copy creates no relation whatsoever between the array and memory allocated (and referenced by the pointer) by a later malloc.
The allocation will not be right after the array.
A realloc of a pointer with a copy of an array access does not work. Realloc only works with pointers which carry the result of a succesful malloc. (Which is probably why you inserted the malloc.)
Longer:
Here are some important facts on your code, see my comments:
#include <stdlib.h>
#include <stdio.h>
int main()
{
int arr[5] = {0}; /* size 5 ints, nothing will change that */
int *ptr = &arr[0]; /* this value will be ignored in the next line */
ptr = malloc(5 * sizeof(int)); /* overwrite value from previous line */
arr[9] = 15; /* arr still only has size 5 and this access beyond */
ptr = realloc(ptr, 1 * sizeof(int)); /* irrelevant, no influence on arr */
for (int i = 0; i < 10; ++i) /* 10 is larger than 5 ... */
{
printf("%d\n", arr[i]); /* starting with 5, this access beyond several times */
}
free(ptr);
return 0;
}
Now let us discuss your description:
In the example below I have allocated 20 bytes of memory ....
True, in the line ptr = malloc(5 * sizeof(int)); (assuming that an int has 4 bytes; not guaranteed, but let's assume it).
... to extend an array by 5 integers.
No. No attribute of the array is affected by this line. Especially not the size.
Note that with the malloc, the line int *ptr = &arr[0]; is almost completely ignored. Only the part int *ptr; remains relevant. The malloc determines the value in the pointer and there is no relation to the array whatsoever.
After that I have set the last element to 15 ...
No, you access memory beyond the array. The last useable array element is arr[4] noce code until now has changed that. Judgin from the output, which still contains "15", you got "lucky", the value has not killed anything and still is in memory. But it is practically unrelated to the array and is also practically guaranteed outside of the allocated memory referenced by ptr.
... and then reallocated the pointer to 4 bytes (1 integer).
True. But I do not really get the point you try to make.
Then I print the first 10 elements of the array ...
No, you print the first 5 elements of the array, i.e. all of them.
Then you print 3 values which happen to be inside memory which you should not access at all. Afterwards you print a fifth value outside of the array, which you also should not access, but which happens to be still be the 15 you wrote there earlier - and should not have in the first place either.
... (it only consists of 6 at this point) ...
You probabyl mean 5 values from the array and 1 from ptr, but they are unrelated and unlikely to be consecutive.
... and the 9th (which I've previously set to 15) is printed without warnings or errors.
There is no 9th, see above. Concerning the lack of errors, well, you are not always lucky enough to be told by the compiler or the runtime that you make a mistake. Life would be so much easier if they could notify you of reliably all mistakes.
Let us go on with your comments:
But isn't arr[9] part of the defined heap?
No. I am not sure what you mean by "the defined heap", but it is surely neither part of the array nor the allocated memory referenced by the pointer. The chance that the allocation is right after the array is as close to zero as it gets - maybe not precisely 0, but you simply are not allowed to assume that.
I have allocated 20 bytes, ...
On many current machines, but assuming that an int has four bytes is also not a afe assumption. However, yes, lets assume that 5 ints have 20 bytes.
... so arr should now consist of 10 integers, instead of 5.
Again no, whatever you do via ptr, it has no influence on the array and there is practically no chance that the ptr-referenced memory is right after the array by chance. It seems that you assume that copying the address of the array into the pointer has an influence on array. That is not the case. It had once a copy of the arrays address, but even that has been overwritten one line later. And even if it had not been overwritten, reallocing the ptr would make an error (that is why you inserted the malloc line, isn't it?) but still not have any effect on the array or its size.
... But I don't think I am passing the barrier of the defined heap.
Again, lets assume that by "the defined heap" you mean either the array or the allocated memory referenced by ptr. Neither can be assumed to contain the arr[9] you access. So yes, you ARE accessing outside of any memory you are allowed to access.
I shouldn't be able to access arr[9], right?
Yes and no. Yes, you are not allowed to do that (with or without the realloc to 1).
No, you cannot expect to get any helpful error message.
Let's look at your comment to another answer:
My teacher in school told me that using realloc() with a smaller size than the already allocated memory frees it until it becomes n bytes.
Not wrong. It is freed, which means you are not allowed to use it anymore.
It is also theoretically freed so that it could be used by the next malloc. That does however not mean that the next malloc will. In no case implies freeing memory any change to the content of that freed memory. It definitly could change, but you cannot expect it or even rely on it. Tom Kuschels answer to this comment is also right.

Related

Appending an array with zeros [duplicate]

This question already has answers here:
How dangerous is it to access an array out of bounds?
(12 answers)
Closed 6 months ago.
I was reading this post and the OP says that he was worried that "that appending A's allocated memory will corrupt the heap ", so he instead allocated new memory, memcopy A, and memset.
i was wondering if that is the case. is it not possible to append without allocating new memory.
I made a lame attempt. and i got an error
int a[3] = {14, 2, 7}; // initialize array
int M = 2;
int size = sizeof a / sizeof a[0]; // size of array
// print contents of array
for (int i=0;i<size;i++) {
printf("%d\n", a[i]);
}
// add (post pad) 2 zeros to existing array , a
memset(a+M, 0, M * sizeof(int));
int size2 = sizeof a / sizeof a[0];
printf("%d\n", size2);
// print updated array
for (int i=0;i<size2;i++) {
printf("%d\n", a[i]);
}
"*** stack smashing detected ***: terminated"
In C there is no such thing as dynamic arrays. The size of the array must also be constant throughout the program and cannot change.
There is also no boundary checking so that is up to you to do as the programmer.
The array you created is a static array with space for 3 integers.
Your call to memset() is erroneous.
memset requires the following:
void *memset(void *s, int c, size_t n);
s = starting address of memory to be filled
c = value to be filled into memory
n = Number of bytes to be filled starting from s to be filled
Your code defines s as a[2], this will cause problems because memset() will try to access memory beyond what the allocated size of a is which can cause a buffer overflow bug.
Secondly it can cause a problem on the stack. Since the array a has a set size, when you try to write beyond this set size, you are accessing memory which does not belong to you and potentially over-writing important data in a stack frame that is not yours.
To put it simply the stack contains a unique value known as a "stack canary" which are placed between each stack frame. If this value is overwritten then the operating system knows the next stack frame was overwritten and a stack smashing event occured and an error is shown.
C does not allow you to write to an array out-of-bounds, no matter where it is allocated. Doing so is undefined behavior and anything can happen.
// add (post pad) 2 zeros to existing array , a
memset(a+M, 0, M * sizeof(int));
This code doesn't exactly do what the comment claims, because it starts by overwriting item a[2] with zero them continues to write out of bounds 1xsizeof(int) bytes from there. What happened in your specific case ("stack smashing") is likely that the code killed a so-called "stack canary" and so the stack corruption was detected. Which is a nice service by the compiler, but by no means guaranteed to happen. You might as well corrupt other variables or cause the program to crash.

Find out if the allocated memory was freed by the realloc() function in C

A part of my main contains
int* p = NULL;
p = malloc(sizof(int)*10);
p = realloc(p, sizeof(int) * 5);
free(p);
Is there any way to find out if, after reallocation, the pointer p points to a memory block of size 20 bytes and not 40 bytes anymore?
The ideal would be to have a function that takes an address of memory as argument and tells if it's allocated or free. Is there such a function?
Another idea would be to check the size before and after the realloc() of the allocated memory. But I don't see how sizeof() could help, because how would I identify the block of memory, sizeof() sends the size of variables and not the size of a block of memory. sizeof(p) will give me 8 bytes, since p is a pointer and sizeof(*p) is 4, since p points to an integer.
Maybe there is a special use of sizeof() or some another function?
Read more if you want to know why I ask...
If I initialize my p to hold/point to an array
for (int i = 0; i < 3 ; i++){
p[i] = i;
}
I want now p to hold only {0,1,2} so I want to reallocate p from sizeof(int)* 5 to sizeof(int)*3.
But let's say I don't really know if p should be reallocated, the memory block is 20 bytes, but maybe it's already 12 bytes, and realloc() is not needed. I know I can run realloc() either way and it won't hurt, so maybe it's not really a good reason for this question. But in a longer code it's easy to lose track of the amount of allocated memory.
Any help will be much appreciated.
PS: if no one answers I will have to get satisfaction from valgrind sending 0 errors.
After all, if something is wrong, for example writing in 21st, 22nd, 23rd and 24th bytes of memory (ex: p[4] = 7) of a block of 20 bytes (because p = realloc(p, sizeof(int) * 5)) valgrind sends errors of type "invalid write of size 4", but to get that I need to write in this memory. This method of verification makes me want to get errors, because if I can accurately predict an error then I know the actual size of the allocated memory.
There is no portable way of checking it.
You need to keep the size information yourself.
typedef struct String
{
size_t size;
int data[];
}data_type;
data_type *allocate(data_type *dt, size_t size)
{
if(!dt || dt -> size != size)
{
dt = realloc(dt, sizeof(*dt) + size * sizeof(dt -> data[0]));
if(dt)
{
dt -> size = size;
}
}
return dt;
}
Usage is the same as realloc. If you pass NULL pointer it will allocate the the stricture and space for data. If you change the size, save the result in the temporary pointer and check if allocation did not fail. If the requested size is the same as the actual allocated space it will do nothing (as reallocation is not needed)
Is there any way to find out if, after reallocation, the pointer p points to a memory block of size 20 bytes and not 40 bytes anymore?
No
This method of verification makes me want to get errors, because if I can accurately predict an error then I know the actual size of the allocated memory.
Use -fsanitize=address for both compiler and linker. Read more here: https://stackoverflow.com/a/40215639/6699433

What happen the data is bigger than malloc declares?

I am new to C and I have a question about malloc. Here is the code:
int *array = malloc(3 * sizeof(int));
if (array != NULL) {
printf("success \n");
}
array[0] = 1;
array[1] = 1;
array[2] = 1;
array[3] = 2; // I assume this should fail ?
array[4] = 1; // I assume this should fail ?
printf(" %d \n", array[3]);
Does it mean the malloc is only a memory allocation hint but not upper limit ? If yes, how do I enforce the upper limit in C ?
C doesn't mandate any bounds checking - the behavior on writing past the end of the array is undefined. Depending on what you overwrite, your code may crash immediately, or it may corrupt other data, or it may work as expected.
Neither the compiler nor the runtime environment are required to issue any warning or throw any exception on writing past the end of the array. You are expected to simply Not Do That.
In this context of memory manipulation, it is important for you to understand what it is you're actually doing when you are declaring this dynamic memory allocation.
Your "array" pointer is located on the stack and points to the first item (the first 4 bytes) of a continuous block of memory (12 bytes (3xsizeof(int)) which is located ON THE HEAP.
When you try to access this heap space memory, through the [N] operator, you're asking the compiler to perform some pointer arithmetic to allow you to access the data contained in memory at the location (array + (N)*sizeof(int)) ON THE HEAP.
So when you try to access array[3], you are attempting to access a 4 byte memory location that is located directly after the last memory space you allocated for your array, this could be many things, including your own data from a previous allocation!
As the other responses to this post have stated, this is undefined behaviour, and should be avoided, as you cannot control what data you manipulate when you access "out of bounds" data.

How should I free all memory created using multiple realloc?

I created array with 10 integer size using malloc. I added values to the elements. Then, I reallocated it to 200 bytes into newArr. And then I reallocated newArr into newArr2 with size of 10 integers again. Code:
void main(){
int i, *arr = (int *)malloc(10* sizeof(int));
for(i=0; i<10; i++){
arr[i] = i;
}
int *newArr = (int *)realloc(arr, 200);
int *newArr2 = (int *)realloc(newArr, 10* sizeof(int));
}
How should I use free to remove all the allocated memory here? I'm getting error while clearing all of them.
Edit: As per the accepted answer the old memory should've been cleared but it didn't. I was able to access memory and was able to change value on old address.
From my point of view, when you use malloc or realloc you're changing the memory reference, so, if you call realloc on a variable you are freeing the old space used and allocate new space, copying the old data to the new memory position, so, in your example, arr doesn't hold a valid memory address after first realloc. The same thing happen on newArr
realloc is basically malloc with the new size, memmove the data to the new block and free the old one. (But implementation can optimize this process because they've got more information they can use, like just extending the current allocated block, producing the same pointer)
So the pointers arr and newArr are invalid and shouldn't be accessed anymore because they might have been freed, so the pointer in newArr2 is the current one and valid, if the previous allocations didn't fail. So free(newArr2) is the correct answer.
Sure, you might access the memory from the old pointers, but it isn't guaranteed because it might've been allocated and overwritten for a different purpose or you might just be lucky to get the same pointer back from realloc (because from eg. the optimization above). It's just undefined behavior when accessing freed memory.
Source on reddit

Realloc simply not diong anything, not erroring

I've got the following code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char ** argv)
{
//just checking to see where the stack
printf("The stack is around %p\n", &argc); is, making sure arr isn't in it
char ** arr = malloc(8*sizeof(char*));
printf("arr is size %li, at %p\n", sizeof(arr), arr);
arr = realloc(arr, 100); //I picked a weird number to show it isn't doing anything. I've picked different numbers (like 200, 2*sizeof(char*)*sizeof(arr), and 16)
printf("arr is size %li, at %p\n", sizeof(arr), arr);
}
That's the entirety of the file (it's a unit test; I was noticing it elsewhere)
The output of the above is as follows:
The stack is around 0x7fff5b94d12c
arr is size 8, at 0x120f010
arr is size 8, at 0x120f010
Perhaps I'm misunderstanding what realloc should do. I'm expecting the following output.
The stack is around 0x7fff5b94d12c
arr is size 8, at 0x120f010
arr is size <size>, at <somewhere>
where <size> is... something odd like 12... at least not 8 and <somewhere> is most likely 0x120f010 but possibly anywhere reasonable.
Are my expectations wrong or am I using realloc incorrectly?
The output of your program is correct, because
Neither malloc nor realloc have anything to do with the automatic storage (i.e. "the stack"). They allocate memory from the dynamic storage area (i.e. "the heap"). One should not expect the position of the top of the stack to change in response to calls to malloc, realloc, or for that matter, any other function.
The value of sizeof(arr) does not depend on what you have allocated to it. It is computed at compile time, and it is always equal to the size of a pointer. On your system, pointers use 8 bytes.
malloc often gives you more memory that you ask, and stores the actual value in a special location that realloc can access at a later time. If you realloc down, or realloc within the bounds, the value returned by realloc does not change. That's the reason why it may perform better than simply calling malloc and memcpy.
sizeof arr
That's the same as
sizeof char**
The size of a pointer isn't going to change, and taking the size of a pointer is not going to tell you how much memory it refers to. Pointers are not arrays, and sizeof is evaluated at compile time.
As for the address bit, realloc doesn't guarantee that the memory block was moved. It could simply expand it successfully and return the same address.
Also, I realize this is just example code, but be aware that, if realloc failed, you leaked what arr originally pointed to.
It's not uncommon, and in fact a bit expected, that a malloc call followed directly by realloc would not change the address of the pointer. In many cases the allocator can just extend the amount of memory reserved at the address and not have to move the pointer. This is what is happening here.
This isn't something you should ever depend on though. It's just a quirk of the implementation
If your assumption is that realloc() has to return a different pointer, then your assumption is wrong.
Typically if you're reducing the size of the allocated memory or leaving it "as is", then realloc() can return the same pointer and avoid copying data, etc.
Sometimes if you're increasing the size of the allocated memory realloc() can check if there's free space above the existing space and still return the same pointer (and avoid copying data).
Mostly, it's only when there is no free space above the allocated memory that realloc() must copy the data somewhere else and return a different pointer.

Resources