when i try the following snippet i am getting an error called stack smashing detected. what could be the reason for this potential bug? Can some one explain?
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int glob=88;
int main()
{
int loc=2;
pid_t pid=vfork();
if(pid==0)
{
printf("Inside child");
glob++;
loc++;
printf("%d %d" ,glob,loc);
}
else
{
printf("Inside parent");
glob++;
loc++;
printf("%d %d",glob,loc);
}
}
and the output when I run this code is like that
user018#sshell ~ $ gcc one.c
user018#sshell ~ $ ./a.out
Inside child89 3Inside parent90 945733057*** stack smashing detected ***: a.out
- terminated
a.out: stack smashing attack in function <unknown> - terminated
KILLED
From the Linux man page (and POSIX):
The vfork() function has the same effect as fork(2), except that the behavior is undefined if the process created by vfork() either modifies any data other than a variable of type pid_t used to store the return value from vfork(), or returns from the function in which vfork() was called, or calls any other function before successfully calling _exit(2) or one of the exec(3) family of functions.
You're modifying data and returning from the function in which vfork was invoked - both of these lead to undefined behavior. vfork is not equivalent to fork, the number of things you can do in a vforkd child are very, very limited. It should only be used in very specific circumstances, essentially when the only thing you need to do in the child is exec something else.
See your operating system's man page for the full details.
vfork() is used to create new processes without copying the page tables of the parent process. So you can't modify the variables in the child process because they are not there anymore. Use fork() instead.
One more thing, it's better to add a \n to the end of printf() because stdout is line buffered by default.
1) I would definitely add a "return 0", since you declared "int main()".
2) If you wanted to disable the warning, use -fno-stack-protector in your compile line.
3) If you wanted to debug where the error is coming from, use "-g" in your compile line, and run the program from gdb (instead of running ./a.out).
My closest to "what's wrong" is this man page about vfork():
http://linux.die.net/man/3/vfork
The vfork() function shall be equivalent to fork(), except that the
behavior is undefined if the process created by vfork() either
modifies any data other than a variable of type pid_t used to store
the return value from vfork(), or returns from the function in which
vfork() was called, or calls any other function before successfully
calling _exit() or one of the exec family of functions.
For Linux, just use "fork()", and I think you'll be happy :)
Related
From the manual page I know that:
exit() flushes output buffers while _exit,_Exit,exit_group don't.
In the code below, the content of test.log will be hello\nhello\n only if exit() was called in child process, which is the same as I tested.
But when there's no statements in child process, it will behave like it's calling exit(), the content of test.log will be hello\nhello\n too.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main()
{
int pid;
FILE *fp = fopen("test.log", "w");
fprintf(fp, "hello\n");
pid = fork();
if (pid == 0)
{
// do something
;
}
else
{
wait(NULL);
exit(1);
}
}
Through ltrace and strace I can see both parent and child called exit_group.
Since I called exit in parent, so it can be judged by using ltrace that exit will call exit_group.
But in the opposite I can't judge whether child process called exit.
Does gcc called exit in child process implicitly? It may be harmful to call exit in child process as many people said. If not, then why the buffer was flushed?
Testing gcc and glibc version:
gcc version 6.3.0 20170516 (Debian 6.3.0-18+deb9u1)
GNU C Library (Debian GLIBC 2.24-11+deb9u4) stable release version
2.24
Promoting PSkocik's comment to an answer:
Returning from main is always equivalent to calling exit(). Either way is supposed to represent "normal" termination of a process, where you want for all the data you buffered to actually get written out, all atexit() cleanup functions called, and so on.
Indeed, the C library's startup code is often written simply as exit(main(argc, argv));
If you want the "harsher" effect of _exit() or _Exit() then you have to call them explicitly. And a forked child process is a common instance where this is what you do want.
But when there's no statements in child process, it will behave like it's calling exit(), the content of test.log will be hello\nhello\n too.
You have a statement in child process (implicit):
When child process returns from main(), the C runtime makes a call to exit(0); (or better, something equivalent to exit(main(arc, argv, envp));, indeed) that implies the flushing of buffers you are trying to avoid.
If you want to eliminate one of the hello\ns, just fflush(fp); before calling to fork() system call, as the call will duplicate fp in both processes, each with a partially full buffer the same way, and this is the reason you get the buffer contents duplicated. Or better flush all output buffers with fflush(NULL);.
In one of the special cases shown below, getpid() for the grandchild created with vfork() returns the PID of the parent process.
#include <stdio.h>
#include <stdlib.h>
int main() {
if(vfork()) { /* parent */
printf("parent pid = %d\n", getpid());
exit(0);
} else {
if(vfork()) { /* child */
printf("child pid = %d\n", getpid());
exit(0);
} else { /* grandchild */
printf("grandchild pid = %d\n", getpid());
exit(0);
}
}
}
Compiled as gcc main.c, this works as expected:
grandchild pid = 12241
child pid = 12240
parent pid = 12239
Compiled as gcc main.c -lpthread, the grandchild PID is incorrect:
grandchild pid = 12431
child pid = 12432
parent pid = 12431
Any clues why? Is this one of the undefined behavior cases?
With ps and strace, I can see the correct PID. BTW, the same example code works fine with fork(), i.e. correct getpid() with or without -lpthread.
getpid is not one of the two operations you're permitted to perform after vfork in the child; the only two are execve and _exit. It happens that glibc caches the process's pid in userspace, and does not update this cache on vfork (since it would modify the parent's cached value, and since it's not needed since valid code can't observe the result); that's the mechanism of the behavior you're seeing. The caching behavior differs slightly with -lpthread linked. But the underlying reason is that your code is invalid.
Pretty much, don't use vfork. There's basically nothing you can do with it.
From the manual page for vfork():
The vfork() function has the same effect as fork(2), except that the behavior is undefined if the
process created by vfork() either modifies any data other than a variable of type pid_t used to store the return value
from vfork(), or returns from the function in which vfork() was called, or calls any other function before successfully
calling _exit(2) or one of the exec(3) family of functions.
It's not very nicely worded, but what this is saying is that the only things that a child process can do after vfork() are:
Check the return value.
Call one of the exec*() family of functions.
Call _exit().
This is because:
vfork() is a special case of clone(2). It is used to create new processes without copying the page tables of the parent process. It may be useful in performance-sensitive applications where a child is created which then immediately
issues an execve(2).
In other words, the intended use of vfork() is only to create children that will execute other programs through exec*(), making this faster than a normal fork() because the page table of the parent is not duplicated in the child (since it's going to be replaced by exec*() anyway). Even then though, vfork() only has a real advantage if this kind of operation needs to be performed multiple times. Since parent memory is not copied, accessing it in any way is undefined behavior.
here is the requirements for vfork()
#include <sys/types.h>
#include <unistd.h>
pid_t vfork(void);
Notice the OPs posted code fails to include the needed header files.
The following code never ends. Why is that?
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#define SIZE 5
int nums[SIZE] = {0, 1, 2, 3, 4};
int main()
{
int i;
pid_t pid;
pid = vfork();
if(pid == 0){ /* Child process */
for(i = 0; i < SIZE; i++){
nums[i] *= -i;
printf(”CHILD: %d “, nums[i]); /* LINE X */
}
}
else if (pid > 0){ /* Parent process */
wait(NULL);
for(i = 0; i < SIZE; i++)
printf(”PARENT: %d “, nums[i]); /* LINE Y */
}
return 0;
}
Update:
This code is just to illustrate some of the confusions I have regarding to vfork(). It seems like when I use vfork(), the child process doesn't copy the address space of the parent. Instead, it shares the address space. In that case, I would expect the nums array get updated by both of the processes, my question is in what order? How the OS synchronizes between the two?
As for why the code never ends, it is probably because I don't have any _exit() or exec() statement explicitly for exit. Am I right?
UPDATE2:
I just read: 56. Difference between the fork() and vfork() system call?
and I think this article helps me with my first confusion.
The child process from vfork() system call executes in the parent’s
address space (this can overwrite the parent’s data and stack ) which
suspends the parent process until the child process exits.
To quote from the vfork(2) man page:
The vfork() function has the same effect as fork(), except that the behaviour is undefined if the process created by vfork() either modifies any data other than a variable of type pid_t used to store the return value from vfork(), or returns from the function in which vfork() was called, or calls any other function before successfully calling _exit() or one of the exec family of functions.
You're doing a whole bunch of those things, so you shouldn't expect it to work. I think the real question here is: why you're using vfork() rather than fork()?
Don't use vfork. That's the simplest advice you can get. The only thing that vfork gives you is suspending the parent until the child either calls exec* or _exit. The part about sharing the address space is incorrect, some operating systems do it, other choose not to because it's very unsafe and has caused serious bugs.
Last time I looked at how applications use vfork in reality the absolute majority did it wrong. It was so bad that I threw away the 6 character change that enabled address space sharing on the operating system I was working on at that time. Almost everyone who uses vfork at least leaks memory if not worse.
If you really want to use vfork, don't do anything other than immediately call _exit or execve after it returns in the child process. Anything else and you're entering undefined territory. And I really mean "anything". You start parsing your strings to make arguments for your exec call and you're pretty much guaranteed that something will touch something it's not supposed to touch. And I also mean execve, not some other function from the exec family. Many libc out there do things in execvp, execl, execle, etc. that are unsafe in a vfork context.
What is specifically happening in your example:
If your operating system shares address space the child returning from main means that your environment cleans things up (flush stdout since you called printf, free memory that was allocated by printf and such things). This means that there are other functions called that will overwrite the stack frame the parent was stuck in. vfork returning in the parent returns to a stack frame that has been overwritten and anything can happen, it might not even have a return address on the stack to return to anymore. You first entered undefined behavior country by calling printf, then the return from main brought you into undefined behavior continent and the cleanup run after the return from main made you travel to undefined behavior planet.
From the official specification:
the behavior is undefined if the process created by vfork() either modifies any data other than a variable of type pid_t used to store the return value from vfork(),
In your program you modify data other than the pid variable, meaning the behavior is undefined.
You also have to call _exit to end the process, or call one of the exec family of functions.
The child must _exit rather than returning from main. If the child returns from main, then the stack frame does not exist for the parent when it returns from vfork.
just call the _exit instead of calling return or insert _exit(0) to the last line in "child process". return 0 calls exit(0) while close the stdout, so when another printf follows, the program crashes.
#include<stdio.h>
#include<sys/types.h>
#include<stdlib.h>
int turn;
int flag[2];
int main(void)
{
int pid,parent=1;
printf("before vfork\n");
if((pid=vfork())<0)
{
perror("vfork error\n");
return 1;
}
while(1)
{
if(pid==0)
{
while(parent==1)
{
sleep(2);
}
parent=0;
flag[0]=1;
turn=1;
while(flag[1]&&turn==1);
printf("This is critical section:parent process\n");
flag[0]=0;
}
else
{
parent=2;
printf("This is parent");
flag[1]=1;
turn=0;
while(flag[0]&&turn==0);
printf("This is critical section:child process %d \n",pid);
flag[1]=0;
}
}
}
This is my code. Can anyone tell why control is not coming to my parent process.
man 2 vfork says:
(From POSIX.1) The vfork() function has the same effect as fork(2),
except that the behavior is undefined if the process created by vfork()
either modifies any data other than a variable of type pid_t used to
store the return value from vfork(), or returns from the function in
which vfork() was called, or calls any other function before success-
fully calling _exit(2) or one of the exec(3) family of functions.
Because you are modifying data in child process, vfork() behaviour is undefined, so whatever it does is correct (here by "correct" I mean "complies with the spec").
Because, the entire virtual address space of the parent is replicated in the child.
So two processes has separate address spaces. And flag[1] never become 1 in parent process.
vfork() was designed for fast creation of processes with execve(): vfork() + immediate execve(). And in cases when children modifies any data of parent process, behavior is undefined. Check man about details.
In my case, that behavior was such that parent reach control only after close of children.
Here is the important bit from the Linux man page. Keywords: "parent is suspended until the child ..."
vfork() differs from fork(2) in that the parent is suspended until the
child makes a call to execve(2) or _exit(2). The child shares all mem-
ory with its parent, including the stack, until execve(2) is issued by
the child. The child must not return from the current function or call
exit(3), but may call _exit(2).
Your parent process has been suspended. Consider using clone.
This code doesn't guarantee fairness; it only prevents deadlock.
There's nothing stopping the child process from executing over and over again.
If you want the lock to be fair, you'll have to make it so.
What is difference between return and exit statement in C programming when called from anywhere in a C program?
return returns from the current function; it's a language keyword like for or break.
exit() terminates the whole program, wherever you call it from. (After flushing stdio buffers and so on).
The only case when both do (nearly) the same thing is in the main() function, as a return from main performs an exit().
In most C implementations, main is a real function called by some startup code that does something like int ret = main(argc, argv); exit(ret);. The C standard guarantees that something equivalent to this happens if main returns, however the implementation handles it.
Example with return:
#include <stdio.h>
void f(){
printf("Executing f\n");
return;
}
int main(){
f();
printf("Back from f\n");
}
If you execute this program it prints:
Executing f
Back from f
Another example for exit():
#include <stdio.h>
#include <stdlib.h>
void f(){
printf("Executing f\n");
exit(0);
}
int main(){
f();
printf("Back from f\n");
}
If you execute this program it prints:
Executing f
You never get "Back from f". Also notice the #include <stdlib.h> necessary to call the library function exit().
Also notice that the parameter of exit() is an integer (it's the return status of the process that the launcher process can get; the conventional usage is 0 for success or any other value for an error).
The parameter of the return statement is whatever the return type of the function is. If the function returns void, you can omit the return at the end of the function.
Last point, exit() come in two flavors _exit() and exit(). The difference between the forms is that exit() (and return from main) calls functions registered using atexit() or on_exit() before really terminating the process while _exit() (from #include <unistd.h>, or its synonymous _Exit from #include <stdlib.h>) terminates the process immediately.
Now there are also issues that are specific to C++.
C++ performs much more work than C when it is exiting from functions (return-ing). Specifically it calls destructors of local objects going out of scope. In most cases programmers won't care much of the state of a program after the processus stopped, hence it wouldn't make much difference: allocated memory will be freed, file ressource closed and so on. But it may matter if your destructor performs IOs. For instance automatic C++ OStream locally created won't be flushed on a call to exit and you may lose some unflushed data (on the other hand static OStream will be flushed).
This won't happen if you are using the good old C FILE* streams. These will be flushed on exit(). Actually, the rule is the same that for registered exit functions, FILE* will be flushed on all normal terminations, which includes exit(), but not calls to _exit() or abort().
You should also keep in mind that C++ provide a third way to get out of a function: throwing an exception. This way of going out of a function will call destructor. If it is not catched anywhere in the chain of callers, the exception can go up to the main() function and terminate the process.
Destructors of static C++ objects (globals) will be called if you call either return from main() or exit() anywhere in your program. They wont be called if the program is terminated using _exit() or abort(). abort() is mostly useful in debug mode with the purpose to immediately stop the program and get a stack trace (for post mortem analysis). It is usually hidden behind the assert() macro only active in debug mode.
When is exit() useful ?
exit() means you want to immediately stops the current process. It can be of some use for error management when we encounter some kind of irrecoverable issue that won't allow for your code to do anything useful anymore. It is often handy when the control flow is complicated and error codes has to be propagated all way up. But be aware that this is bad coding practice. Silently ending the process is in most case the worse behavior and actual error management should be preferred (or in C++ using exceptions).
Direct calls to exit() are especially bad if done in libraries as it will doom the library user and it should be a library user's choice to implement some kind of error recovery or not. If you want an example of why calling exit() from a library is bad, it leads for instance people to ask this question.
There is an undisputed legitimate use of exit() as the way to end a child process started by fork() on Operating Systems supporting it. Going back to the code before fork() is usually a bad idea. This is the rationale explaining why functions of the exec() family will never return to the caller.
I wrote two programs:
int main(){return 0;}
and
#include <stdlib.h>
int main(){exit(0)}
After executing gcc -S -O1. Here what I found watching
at assembly (only important parts):
main:
movl $0, %eax /* setting return value */
ret /* return from main */
and
main:
subq $8, %rsp /* reserving some space */
movl $0, %edi /* setting return value */
call exit /* calling exit function */
/* magic and machine specific wizardry after this call */
So my conclusion is: use return when you can, and exit() when you need.
In C, there's not much difference when used in the startup function of the program (which can be main(), wmain(), _tmain() or the default name used by your compiler).
If you return in main(), control goes back to the _start() function in the C library which originally started your program, which then calls exit() anyways. So it really doesn't matter which one you use.
For the most part, there is no difference in a C program between using return and calling exit() to terminate main().
The time when there is a difference is if you've created code that will be executed after you return from main() that relies on variables local to main(). One way that manifests itself is with setvbuf():
int main(void)
{
char buffer[BUFSIZ];
setvbuf(stdout, buffer, _IOFBF, BUFSIZ);
…code using stdout…
return 0;
}
In this example, the buffer provided via setvbuf() goes out of scope when main() returns, but the code that flushes and closes stdout will be attempting to use that buffer. This leads to undefined behaviour.
The other mechanism is to invoke atexit() with a function that accesses data from main() — via a pointer. This is harder to set up as the functions called via the atexit() mechanism are not given any arguments. So, you have to do something like this:
static void *at_exit_data = 0;
static void at_exit_handler(void)
{
char *str = at_exit_data;
printf("Exiting: %s\n", str);
}
int main(void);
{
char buffer[] = "Message to be printed via functions registered with at_exit()";
at_exit_data = buffer;
at_exit(at_exit_handler);
…processing…
return 0;
}
Again, the buffer pointed at by at_exit_data has ceased to exist when the program returned from main() and so the handler function invokes undefined behaviour.
There is a related function, at_quick_exit(), but the functions registered with it are only called if the quick_exit() function is called, which precludes the functions being called after main() returns.
the return statement exits from the current function and exit() exits from the program
they are the same when used in main() function
also return is a statement while exit() is a function which requires stdlb.h header file