If you get a runtime error from malloc, realloc or free, it means you've corrupted the heap. Common reasons for heap corruption include using a memory block after it has been freed (this includes calling free twice) and buffer overflows (and underflows).
The corruption happened before the runtime error. It may have happened a long time before, so if you just start debugging the program at the point the error happens, it may be difficult to reconstruct what happened. There are other tools that can help you locate the problem more precisely. On Unix-like systems with GCC or Clang, AddressSanitizer is pretty useful.
# Settings for Ubuntu 20.04; you may need to adapt for your system
$ export ASAN_OPTIONS=symbolize=1 ASAN_SYMBOLIZER_PATH=/usr/lib/llvm-10/bin/llvm-symbolizer
$ gcc -O -Wall -Wextra a.c -fsanitize=address,undefined && python3 -c "print('.'*117+'\n'+'.'*12)" | ./a.out
=================================================================
==446177==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60c000000236 at pc 0x7f246fc0ea6d bp 0x7ffd4e309380 sp 0x7ffd4e308b28
READ of size 119 at 0x60c000000236 thread T0
#0 0x7f246fc0ea6c (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x67a6c)
#1 0x55c04dfb32e7 in get_unlimited_input (.../65891246/a.out+0x12e7)
#2 0x55c04dfb34b1 in main (.../65891246/a.out+0x14b1)
#3 0x7f246f06f0b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
#4 0x55c04dfb320d in _start (.../65891246/a.out+0x120d)
0x60c000000236 is located 0 bytes to the right of 118-byte region [0x60c0000001c0,0x60c000000236)
allocated by thread T0 here:
#0 0x7f246fcb4ffe in __interceptor_realloc (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x10dffe)
#1 0x55c04dfb345c in get_unlimited_input (.../65891246/a.out+0x145c)
SUMMARY: AddressSanitizer: heap-buffer-overflow (/usr/lib/x86_64-linux-gnu/libasan.so.5+0x67a6c)
Shadow bytes around the buggy address:
0x0c187fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c187fff8000: fa fa fa fa fa fa fa fa fd fd fd fd fd fd fd fd
0x0c187fff8010: fd fd fd fd fd fd fd fa fa fa fa fa fa fa fa fa
0x0c187fff8020: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fa
0x0c187fff8030: fa fa fa fa fa fa fa fa 00 00 00 00 00 00 00 00
=>0x0c187fff8040: 00 00 00 00 00 00[06]fa fa fa fa fa fa fa fa fa
0x0c187fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c187fff8060: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c187fff8070: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c187fff8080: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c187fff8090: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
Shadow gap: cc
==446177==ABORTING
You. The most important parts are the stack trace, and the indication that buffer overflow happened on a “118-byte region”, which suggests that it's happening at the very end of the first invocation of get_unlimited_input or at the very beginning of the second one. The stack trace gives you the exact code address at which the overflow happens, which you can use to set a breakpoint in a debugger; you'll see that it's near the end of the function. As others have already noted,
*cursor = '\0';
buffer = realloc(buffer, cursor - buffer);
is wrong: you aren't leaving room for the '\0' terminator. You need
*(cursor++) = '\0';
buffer = realloc(buffer, cursor - buffer);
or
*cursor = '\0';
buffer = realloc(buffer, cursor - buffer + 1);
I haven't reviewed for other bugs (it isn't the only one).
realloc(buffer, cursor - buffer);->realloc(buffer, cursor - buffer + 1);. Compile your program with-fsanitize=addressand check for stack overflows.realloc()can fail. Therefore, always assigned the returned pointer to atempvariable, check thattempvariable for NULL. If NULL then handle error, else assigntempto the target variable. If this check is not made, then the result is a unrecoverable memory leak