1

I was just playing around with the pointers and malloc function,

I tried to create an array using malloc and then assign It's values using a for loop

Here is my code:

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


int main () {
  // Allocate enough memory for 3 integer elements
  int *array = (int *) malloc(sizeof(int) * 3);

  // Write data, even tho I have allocated enough memory
  // for only 3 integer elements, I am able to write more!
  // Somehow if I write 7 elements instead of 6, I'll get an error!
  for(int i = 0; i<6; i++) {
    array[i] = i*5;
  }

  // Read the data, everything is fine
  for(int i = 0; i<6; i++) {
    printf("%d. %p (%d)\n", i, &array[i], array[i]);
  }

  // Let the memory go...
  free(array);
  return 0;
}

At first, I was trying to write on memory for only 3 elements, but I "accidentally" did write 4 elements, and shockingly (for me) It worked without any error!

So I tried with more elements, I was able to write 6 elements using the pointer and the memory I was allocated for only 3 elements,

Till 6 elements, it worked, But if I write 7 elements, I get an error and the core will get dumped.

malloc.c:2379: sysmalloc: Assertion `(old_top == initial_top (av) && old_size == 0) || ((unsigned long) (old_size) >= MINSIZE && prev_inuse (old_top) && ((unsigned long) old_end & (pagesize - 1)) == 0)' failed.

I know It's wrong to write on a spot that I haven't allocated, but I just can't understand how it is working properly with 6 elements, and not with 7 elements...

I ran the code a bunch of time Here is an example of the results with 6 elements:

0. 0x55bc431fe2a0 (0)
1. 0x55bc431fe2a4 (5)
2. 0x55bc431fe2a8 (10)
3. 0x55bc431fe2ac (15)
4. 0x55bc431fe2b0 (20)
5. 0x55bc431fe2b4 (25)

The pointers are right, The values are right, If it would return me some junk value then it wasn't weird (I guess!?)

So, How am I writing 6 integers (6 * sizeof(int)) on memory I allocated for 3 (3 * sizeof(int))? And why it doesn't do the job the same way when I try to write 7 integers?

I even tried to running code using valgrind and see if there is any useful information (I'm a newbie)

==311441== Memcheck, a memory error detector
==311441== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==311441== Using Valgrind-3.16.0.GIT and LibVEX; rerun with -h for copyright info
==311441== Command: ./6els
==311441== 
==311441== Invalid write of size 4
==311441==    at 0x109197: main (in /home/alireza/Projects/CPointerz/6els)
==311441==  Address 0x4a3404c is 0 bytes after a block of size 12 alloc'd
==311441==    at 0x483977F: malloc (vg_replace_malloc.c:307)
==311441==    by 0x10916A: main (in /home/alireza/Projects/CPointerz/6els)
==311441== 
0. 0x4a34040 (0)
1. 0x4a34044 (5)
2. 0x4a34048 (10)
==311441== Invalid read of size 4
==311441==    at 0x1091C0: main (in /home/alireza/Projects/CPointerz/6els)
==311441==  Address 0x4a3404c is 0 bytes after a block of size 12 alloc'd
==311441==    at 0x483977F: malloc (vg_replace_malloc.c:307)
==311441==    by 0x10916A: main (in /home/alireza/Projects/CPointerz/6els)
==311441== 
3. 0x4a3404c (15)
4. 0x4a34050 (20)
5. 0x4a34054 (25)
==311441== 
==311441== HEAP SUMMARY:
==311441==     in use at exit: 0 bytes in 0 blocks
==311441==   total heap usage: 2 allocs, 2 frees, 1,036 bytes allocated
==311441== 
==311441== All heap blocks were freed -- no leaks are possible
==311441== 
==311441== For lists of detected and suppressed errors, rerun with: -s
==311441== ERROR SUMMARY: 6 errors from 2 contexts (suppressed: 0 from 0)

Also, the code is compiled using GCC with no additional compile flags.

Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-pc-linux-gnu/10.1.0/lto-wrapper
Target: x86_64-pc-linux-gnu
Configured with: /build/gcc/src/gcc/configure --prefix=/usr --libdir=/usr/lib --libexecdir=/usr/lib --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=https://bugs.archlinux.org/ --enable-languages=c,c++,ada,fortran,go,lto,objc,obj-c++,d --with-isl --with-linker-hash-style=gnu --with-system-zlib --enable-__cxa_atexit --enable-cet=auto --enable-checking=release --enable-clocale=gnu --enable-default-pie --enable-default-ssp --enable-gnu-indirect-function --enable-gnu-unique-object --enable-install-libiberty --enable-linker-build-id --enable-lto --enable-multilib --enable-plugin --enable-shared --enable-threads=posix --disable-libssp --disable-libstdcxx-pch --disable-libunwind-exceptions --disable-werror gdc_include_dir=/usr/include/dlang/gdc
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 10.1.0 (GCC) 
DarkSuniuM
  • 2,523
  • 2
  • 26
  • 43
  • 6
    Undefined behavior is undefined. – Retired Ninja Jun 10 '20 at 22:48
  • 2
    Related: https://stackoverflow.com/questions/2397984/undefined-unspecified-and-implementation-defined-behavior – user3386109 Jun 10 '20 at 22:51
  • 2
    The `malloc()` function allocates at least the requested space; it may allocate what you request rounded up to some size that is convenient for it. You can't reliably use that extra space (it may not exist, even). Things may go wrong if you abuse memory that was not formally allocated to you. Or they may appear to go right until some indeterminate time in the future, when your job depends on it working correctly and it can see an opportunity for it to work incorrectly — when your boss, boss's boss, and boss's boss's boss are all showing your work to the most important customer. – Jonathan Leffler Jun 10 '20 at 22:52
  • 1
    Valgrind works on a best-effort basis. While it strives to detect a variety of addressing errors, it will not necessarily detect every possible error out there. Writing four or five elements happens to work not because it's legal or OK, but because for *your specific environment*, it happened to not result in a crash, and valgrind didn't have enough granularity to detect the error. On another system (or with e.g. ASAN instead of valgrind) it could very well crash or trigger the sanitizer. – nanofarad Jun 10 '20 at 22:56
  • 1
    most likely your error is coming from `free`. Writing beyond space allocated for you by malloc might corrupt malloc structures (probably the seventh element does). The libc message is telling you about it. `invalid read` and `write` in valgrind are telling that you write to or read from unallocated memory. – Serge Jun 10 '20 at 23:11
  • All the comments were helpful, I now get what "Undefined behavior" is, but It was weird that on every single time I ran the binary, It had the exact same behavior and I didn't know this could be known as "Undefined". – DarkSuniuM Jun 10 '20 at 23:18
  • @Serge about the `invalid read` and `write`, Yeah, now that I see, it does make sense now, but still can't get the `free` part! – DarkSuniuM Jun 10 '20 at 23:19
  • @JonathanLeffler is there anyway to do some tests for this kinda behavior? like making sure about stuff like this? – DarkSuniuM Jun 10 '20 at 23:19
  • 1
    Using Valgrind and thinking carefully about what your code does, and knowing how much space was allocated whenever you allocate space, are the main ways. Valgrind will spot problems if your code executes the problematic code with bad values. It won't necessarily spot the problem if you don't test the extremes (zero, one, and many). – Jonathan Leffler Jun 10 '20 at 23:24
  • 1
    Undefined doesn't mean inconsistent, it just means what happens happens. It might always work the same way, it might not, both are valid because there's no defined right thing to do in that case. – Retired Ninja Jun 10 '20 at 23:25

0 Answers0