malloc'd memory and sigsegv - c

help me in understanding the malloc behaviour.. my code is as follows::
int main()
{
int *ptr=NULL;
ptr=(int *)malloc(1);
//check for malloc
*ptr=1000;
printf("address of ptr is %p and value of ptr is %d\n",ptr,*ptr);
return 0;
}
the above program works fine(runs without error)...how?? as I have supplied a value of 1000 in 1 byte only!!
Am I overwriting the next memory addresss in heap?
if yes, then why not sigsgev is there?

Many implementations of malloc will allocate at a certain "resolution" for efficiency.
That means that, even though you asked for one byte, you may well have gotten 16 or 32.
However, it's not something you can rely on since it's undefined behaviour.
Undefined behaviour means that anything can happen, including the whole thing working despite the problematic code :-)

Using a debug heap you will definitely get a crash or some other notification when you freed the memory (but you didn't call free).
Segmentation faults are for page-level access violations, and a memory page is usually on the order of 4k, so an overrun by 3 bytes isn't likely to be detected until some finer grained check detects it or some other part of your code crashes because you overwrote some memory with 'garbage'

Related

Acessing beyond the allocated space and not getting segfault

I allocated some space for a char pointer and tried to access beyond the allocated space but still getting no segmentation fault. my code is like below:
char *src = malloc(4);
strcpy(src, "1234");
char *temp;
for(int i = 0 ; i<5 ; i++) {
temp = src;
src ++;
printf("ite ch %c\n",src[0]);
}
printf("Still no segfault %s\n",temp);
Now my question is: how can I go beyond the allocated space? Shouldn't I get segmentation fault?
When you write past the end of a memory block allocated by malloc as you've done here, you invoke undefined behavior.
Undefined behavior means the behavior of the program can't be predicted. It could crash, it could output strange results, or it could appear to work properly. Also, a seemingly unrelated change such as adding an unused local variable or a call to printf for debugging can change the way undefined behavior manifests itself.
To summarize, with undefined behavior, just because the program could crash doesn't mean it will.
The malloc() function implementation is system and library specific. One of the things that many memory allocation implementations have to deal with is memory fragmentation.
The question code allocates 4 bytes. In order to minimize memory fragmentation, many systems actually allocate more than 4; perhaps a minimum of 16 bytes. Doing so both satisfies the malloc(4) request, as well as keeps memory fragments (once the memory has been freed) to a minimum size of 16 bytes. Hence a "memory fragment pool" of 16 byte fragments can be used to satisfy malloc() request from 1 to 16 bytes.
Many memory management systems maintain "memory fragment pools" of 16,32,64,128, (etc) bytes each. For example, if a call of malloc(44) is made, a memory fragment from the 64 byte pool can satisfy the request.
On some systems, there is a provision to determine the actual size of the memory fragment returned by malloc(). On a Linux system, the function malloc_usable_size() performs this function. OS X systems can use malloc_size().

what happens to array elements after the original array is reallocated?

#include <stdio.h>
#include <stdlib.h>
int main()
{
int *a;
a = (int *)malloc(100*sizeof(int));
int i=0;
for (i=0;i<100;i++)
{
a[i] = i+1;
printf("a[%d] = %d \n " , i,a[i]);
}
a = (int*)realloc(a,75*sizeof(int));
for (i=0;i<100;i++)
{
printf("a[%d] = %d \n " , i,a[i]);
}
free(a);
return 0;
}
In this program I expected the program to give me a segmentation fault because im trying to access an element of an array which is freed using realloc() . But then the output is pretty much the same except for a few final elements !
So my doubt is whether the memory is actually getting freed ? What exactly is happening ?
The way realloc works is that it guarantees that a[0]..a[74] will have the same values after the realloc as they did before it.
However, the moment you try to access a[75] after the realloc, you have undefined behaviour. This means that the program is free to behave in any way it pleases, including segfaulting, printing out the original values, printing out some random values, not printing anything at all, launching a nuclear strike, etc. There is no requirement for it to segfault.
So my doubt is whether the memory is actually getting freed?
There is absolutely no reason to think that realloc is not doing its job here.
What exactly is happening?
Most likely, the memory is getting freed by shrinking the original memory block and not wiping out the now unused final 25 array elements. As a result, the undefined behaviour manifests itself my printing out the original values. It is worth noting that even the slightest changes to the code, the compiler, the runtime library, the OS etc could make the undefined behaviour manifest itself differently.
You may get a segmentation fault, but you may not. The behaviour is undefined, which means anything can happen, but I'll attempt to explain what you might be experiencing.
There's a mapping between your virtual address space and physical pages, and that mapping is usually in pages of 4096 bytes at least (well, there's virtual memory also, but lets ignore that for the moment).
You get a segmentation fault if you attempt to address virtual address space that doesn't map to a physical page. So your call to realloc may not have resulted in a physical page being returned to the system, so it's still mapped to you program and can be used. However a following call to malloc could use that space, or it could be reclaimed by the system at any time. In the former case you'd possibly overwrite another variable, in the latter case you'll segfault.
Accessing an array beyond its bounds is undefined behaviour. You might encounter a runtime error. Or you might not. The memory manager may well have decided to re-use the original block of memory when you re-sized. But there's no guarantee of that. Undefined behaviour means that you cannot reason about or predict what will happen. There's no grounds for you to expect anything to happen.
Simply put, don't access beyond the end of the array.
Some other points:
The correct main declaration here is int main(void).
Casting the value returned by malloc is not needed and can mask errors. Don't do it.
Always store the return value of realloc into a separate variable so that you can detect NULL being returned and so avoid losing and leaking the original block.

Dynamic Arrays of Struct

#include <stdio.h>
#include <stdlib.h>
struct ver{
double x;
double y;
};
struct ver *v=NULL;
int main(){
v=(struct ver*)realloc(v,1*sizeof(struct ver));
v[0].x=1.444;
v[0].y=1.555;
v[1].x=1.333;
v[1].y=1.222;
v[3].x=1.111;
v[3].y=1.777;
v[8].x=1.999;
v[8].y=1.888;
printf("x:%f y:%f \n", v[0].x, v[0].y);
printf("x:%f y:%f \n", v[1].x, v[1].y);
printf("x:%f y:%f \n", v[3].x, v[3].y);
printf("x:%f y:%f \n", v[8].x, v[8].y);
}
The result is:
x:1.444000 y:1.555000
x:1.333000 y:1.222000
x:1.111000 y:1.777000
x:1.999000 y:1.888000
Shouldn't i get segmentation fault? It ignores the realloc.
I want to make an array of structs which i want to expand 1 array-cell each time.
You only get a segmentation fault if there is no memory behind the address you are accessing. This is meant in a physical sense, a segfault is first signaled by hardware, not by software. Now, physical memory mapping always happens in terms of complete pages. which are 4 kiB on most hardware. The size you requested from realloc() is just 16 bytes.
Of course, there must be memory behind the address that realloc() returns. Consequently, there must be at least 4080 addresses surrounding your struct that are just as valid in the eyes of the hardware as the 16 addresses belonging to the struct itself. That means that the hardware cannot signal your OS that something might be amiss, and your OS cannot send you a segmentation fault. You might get a segfault if you try to access v[1000], but even that is not certain.
That does not mean that you are allowed to access these addresses, it just means that you cannot rely on a segfault when you access them. There might be other allocations that you can clobber, or worse, there might be information standing there which malloc uses to determine which memory regions are used and which not. If you access any address outside of the region requested from realloc(), your program has undefined behaviour and anything can happen.
You're calling realloc to allocate a single entry. Accessing anything but v[0] is undefined behavior which may cause a crash, or it may not.
Technically, defining main like you do (without any arguments, not even void) is also undefined behavior as well in C. You either have to declare it with void as argument, or int and char**.
Calling realloc with a NULL pointer is well defined behaviour. So v points to enough memory for exactly one struct ver.
The reason that v[1] is not generating a segmentation fault: Such errors do not have to happen alwyas - you can get lucky... Perhaps realloc also gives you a bit more memory as you only asked for 16 bytes.
Sometimes malloc grabs a big chunk from the OS (so it can use it later) without doing a context switch.
That is expensive.
So you are just lucky

try to buffer overflow value allocated by malloc()

I'm a bit confused about malloc() function.
if sizeof(char) is 1 byte and the malloc() function accepts N bytes in argument to allocate, then if I do:
char* buffer = malloc(3);
I allocate a buffer that can to store 3 characters, right?
char* s = malloc(3);
int i = 0;
while(i < 1024) { s[i] = 'b'; i++; }
s[i++] = '$';
s[i] = '\0';
printf("%s\n",s);
it works fine. and stores 1024 b's in s.
bbbb[...]$
why doesn't the code above cause a buffer overflow? Can anyone explain?
malloc(size) returns a location in memory where at least size bytes are available for you to use. You are likely to be able to write to the bytes immediately after s[size], but:
Those bytes may belong to other bits of your program, which will cause problems later in the execution.
Or, the bytes might be fine for you to write to - they might belong to a page your program uses, but aren't used for anything.
Or, they might belong to the structures that malloc() has used to keep track of what your program has used. Corrupting this is very bad!
Or, they might NOT belong to your program, which will result in an immediate segmentation fault. This is likely if you access say s[size + large_number]
It's difficult to say which one of these will happen because accessing outside the space you asked malloc() for will result in undefined behaviour.
In your example, you are overflowing the buffer, but not in a way that causes an immediate crash. Keep in mind that C does no bounds checking on array/pointer accesses.
Also, malloc() creates memory on the heap, but buffer overflows are usually about memory on the stack. If you want to create one as an exercise, use
char s[3];
instead. This will create an array of 3 chars on the stack. On most systems, there won't be any free space after the array, and so the space after s[2] will belong to the stack. Writing to that space can overwrite other variables on the stack, and ultimately cause segmentation faults by (say) overwriting the current stack frame's return pointer.
One other thing:
if sizeof(char) is 1 byte
sizeof(char) is actually defined by the standard to always be 1 byte. However, the size of that 1 byte might not be 8 bits on exotic systems. Of course, most of the time you don't have to worry about this.
It is Undefined Behavior(UB) to write beyond the bounds of allocated memory.
Any behavior is possible, no diagnostic is needed for UB & any behavior can be encountered.
An UB does not necessarily warrant a segmentation fault.
In a way, you did overflow your 3 character buffer. However, you did not overflow your program's address space (yet). So you are well out of the bounds of s*, but you are overwriting random other data in your program. Because your program owns this data, the program doesn't crash, but still does very very wrong things, and the future behaviour is undefined.
In practice what this is doing is corrupting the heap. The effects may not appear immediately (in fact, that's part of what makes such errors a PITA to debug). However, you may trash anything else that happens to be in the heap, or in that part of your program's address space for that matter. It's likely that you have also trashed malloc() internal data structures, and so it's likely that subsequent malloc() or free() calls may crash your program, leading many programmers to (falsely) believe they've found a bug in malloc().
You're overflowing the buffer. It depends what memory you're overflowing into to get an error msg.
Did you try executing your code in release mode or did you try to free up the memory you of s? It is an undefined behavior.
It's a bit of a language hack, and a bit dubious about it's use.

Freeing pointer after pointer arithmetic

My question is very simple one. Say we have:
char* ptr = (char*) malloc(sizeof(char)*SIZE);
ptr+= SIZE/2;
free(ptr);
What happens when we free the pointer? Is it undefined operation?
Does it free all of SIZE buffer or only the remaining SIZE/2?
Thanks in advance for disambiguating this for me.
Your program will probably crash: the free() operation is actually quite simple in C, but works only on the original allocated address.
The typical memory allocator works like this pseudo code:
ask to alloc 64 bytes
allocator allocs 70 bytes (6 bytes more)
the first 2 bytes is set to a "signature", a pattern recognized by the allocator to identify the memory allocated by him
the next 4 bytes denote the allocated size
return a pointer to the start of the 7th byte
So when you call free(ptr), the allocator goes 6 bytes before your pointer to check for the signature. If it doesn't find the signature, it crashes :)
If the argument to free() does not match a pointer previously allocated by means of malloc() and friends, the behaviour is undefined. You will most likely encounter a segmentation fault or a failed assertion in your version of libc.
Offtopic: it's better you didn't cast the result of malloc() in C.
The behavior is undefined and will most likely result in a segmentation fault - and that's the good case. In the worst case, it will corrupt your program's memory and induce all kind of weird bugs and wrong outputs.
On most implementations this should result in some sort of fatal error. You can only free the begining of an allocated buffer.
They typical error you would get on windows (with the visual studio compilers) would be something like "not a valid heap pointer". On linux, as said above by phihag, it would usually result in a segmentation fault. In both cases, it is a runtime error that would usually terminate the execution of the program.
The behaviour is undefined. I guess you'd get a segfault ... that's what I got when I tried in my system.
free() requires the caller to pass an address that was returned by a memory allocator function like malloc(). Anything else results in undefined behaviour.

Resources