Home > Mobile >  I don't understand why am I getting this output in this C program
I don't understand why am I getting this output in this C program

Time:11-09

#include <stdio.h>
#include <string.h>


int main(void) {

    char s1[6] = "Hello";
    char s2[3];

    strcpy(s2, s1);

    printf("%s\n", s2);
    
    return 0;
}

I have written this code in C and expected the output to be either an error or maybe s2 will be {'H', 'e', '\0'} but the actual output was a "Hello" (without "") just like s1 so why did this happen considering that s2 should only hold up to 3 chars which is smaller than s1 size.

CodePudding user response:

Having placed your code in question.c, let's do a little debugging to figure out what is happening.

$ gcc -Wall -fstack-protector-strong -g -o question question.c
$ ./question 
Hello

So far none of the warnings you get with -Wall or -fstack-protector-strong have helped us to catch this issue. So let's try valgrind:

$ valgrind -q ./question 
==1313263== Source and destination overlap in strcpy(0x1ffefff73f, 0x1ffefff742)
==1313263==    at 0x484EF00: strcpy (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==1313263==    by 0x1091C3: main (question.c:10)
==1313263== 
Hello

Looks like valgrind has provided a clue that something is wrong here. Let's try debugging it.

$ gdb ./question 
(gdb) b main
Breakpoint 1 at 0x1195: file question.c, line 5.

(gdb) r
Breakpoint 1, main () at question.c:5
5   int main(void) {

(gdb) n
7       char s1[6] = "Hello";
(gdb) n
10      strcpy(s2, s1);
(gdb) n
12      printf("%s\n", s2);
(gdb) p s1
$1 = "lo\000lo"
(gdb) p s2
$2 = "Hel"
(gdb) p (char*) s2
$3 = 0x7fffffffd7cf "Hello"

From the above debug session you can kind of see what is really going on:

  • Because gdb knows that s2 is only a three-byte array, printing it out dislays Hel.
  • You can see by printing out s1 that the remainder of the string has overflowed into s1.

Why did it do this? It's due how how the variables were laid out on the stack when you ran the program. You can check for yourself by dumping the stack in gdb:

(gdb) p $sp
$4 = (void *) 0x7fffffffd7c0
(gdb) p &s1
$5 = (char (*)[6]) 0x7fffffffd7d2
(gdb) p &s2
$6 = (char (*)[3]) 0x7fffffffd7cf

This shows the in-memory addresses of the stack, and the addresses of s1 and s2. By printing out a hex dump of the stack, you can also visualize this:

(gdb) dump binary memory stackdump $sp $sp 32
(gdb) shell xxd stackdump
00000000: e9dc ffff ff7f 0000 6400 0000 0000 0048  ........d......H
00000010: 656c 6c6f 006c 6f00 0002 9cd6 42c9 4be5  ello.lo.....B.K.

You can see from the above that 15 bytes into the stack is where s2 was, and confirm that as follows:

(gdb) p (char*) $sp 15
$7 = 0x7fffffffd7cf "Hello"
(gdb) p (char*) s2
$8 = 0x7fffffffd7cf "Hello"

... and similarly, you can see that s1 was 18 bytes into the stack and has been overwritten by the write to s2:

(gdb) p (char*) $sp 18
$10 = 0x7fffffffd7d2 "lo"
(gdb) p (char*) s1
$11 = 0x7fffffffd7d2 "lo"
  • Related