0

I am doing some test over the use of the malloc function. Here is the code I am using

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

int main(){
    char * string = NULL;
    int size = 0;

    printf("Insert the number of caracters that u want to enter: ");
    scanf("%d", &size);

    string = malloc(size * sizeof(char));   //(char *)calloc(size, char)
    if(string != NULL){
        printf("Insert the string of %d size: ", size);
        scanf("%s", string);    //gets(string)

        printf("The string that u entered is \"%s\"\n", string);
    }
    
    free(string);

    return 0;
}

Now if I enter size = 4 and then I enter a string like "12345678910" the output of the printf is correct even if I enter a size of 0 and I enter a long string the output of printf is correct. Why is this happening?

enter image description here

Luca90
  • 63
  • 1
  • 2
  • 12
  • 1
    Remember to allocate `1` more than the number of "real" characters to account for the `'\0'`, ie. allocate `6` bytes for the string `"hello"`. – pmg May 01 '21 at 15:33
  • Other than my other comment, you are invoking **Undefined Behaviour** by trying to access memory that does not belong to your program. – pmg May 01 '21 at 15:34
  • 1
    It "works" because undefined behavior means that the behavior is not defined. It might work, and it might crash. – klutt May 01 '21 at 15:36
  • Have a read through https://stackoverflow.com/questions/2397984/undefined-unspecified-and-implementation-defined-behavior – Nate Eldredge May 01 '21 at 16:10
  • I am aware that I was invoking Undefined Behaviour it was the purpose of my test but I thought that my code would crash or print some not understandable characters. The string was printed correctly all the times I tested the code instead. I took the code from a course and I wanted to test it searching for problems – Luca90 May 01 '21 at 17:03

3 Answers3

1

First of all, you should not call malloc with 0. According to the specification, you will either get a null pointer or a unique pointer that can be freed with free. Secondly, scanf is not safe. It doesnt know how big the buffer is you are giving it to store the string, if the size of the string is larger than the buffer, it will effectively write to memory that doesnt belong to you. That is non defined behaviour and may work by luck. Instead use something like fgets, that lets you specifiy how many characters to read. Also when allocating a buffer for your string, you should account for terminating null character.

lulle2007200
  • 888
  • 9
  • 20
1

It works because most of the time the memory behind the malloc()ed region is not being used AND is zero, ending the string.

scanf() can only take a constant maximum width, or then the "m" flag (%ms), which is exactly for this situation:

The %c, %s, and %[ conversion specifiers shall accept an optional assignment-allocation character 'm', which shall cause a memory buffer to be allocated to hold the string converted including a terminating null character [...] The system shall allocate a buffer as if malloc() had been called.

See: difference between %ms and %s scanf

0

As others have said, this will write out of bounds if the input read by scanf is too big. Writing out of bounds, reading invalid memory, etc. are undefined behavior. Being undefined admits the possibilities that the code may crash or work just fine as you see here.

For debugging cases when you suspect there may be such a problem, I'd suggest using address sanitizer (and the other sanitizers). Those will identify the exact spot of issues in your program by turning various kinds of bugs and undefined behavior into reproducible hard errors.

$ gcc -fsanitize=address foo.c -static-libasan -g                                                                                                                 
$ ./a.out                                                                                                                                                            
Insert the number of caracters that u want to enter: 2
Insert the string of 2 size: aaa
=================================================================
==27085==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000000012 at pc 0x55efb85ade6a bp 0x7ffc2efa1b40 sp 0x7ffc2efa12c8
WRITE of size 4 at 0x602000000012 thread T0
    #0 0x55efb85ade69 in scanf_common(void*, int, bool, char const*, __va_list_tag*) (/home/ry/a.out+0x49e69)
    #1 0x55efb85aec38 in __isoc99_vscanf (/home/ry/a.out+0x4ac38)
    #2 0x55efb85aed3e in __interceptor___isoc99_scanf (/home/ry/a.out+0x4ad3e)
    #3 0x55efb866a115 in main /home/ry/foo.c:14
    #4 0x7f5902ecebf6 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21bf6)
    #5 0x55efb856b9d9 in _start (/home/ry/a.out+0x79d9)

0x602000000012 is located 0 bytes to the right of 2-byte region [0x602000000010,0x602000000012)
allocated by thread T0 here:
    #0 0x55efb86297b0 in malloc (/home/ry/a.out+0xc57b0)
    #1 0x55efb866a0d1 in main /home/ry/foo.c:11
    #2 0x7f5902ecebf6 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21bf6)

SUMMARY: AddressSanitizer: heap-buffer-overflow (/home/ry/a.out+0x49e69) in scanf_common(void*, int, bool, char const*, __va_list_tag*)
Shadow bytes around the buggy address:
  0x0c047fff7fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c047fff7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c047fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c047fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c047fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c047fff8000: fa fa[02]fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8010: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8050: 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
==27085==ABORTING
Ryan Livingston
  • 1,898
  • 12
  • 18