vfork leads to a memory "free()" crash, but fork doesn't, how it happened?

345 views Asked by At

I was trying to see if vfork creates a child process that shared memory with father process, like below:

#include<stdio.h>
#include<unistd.h>
int main()
{
    int* pi = new int(5);
    int i = 5;
    pid_t id = vfork();
    if (id > 0) //father
    {
        *pi = 4;
        i = 4;
        printf("father set i=%d, *pi=%d\n", i, *pi);
        sleep(2);
        printf("father get i=%d, *pi=%d\n", i, *pi);
        delete pi;
    }
    else //child
    {
        sleep(1);
        printf("child get i=%d, *pi=%d\n", i, *pi);
        i = 3;
        *pi = 3;
        printf("child set i=%d, *pi=%d\n", i, *pi);
    }
    return 0;
}

I expected that the value if i and *pi are shared between father and chid process, but when I executed it: $ g++ myvshare.cpp && ./a.out

child get i=5, *pi=5
child set i=3, *pi=3
father set i=4, *pi=4
father get i=4, *pi=4
*** Error in `./a.out': free(): invalid pointer: 0xb75f9000 ***
======= Backtrace: =========
/lib/i386-linux-gnu/libc.so.6(+0x67257)[0xb74ae257]
/lib/i386-linux-gnu/libc.so.6(+0x6d577)[0xb74b4577]
/lib/i386-linux-gnu/libc.so.6(+0x6dd31)[0xb74b4d31]
/usr/lib/i386-linux-gnu/libstdc++.so.6(_ZdlPv+0x18)[0xb766bd98]
./a.out[0x8048621]
/lib/i386-linux-gnu/libc.so.6(+0x15f2d4)[0xb75a62d4]
======= Memory map: ========
08048000-08049000 r-xp 00000000 08:01 1055312    /home/x/cpp/a.out
08049000-0804a000 r--p 00000000 08:01 1055312    /home/x/cpp/a.out
0804a000-0804b000 rw-p 00001000 08:01 1055312    /home/x/cpp/a.out
0859e000-085c3000 rw-p 00000000 00:00 0          [heap]
b7200000-b7221000 rw-p 00000000 00:00 0
b7221000-b7300000 ---p 00000000 00:00 0
b73d3000-b73d5000 rw-p 00000000 00:00 0
b73d5000-b73f1000 r-xp 00000000 08:01 1181015    /lib/i386-linux-gnu/libgcc_s.so.1
b73f1000-b73f2000 rw-p 0001b000 08:01 1181015    /lib/i386-linux-gnu/libgcc_s.so.1
b73f2000-b7445000 r-xp 00000000 08:01 1181047    /lib/i386-linux-gnu/libm-2.23.so
b7445000-b7446000 r--p 00052000 08:01 1181047    /lib/i386-linux-gnu/libm-2.23.so
b7446000-b7447000 rw-p 00053000 08:01 1181047    /lib/i386-linux-gnu/libm-2.23.so
b7447000-b75f6000 r-xp 00000000 08:01 1180977    /lib/i386-linux-gnu/libc-2.23.so
b75f6000-b75f7000 ---p 001af000 08:01 1180977    /lib/i386-linux-gnu/libc-2.23.so
b75f7000-b75f9000 r--p 001af000 08:01 1180977    /lib/i386-linux-gnu/libc-2.23.so
b75f9000-b75fa000 rw-p 001b1000 08:01 1180977    /lib/i386-linux-gnu/libc-2.23.so
b75fa000-b75fd000 rw-p 00000000 00:00 0
b75fd000-b776a000 r-xp 00000000 08:01 400094     /usr/lib/i386-linux-gnu/libstdc++.so.6.0.21
b776a000-b776b000 ---p 0016d000 08:01 400094     /usr/lib/i386-linux-gnu/libstdc++.so.6.0.21
b776b000-b7770000 r--p 0016d000 08:01 400094     /usr/lib/i386-linux-gnu/libstdc++.so.6.0.21
b7770000-b7771000 rw-p 00172000 08:01 400094     /usr/lib/i386-linux-gnu/libstdc++.so.6.0.21
b7771000-b7774000 rw-p 00000000 00:00 0
b7788000-b778b000 rw-p 00000000 00:00 0
b778b000-b778d000 r--p 00000000 00:00 0          [vvar]
b778d000-b778e000 r-xp 00000000 00:00 0          [vdso]
b778e000-b77b0000 r-xp 00000000 08:01 1180949    /lib/i386-linux-gnu/ld-2.23.so
b77b0000-b77b1000 rw-p 00000000 00:00 0
b77b1000-b77b2000 r--p 00022000 08:01 1180949    /lib/i386-linux-gnu/ld-2.23.so
b77b2000-b77b3000 rw-p 00023000 08:01 1180949    /lib/i386-linux-gnu/ld-2.23.so
bf9ca000-bf9eb000 rw-p 00000000 00:00 0          [stack]
Terminated (core dupm)

What I was confused:

  1. I expect that vfork should differ from fork in that i and *pi are shared. But still vfork seem to have COW for i and *pi, when father sets the value, child still gets old value, and vice versa.

  2. Why there's a core dump saying "free()" failed? I tried to change vfork to fork, no such problem. How did this happen, inside libc, or glibc?

Thanks!

1

There are 1 answers

0
Stargateur On BEST ANSWER

You wrong, with vfork(), the child doesn't share memory with the parent. The memory belongs to the parent only. vfork() differs from fork() because it doesn't copy the memory, but the behavior is undefined is you attempt to use the memory of the parent except the pid_t return by vfork(). Plus, the child must not call another function that exit() or exec() family. For example, it's undefined behavior to call printf() in the child. The child need to finish by exit() or exec().

Finally, the parent thread is blocked until the child call exit() or exec().

Like fork(), the processes created by vfork() inherits file descriptors, signal dispositions, and current working directory.

All information is in the manual of vfork.

TLDR: This is undefined behavior.