Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does a program accessing illegal pointer to pointer not crash?

A program accessing illegal pointer to pointer does not crash with SIGSEGV. This is not a good thing, but I’m wondering how this could be and how the process survived for many days in production. It is bewildering to me.

I have given this program a go in Windows, Linux, OpenVMS, and Mac OS and they have never complained.

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

void printx(void *rec) { // I know this should have been a **
    char str[1000];
    memcpy(str, rec, 1000);
    printf("%*.s\n", 1000, str);
    printf("Whoa..!! I have not crashed yet :-P");
}

int main(int argc, char **argv) {
    void *x = 0; // you could also say void *x = (void *)10;
    printx(&x);
}
like image 207
pavan.mankala Avatar asked Jul 25 '13 07:07

pavan.mankala


2 Answers

I am not surprised by the lack of a memory fault. The program is not dereferencing an uninitialized pointer. Instead, it is copying and printing the contents of memory beginning at a pointer variable, and the 996 (or 992) bytes beyond it.

Since the pointer is a stack variable, it is printing memory near the top of stack for a ways down. That memory contains the stack frame of main(): possibly some saved register values, a count of program arguments, a pointer to the program arguments, a pointer to a list of environment variables, and a saved instruction register for main() to return, usually in the C runtime library startup code. In all implementations I have investigated, the stack frames below that has copies of the environment variables themselves, an array of pointers to them, and an array of pointers to the program arguments. In Unix environments (which you hint you are using) the program argument strings will be below that.

All of this memory is "safe" to print, except some non-printable characters will appear which might mess up a display terminal.

The chief potential problem is whether there is enough stack memory allocated and mapped to prevent a SIGSEGV during access. A segment fault could happen if there is too little environment data. Or if the implementation puts that data elsewhere so that there are only a few words of stack here. I suggest confirming that by cleaning out the environment variables and re-running the program.

This code would not be so harmless if any of the C runtime conventions are not true:

  • The architecture uses a stack
  • A local variable (void *x) is allocated on the stack
  • The stack grows toward lower numbered memory
  • Parameters are passed on the stack
  • Whether main() is called with arguments. (Some light duty environments, like embedded processors, invoke main() without parameters.)

In all mainstream modern implementations, all of these are generally true.

like image 186
wallyk Avatar answered Oct 15 '22 18:10

wallyk


Illegal memory access is undefined behaviour. This means that your program might crash, but is not guaranteed to, because exact behaviour is undefined.

(A joke among developers, especially when facing coworkers that are careless about such things, is that "invoking undefined behaviour might format your hard drive, it's just not guaranteed to". ;-) )

Update: There's some hot discussion going on here. Yes, system developers should know what actually happens on a given system. But such knowledge is tied to the CPU, the operating system, the compiler etc., and generally of limited usefulness, because even if you make the code work, it would still be of very poor quality. That's why I limited my answer to the most important point, and the actual question asked ("why doesn't this crash"):

The code posted in the question does not have well-defined behaviour, but that does just mean that you can't really rely on what it does, not that it should crash.

like image 30
DevSolar Avatar answered Oct 15 '22 20:10

DevSolar