pavan.mankala
pavan.mankala

Reputation: 249

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);
}

Upvotes: 19

Views: 2167

Answers (5)

wallyk
wallyk

Reputation: 57774

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.

Upvotes: 29

DevSolar
DevSolar

Reputation: 70273

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.

Upvotes: 17

Jo&#227;o Fernandes
Jo&#227;o Fernandes

Reputation: 1101

When you have

int main(int argc, char **argv) {
    void *x = 0; // you could also say void *x = (void *)10;
    printx(&x);
}

You are declaring x as a pointer with value 0, and that pointer lives in the stack since it's a local variable. Now, you are passing to printx the address of x, which means that with

memcpy(str, rec, 1000);

you are copying data from above the stack (or in fact from the stack itself), to the stack (because the stack pointer address decreases on each push). The source data is likely to be covered by the same page table entry as you are copying just 1000 bytes, so you get no segmentation fault. However, ultimately, as already written, we are talking about undefined behavior.

Upvotes: 4

Alexander Mihailov
Alexander Mihailov

Reputation: 1152

It would be crashed with great probability if you write to unacceed area. But you are reading, it can be ok. But the behaviour will be still undefined.

Upvotes: 2

Devolus
Devolus

Reputation: 22084

If you dereference an invalid pointer, you are invoking undefined behaviour. Which means, the program can crash, it can work, it could cook some coffee, whatever.

Upvotes: 9

Related Questions