Why segmentation fault when quitting the main

  • Thread starter jk22
  • Start date
  • Tags
    Fault
In summary: So when (*ret)() returns, it jumps to this new stack frame, and the program crashes. In summary, the code contains a function call and a return address deviation program. The function call returns to a replaceexit() function which pushes more memory onto the stack, then calls a (*ret) function which returns to the replaceexit() function, and the program crashes.
  • #1
jk22
731
24
// Example of variable argument number function call and return adress deviation programm
// never program in a style like this
Code:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

void (*ret)();
void replaceexit(void);void prtarg(char *arg, ...)
{
	char **stack=&arg;

	printf("Return adr : %x\n",(int)(*(stack-1)));
	ret=(void (*)())*(stack-1);
	*(stack-1)=(char *)(replaceexit);

	
	while(*stack)
	{
		printf("%s",*stack);
		stack++;
	}
}

void replaceexit(void)
{
	printf("I come here\n");
	(*ret)();
}

int main(void)
{
	char *arg1;
	arg1=new char[10];
	strcpy(arg1,"Hello\n");

	prtarg(arg1,"Bonjour\n","Salut\n",0);

	printf("Return to main\n");
}

What it actually prints when runned is :

Hello
Bonjour
Salut
I come here // it is in the replace function for the exit
Return to main // it returns to main
Segmentation fault // when quitting the program error

I don't understand, maybe the stack is not treated in a good way ?
 
Last edited by a moderator:
Technology news on Phys.org
  • #2
It looks like you are trying to mess with the stack. Is this an exercise for a class?

The segmentation fault means you're referencing memory outside your program which means one of your pointers is messed up.

Try putting printf stmts to print the value of the pointer instead of the contents of where it points. IE if the pointer has the value of 1000 then print the 1000 don't print what's at memory location 1000. The segmentation is happening because you're trying to print the contents at memory location 1000.

In particular look at the ret pointer.

Better yet run your program in a debugger and step through each line.

I don't think the program has returned to main but the while loop has printed the message you see. A debugger would more clearly show this. ( Don't leave home without it. :-) )
 
Last edited:
  • #3
This code is neither C nor C++. How the H%ll did you compile it?
Code:
char *arg1;
arg1=new char[10];  [color=red]// no way  in C [/color]
To get it to compile in C:
Code:
char *arg1=malloc(10);
After that on return to main the stack pointer is broken and you get an access violation - you attempt to execute non-executable memory.

FWIW - when C runs it starts with code you do not see: often named something __start, when main ends, there is some image rundown code (closing open files, etc.) that executes, kinda like the reverse of __start. It has lots of names depending on the platform. Lets's call it __fini. You forced __fini to execute read-only memory.
What happens is actually implementation dependent. I cannot know what your system actually does.

Most modern OS runtimes have a "noexec stack protective mechanism" to prevent malicious code from doing crashing the stack. Somebody is else is sure to say 'undefined behavior!' which is what I'm saying with a marginal explanation. Simply saying 'UB' is not an answer to this kind of thing, IMO. The fact that coders do this is proof , to me, they do not understand the full consequences of UB. You obviously did this intentionally. In the hope of learning something...
 
  • #4
Here is what happens. When the prtarg is called, the stack looks like this:

address to return to main
address of "Hello"
address of "Bonjour"
address of "Salut"
0

Then prtarg does a nasty thing and the stack becomes

address to "return" to replaceexit
address of "Hello"
address of "Bonjour"
address of "Salut"
0

I wrote "return" because it does not really return there in the ordinary sense of the word.

Then, when prtarg "returns" to replaceexit, the stack is:

address of "Hello"
address of "Bonjour"
address of "Salut"
0

Note the return address is gone, it was popped off the stack as part of the return sequence from prtarg. Then replaceexit does its thing and CALLS (!) into main using the return address previously stored in the ret variable. This makes the stack inside main() look like this:

address to return to into replaceexit
address of "Hello"
address of "Bonjour"
address of "Salut"
0

At this stage main executes the stack cleanup sequence, it has to remove the addresses of the strings it pushed there before calling prtarg, and the sequence expects the stack will be like this:

address of "Hello"
address of "Bonjour"
address of "Salut"
0

So it just pops four addresses off the stack. But the stack in reality is different, so after that sequence is complete, the stack is:

0
(data pushed on stack earlier)

This is where things go very wrong. main() probably expects that the (data pushed on stack earlier) starts with an address to return to, so it returns to the zero address, which produces the message you see.
 
  • Like
Likes 1 person
  • #5
As voko posed, the first problem occurs when printarg() returns, since the return address is popped off the stack, and the program junps to replaceexit() with no space allocated on the stack for a return address, so at the start of replaceexit(), what should be the return address is the first argument passed to printarg().

The next problem occurs because replaceexit() pushes even more stuff on the stack (usually ebp, maybe more stuff), then calls (*ret)() which pushes a return address onto the stack, and jumps back to just after where main called printarg(). At this point, the stack contains the return address from the call to (*ret)(), and whatever replaceexit() pushed onto the stack before the call to (*ret)().
 
  • #6
jim mcnamara said:
This code is neither C nor C++. How the H%ll did you compile it?

I just used g++ but I know I make mix between c and c++.

Thanks for your help.

Still I don't see how to make the program todo this kind of replacement of the return adress that works (I suppose it's used by some virii program)
 
  • #7
jk22 said:
Still I don't see how to make the program todo this kind of replacement of the return adress that works (I suppose it's used by some virii program)
I'm not sure what would be gained by replacing the return address in C / C++, since it messes up the stack.

A call back (end action) pointer to function could be assigned to one of multiple functions, and this is useful for interrupt driven code used in device drivers or asynchronous I/O call for some operating systems.
 
  • #8
jk22 said:
Still I don't see how to make the program todo this kind of replacement of the return adress that works (I suppose it's used by some virii program)

Malware will typically NOT return to the main function after it has gained control, because at that point the stack may be smashed so badly the application won't work anyway. It is possible, though, by manipulating the stack pointer directly, which would require use of the assembly language or some non-standard C language or library features. There may be some clever technique in "pure" C; I write pure as "pure" because that will again very significantly depend on the platform and the compiler.
 
  • #9
One C feature that does something like this is the longjmp feature that allows you to exit some subroutine skipping the return back up a few levels to an associated setjmp which resets the stack. Its kind of like the throw catch mechanism.
 
  • #10
setjmp / longjmp example:

Code:
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>

static jmp_buf cpustate;    /* this is an array of integers */

void fun0(void){
    printf("fun0\n");
    longjmp(cpustate, 1);
}

void fun1(void){
    printf("fun1\n");
    longjmp(cpustate, 2);
}

void fun2(void){
    printf("fun2\n");
    longjmp(cpustate, 3);
}

int main(int argc, char *argv[])
{
int i;
    i = setjmp(cpustate);   /* initial call returns 0 */
                            /* longjmp also returns here */
                            /* setting i to longjmp parameter */
    switch(i){
      case 0:
        fun0();
        break;
      case 1:
        fun1();
        break;
      case 2:
        fun2();
        break;
      case 3:
        printf("main\n");
        break;
      default:
        break;
    }
    return(0);
}
 
Last edited:

FAQ: Why segmentation fault when quitting the main

Why do I get a segmentation fault when exiting the main function?

A segmentation fault occurs when a program attempts to access memory that it does not have permission to access. This can happen when there is a bug in the code that causes the program to try and access invalid memory addresses. In the case of exiting the main function, it is possible that there is a memory access error in the code that is executed before the program terminates, causing the segmentation fault.

How can I fix a segmentation fault when quitting the main function?

To fix a segmentation fault when exiting the main function, you will need to debug your code to find the source of the problem. This can involve using a debugger or printing out the memory addresses being accessed to identify the specific line of code causing the issue. Once you have identified the problem, you can then make the necessary changes to the code to prevent the segmentation fault from occurring.

Is a segmentation fault always caused by a bug in the code?

Not necessarily. While a segmentation fault is often caused by a bug in the code, it can also occur due to other factors such as hardware or operating system issues. It is important to thoroughly test and debug your code to identify and fix any potential bugs, but also keep in mind that there may be other underlying causes for a segmentation fault.

Can a segmentation fault be prevented?

In some cases, yes. By writing well-structured and error-free code, you can minimize the chances of a segmentation fault occurring. This includes avoiding accessing memory that is out of bounds, properly allocating and freeing memory, and handling errors and exceptions effectively. However, since a segmentation fault can also be caused by external factors, it is not always possible to prevent them entirely.

How does a segmentation fault affect my program?

A segmentation fault can cause your program to crash or terminate unexpectedly. This can be particularly problematic if your program is performing important tasks or handling sensitive data. Additionally, a segmentation fault can also make it difficult to debug and troubleshoot issues in your code, as the program may not provide any useful error messages or information before crashing.

Similar threads

Replies
4
Views
950
Replies
1
Views
2K
Replies
17
Views
3K
Replies
1
Views
2K
Replies
89
Views
5K
Replies
36
Views
2K
Replies
23
Views
2K
Back
Top