What is the trick behind strcpy()/uninitialized ch

2020-04-17 02:19发布

问题:

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

void main ()
{
  char *imsi;
  unsigned int i;
  int val;
  char *dest;

  imsi = "405750111";

  strncpy(dest,imsi,5);

  printf("%s",dest);

  /*  i = 10; */
}

In the above code, with the i = 10 assignment is commented as above, the code works fine without error. When assignment is included for compilation, the error (segmentation fault) occurs at strncpy(dest,imsi,5);.

By avoiding optimization to variable i (i.e., volatile int i;), the error is cleared even with the assignment (i = 10) included.

回答1:

In your code, by saying

 strncpy(dest,imsi,5);

you're trying to write into an unitialized pointer dest. It can (and most possibly, it will) point to some memory which is not accessible from your program (invalid memory). It invokes undefined behavior.

There is nothing that can be guaranteed about a program having UB. It can work as expected (depends on what you're expecting, actually) or it may crash or open your bank account and transfer all money to some potential terrorist organization.

N.B - I hope by reading last line you got scared, so the bottom line is

Don't try to write into any uninitialized pointer (memory area). Period.



回答2:

The behaviour of this code is unpredictable because the pointer dest is used before it is initialised. The difference in observed behaviour is only indirectly related to the root cause bug, which is the uninitialised variable. In C it is the programmers responsibility to allocate storage for the output of the strncpy() function and you haven't done that.

The simplest fix is to define an output buffer like this: char dest[10];



回答3:

Assuming you compiled this C source code into machine code for some "normal" architecture and then ran it, the possible effects of read-undefined UB basically boil down to what value floating around in registers or memory ends up getting used.

If the compiler happens to use the same value both times, and that value happened to point to a writeable memory address (and didn't overwrite anything that would break printf), it could certainly happen to work. UB doesn't guarantee a crash. It doesn't guarantee anything. Part of the point of UB is to let the compiler make assumptions and optimize based on them.

Any changes to surrounding code will affect code-gen for that function, and thus will can affect what's in the relevant register when the call happens, or which stack slot is used for dest. Reading from a different stack address will give dest a different value.

Before main, calls to dynamic-linker functions might have dirtied some memory, leaving some pointers floating around in there, maybe including apparently some to writeable memory.

Or main's caller might have a pointer to some writeable memory in a register, e.g. a stack address. Although that's less likely; if a compiler was going to just not even set a register before making a call, strncpy would probably get main's first arg, an integer argc, unless the compiler used that register as a temporary first. But string literals normally go in read-only memory so that's an unlikely explanation in this case. (Even on an ISA / calling convention like ARM where gcc's favourite register for temporaries is R0, the return-value register but also the first arg-passing register. If optimization is disabled so statements compile separately, most expressions will use R0.)