1

I'm in the process of learning how to use pointers and structs in C. Naturally, I'm trying to deliberately break my code to further understand how the language works. Here is some test code that works as I expected it to work:

#include <stdio.h>
#include <stdlib.h>

struct pair {
    int x;
    int y;
};

typedef struct pair pair;

void p_struct( pair ); //prototype

int main( int argc, char** argv ) {
    pair *s_pair;
    int size, i;

    printf( "Enter the number of pair to make: " );
    scanf( "%d", &size );
    getchar();
    printf( "\n" );

    s_pair = (pair*)malloc( size * sizeof(pair) );

    for( i = 0; i < size; i++ ) {
        s_pair[i].x = i;
        s_pair[i].y = i;
        p_struct( s_pair[i] );
    }

    getchar();

    return (EXIT_SUCCESS);
}

void p_struct( pair s_pair ) {
    printf( "\n%d %d\n", s_pair.x, s_pair.y );
}

As previously stated, this code is functional as far as I can tell.

I then decided to modify a part of the code like so:

for( i = 0; i < size + 3; i++ ) {
    s_pair[i].x = i;
    s_pair[i].y = i;
    p_struct( s_pair[i] );
}

This modification did not produce the seg fault error that I expected it would. All of the "pairs" were printed despite me exceeding the buffer I explicitly set when assigning a value to my variable size using the scanf function.

As I understand pointers (correct me if I'm wrong), a contiguous block of memory of size size*sizeof(pair) is reserved by the memory manager in the heap when I called the malloc function for my pointer of type pair s_pair. What I did was I exceeded the last assigned address of memory when I modified my for loop to the condition i < size + 3.

If I'm understanding this correctly, did my pointer exceed its reserved memory limit and just so happen to be in the clear because nothing adjacent and to the right of it was occupied by other data? Is this normal behaviour when overflowing a buffer?

To add, I did receive a seg fault when I tested with a for loop condition of i < size + 15. The thing is, it still prints the output. As in, it prints the pair "0 0" to pair "24 24" when size = 10 on the screen as per the p_struct function I made. The program crashes by seg fault only after it gets to one of those getchar()s at the bottom. How on earth could my program assign values to pairs that exceed the buffer, print them on the screen, and then all of a sudden decide to crash on seg fault when it gets to getchar()? It seemed to have no issue with i < size + 3 (despite it still being wrong).

For the record, I also tested this behaviour with a regular pointer array:

int size, i, *ptr;

scanf( "%d", &size );

ptr = (int*)malloc( size * sizeof(int) );

for( i = 0; i < size + 15; i++ )
    ptr[i] = i;

This produces the exact same result as above. At i < size + 3 there doesn't seem to be any issue with seg faults.

Finally, I tested with an array, too:

int i, array[10];

for( i = 0; i < 25; i++ )
    array[i] = i;

For the condition i < 25, I get a seg fault without fail. When I change it to i < 15, I receive no seg fault.

If I remember correctly, the only difference between an array of pointers and an array is that the memory allocated to an array is located on the stack as opposed to the heap (not sure about this). With that in mind, and considering the fact that i < 15 when array[10] doesn't produce any seg faults, why would i < 25 be an issue? Isn't the array at the top of the stack during that for loop? Why would it care about 100 extra bytes when it didn't care about 60 extra bytes? Why isn't the ceiling for that array buffer all the way to the end of whatever arbitrary chunk of memory is reserved for the whole stack?

Hopefully all of this made sense to whoever decides to read a slightly inebriated man's ramblings.

jgor
  • 11
  • 1
  • 2
    Undefined behaviour: your code is not required to crash or segfault when you do undefined things. It just so happens that apps crash on some platforms when you do things that are egregiously wrong. The system is not required to catch you doing undefined things, though; this is *the* most common source of hidden bugs in C. – nneonneo Sep 02 '13 at 04:51
  • 2
    [Please don't cast the return value of `malloc()` in C](http://stackoverflow.com/a/605858/28169). – unwind Sep 02 '13 at 11:04

4 Answers4

2

If I'm understanding this correctly, did my pointer exceed its reserved memory limit and just so happen to be in the clear because nothing adjacent and to the right of it was occupied by other data?

Pretty much. Except that you're not "in the clear" because adjacent things probably were occupied by other data and your code simply stomped on that memory and changed the values. You might never notice a problem, or you might notice a problem much later. Either way, it's undefined behaviour.

Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
2

Welcome to the glorious world of C!

The memory allocation functions (malloc, calloc, realloc, etc) give you memory that's on the heap. When you call one of them and your program doesn't have enough space, it makes a system call to get more. It doesn't do this in precise increments though (it often will do so in some number of whole page increments). When you're indexing past the end of your array (or even before the beginning of it) you are still within the bounds of your program's legal address space. Only when you leave the segment your program owns will you get a Segmentation Violation.

I highly recommend using Valgrind to inspect your program, especially if you are deliberately trying to learn about memory by breaking things. Among other things, it will store canary values on either side of allocations to help you figure out when you're accessing out of bounds and warn you about double frees and memory leaks.

Kyle Lemons
  • 4,716
  • 1
  • 19
  • 23
1

When you call malloc, you might be given more memory than you need it because memory is allocated in multiples of a common block size. If block size is 64bytes and you ask for only 10 bytes, then the OS will give you 64 bytes, hence you can still access memory beyond your requested range which is the behavior your program is observing.

Saradhi
  • 477
  • 6
  • 13
  • While that could be a reason, in this case it probably isn't: `malloc()` likely allocs in multiples of 8/16 bytes (`double`/SSE-aligned). Hint: `free(s_pair)` at the end of the program. – ninjalj Sep 02 '13 at 11:37
1

As others said, undefined behaviour doesn't mean your program will crash under all circumstances.

It completely depends on what is supposed to be there where you overwrite the data.

  • There may be nothing, as the C lib hasn't allocated the program there,
  • You may have overwritten important administration information which is used later and only then leads to a crash,
  • or whatever else.

For helping you to understand what really happens under the hood, printing addresses (such as printf("%p\n", s_pair); or anything like that) may be helpful, as well as compiling the program to readable assembler mnemonics (such as gcc -S filename.c -o-)

glglgl
  • 89,107
  • 13
  • 149
  • 217