0

While trying to implement my own version of the C function memccpy(), I also happened upon this other person's implementation of memccpy() on stack overflow and tested their variant against the original. It reproduced the same results as the C standard function with different string and integer array inputs I gave it. The problem is I don't understand why their version doesn't segfault on certain inputs such as this integer array.

I tried to see what would happen if the length taken exceeded that of the input array. Of course I expected a segfault, but to my surprise it did not. Here is the function implementation:

void    *ft_memccpy(void *str_dest, const void *str_src, int c, 
size_t n)
{
    unsigned int    i;
    char            *dest;
    char            *src;
    char            *ptr;

    dest = (char *)str_dest;
    src = (char *)str_src;
    i = 0;
    ptr = 0;
    while (i < n && ptr == 0)
    {
        dest[i] = src[i];
        if (src[i] == ((char)c))
            ptr = dest + i + 1;
        i++;
    }
    return (ptr);
}

And the code used to test it:

int main()
{
    int num1[5] = {1, 2, 3, 4, 5};
    int num2[5] = {0, 0, 0, 0, 0};

    int (*num1p)[5] = &num1;
    int (*num2p)[5] = &num2;

    for (int i = 0; i < 5; i++)
    {
        printf("value before copy = %d\n", num2[i]);
    }
    //THE INPUT
    ft_memccpy(num2p, num1p, 9, (sizeof(int)*8));
    for (int i = 0; i < 5; i++)
    {
        printf("value after copy = %d\n", num2[i]);
    }

    return 0;
}

What I expected was for there to be a segfault since the parameters passed were 9 and 32 bytes for size (8 * sizeof(int)). I thought that since the array size itself was only 20 bytes, it would segfault once it passed 20 bytes on the line dst[i] = src[i], but it doesn't. Indeed when I pass these same parameters into the standard C version, it also does not segfault. What could be the reason for this?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Thunderpurtz
  • 189
  • 2
  • 12
  • Note that `memccpy()` is not a part of the C standard (see [C11](http://port70.net/~nsz/c/c11/n1570.html) for example). It is a part of POSIX, though ([`memccpy()`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/memccpy.html)) – Jonathan Leffler Feb 13 '19 at 20:45
  • Stack bashing doesn't always go outside of your memory segment. There's definitely undefined behavior but that's undefined, not necessarily a segmentation violation... – cleblanc Feb 13 '19 at 20:45
  • Undefined behavior doesn’t guarantee crashing. It’s just undefined per the language standard. Your actual implementation may do whatever. You can’t even expect anything. Even null pointer dereferences don’t need to crash: sometimes the compiler optimizes such code out. – Kuba hasn't forgotten Monica Feb 13 '19 at 20:59
  • Hope this live test (https://segfault.stensal.com/a/rxF9Nt6q9AWVS3Vf) will give you a more intuitive illustration of the out-of-bounds illegal memory access. – stensal Feb 14 '19 at 00:22

2 Answers2

5

Your arrays are function scoped, on the stack. Writing outside the bounds of any array is undefined behavior, and will do all sorts of terrible things in many cases, but a segfault is rarely the direct result of writing only a little outside the bounds of an array on the stack. After all, the stack is all writable memory, it's just not logically part of the array (so you might be overwriting other local variables, function return addresses, registers spilled to the stack, etc.).

Point is, the code is wrong, but segfaults usually don't happen unless you're trying to write to the NULL pointer, significantly outside allocated memory, or you just get "lucky" and happen to overwrite a buffer near the end of an allocated page (and therefore write into an unallocated page).

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
5

When you write past the bounds of an object, you invoke undefined behavior. This means you can't predict the behavior of the program. It may crash, it may output strange results, or it may appear to work properly.

Also, how undefined behavior manifests can change by making seemingly unrelated changes, such as an extra printf for debugging or adding an unused local variable. It can also change by using different optimization settings or a different compiler. For example, when I ran your code it detected stack smashing and crashed with SIGABORT.

Just because a crash could happen doesn't mean it will.

dbush
  • 205,898
  • 23
  • 218
  • 273
  • ...and possibly also depends on whether built as "debug" or "release" (e.g. if bounds checking is built into debug but not release builds). – phonetagger Feb 13 '19 at 21:18