简体   繁体   中英

Tiny crashing program

The following program compiles with g++ but then crashes upon running:

class someClass
{
public:
    int const mem;
    someClass(int arg):mem(arg){}
};

int main()
{
    int arg = 0;
    someClass ob(arg);

    float* sample;
    *sample = (float)1;

    return 0;
}

The following program does not crash:

int main()
{

    float* sample;
    *sample = (float)1;

    return 0;
}
float* sample;
*sample = (float)1;

sample is never initialized to point to an object, so when you dereference it, your program crashes. You need to initialize it before you use it, for example:

float f;
float* sample = &f;
*sample = (float)1;

Your second program is still wrong, even though it does not crash. Dereferencing a pointer that does not point to a valid object results in undefined behavior. The result could be your program crashing, some other data in memory getting overwritten, your application appearing to continue to run correctly, or any other result. Your program could appear to run fine today but crash when you run it tomorrow.

After a bit of thinking, I can tell you with some degree of certainty why the 2nd example didn't crash.

When a program is executed, the crt (c runtime) pushes on the stack 3 values: the number of arguments ( int ), the arguments as a char ** and the environment strings also as a char ** , then calls main .

Now when you write your main function, as far as I know it always reads the first 2 values and passes them to the arguments of the function, if there are any. If you include the 3rd argument, it passes the 3rd value too, otherwise it's left on the stack. So the stack at the start of the program looks like this:

+--------+
| # args |  
+--------+    
|  args  |  
+--------+  <------ stack pointer
|  envs  |  
+--------+  

In the first example, you allocate the int and the struct on the stack, then the pointer, so the full stack in the first example looks like:

+--------+
| # args |  
+--------+    
|  args  |  
+--------+  <------ stack pointer
|  arg   |  <------ your integer, initialized in code
+--------+  
|   ob   |  <------ your struct, initialized in code
+--------+  
| sample |  <------ your pointer, uninitalized = garbage
+--------+  

So sample is pure garbage and attempting to dereference it crashes your program.

Now in the second example, the stack looks like this:

+--------+
| # args |  
+--------+    
|  args  |  
+--------+  <------ stack pointer
| sample |  <------ pointer, uninitalized (!)
+--------+  

The pointer is still uninitalized, but the value it overwrote is envp , which is an actual pointer to an array: char ** . When you dereference it, you get back an array of "pointers to char", so you can overwrite it safely (as long as you don't try to treat this as the original pointer anymore). The memory is allocated and you can use it.

Now this is of course heavily implementation specific, but it seems to fit for your compiler right? You can check with gdb that (char**)sample indeed points to an array of environment variables before you overwrite it.

Screenshot from MSVC++ 10, in 32-bit release mode (debug mode initializes all variables forcefully to prevent this kind of stuff):

the pointer in action http://img651.imageshack.us/img651/5918/69916340.png

取消引用未初始化的指针: http : //www.cprogramming.com/debugging/segfaults.html

You are dereferencing an unitialized pointer.

The real interesting thing is, why the second example does not crash for you, because it has the same problem

BTW: For me (gcc 4.4, amd64) both example crash.

If you are really interested in why the second example does not crash for you, compile it with debugging information and start it in a debugger.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM