Bug 215706 - SYS_vfork syscall may cause Segmentation fault
Summary: SYS_vfork syscall may cause Segmentation fault
Status: NEW
Alias: None
Product: Linux
Classification: Unclassified
Component: Kernel (show other bugs)
Hardware: x86-64 Linux
: P1 normal
Assignee: Virtual assignee user for helpdesk
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2022-03-21 03:55 UTC by Odysseus_sz
Modified: 2023-12-08 03:06 UTC (History)
1 user (show)

See Also:
Kernel Version:
Subsystem:
Regression: No
Bisected commit-id:


Attachments

Description Odysseus_sz 2022-03-21 03:55:37 UTC
Hi Friends:
  I found syscall(SYS_vfork) may cause Segmentation fault on Linux ubuntu-2004-001 5.13.0-1018-oracle. This is also appeared on Linux SUSE 12sp4 4.12.14-94.41.My code shows as below:

#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <stdlib.h>

int main()
{
    pid_t child;
    child = syscall(SYS_vfork);
    if (child) {
        printf("parrent\n");
    }
    exit(0);
}

Used strace to catch the syscall information, can make sure syscall function was successful. Child exit normally, but the parrent caught a signal SIGSEGV. strace's log shows as below:

169580 execve("./vfork_test", ["./vfork_test"], 0x7ffc4571c9b8 /* 26 vars */) = 0
169580 brk(NULL)                        = 0x5639a89ac000
169580 arch_prctl(0x3001 /* ARCH_??? */, 0x7ffc5f573e00) = -1 EINVAL (Invalid argument)
169580 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
169580 openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
169580 fstat(3, {st_mode=S_IFREG|0644, st_size=117077, ...}) = 0
169580 mmap(NULL, 117077, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f1e25e3b000
169580 close(3)                         = 0
169580 openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
169580 read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\360A\2\0\0\0\0\0"..., 832) = 832
169580 pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
169580 pread64(3, "\4\0\0\0\20\0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0", 32, 848) = 32
169580 pread64(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0\237\333t\347\262\27\320l\223\27*\202C\370T\177"..., 68, 880) = 68
169580 fstat(3, {st_mode=S_IFREG|0755, st_size=2029560, ...}) = 0
169580 mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f1e25e39000
169580 pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
169580 pread64(3, "\4\0\0\0\20\0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0", 32, 848) = 32
169580 pread64(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0\237\333t\347\262\27\320l\223\27*\202C\370T\177"..., 68, 880) = 68
169580 mmap(NULL, 2037344, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f1e25c47000
169580 mmap(0x7f1e25c69000, 1540096, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x22000) = 0x7f1e25c69000
169580 mmap(0x7f1e25de1000, 319488, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x19a000) = 0x7f1e25de1000
169580 mmap(0x7f1e25e2f000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1e7000) = 0x7f1e25e2f000
169580 mmap(0x7f1e25e35000, 13920, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f1e25e35000
169580 close(3)                         = 0
169580 arch_prctl(ARCH_SET_FS, 0x7f1e25e3a540) = 0
169580 mprotect(0x7f1e25e2f000, 16384, PROT_READ) = 0
169580 mprotect(0x5639a7a5f000, 4096, PROT_READ) = 0
169580 mprotect(0x7f1e25e85000, 4096, PROT_READ) = 0
169580 munmap(0x7f1e25e3b000, 117077)   = 0
169580 vfork( <unfinished ...>
169581 exit_group(0)                    = ?
169580 <... vfork resumed>)             = 169581
169581 +++ exited with 0 +++
169580 --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=169581, si_uid=0, si_status=0, si_utime=0, si_stime=0} ---
169580 --- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_ACCERR, si_addr=0x7ffc5f573ed0} ---
169580 +++ killed by SIGSEGV (core dumped) +++
Comment 1 Odysseus_sz 2022-07-04 02:23:07 UTC
fix the test program:

#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>

int main()
{
    pid_t child;
    int status;
    child = syscall(SYS_vfork);
    if (child) {
        printf("parrent: %ld\n\n", child);
        child > 0 ? wait(&status) : exit(-1);
        _exit(0);
    }
    printf("child\n");
    exit(0);
}
Comment 2 minzhi 2023-12-08 03:02:23 UTC
I think this segfault is totally legitimate.

As the man page described, child process created by vfork shares the same linear address space with the parent. What child process do to modify the memory(including stack) would reflect the parent.

|  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  call‐ing _exit(2) or one of the exec(3) family of functions.
Comment 3 minzhi 2023-12-08 03:06:36 UTC
(In reply to minzhi from comment #2)
> I think this segfault is totally legitimate.
> 
> As the man page described, child process created by vfork shares the same
> linear address space with the parent. What child process do to modify the
> memory(including stack) would reflect the parent.
> 
> |  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  call‐ing _exit(2) or one of the
> | exec(3) family of functions.

The problem here is mix the glibc wrapper with raw system calls.
If you replace `syscall(SYS_vfork)` with glibc wrapper `vfork`, or replace `exit(0)` with `syscall(SYS_exit, 0)`, there will be no such segmentation fault.

Since the glibc wrapper do more than just `syscall()`, it definitely modified the data.

Note You need to log in before you can comment on or make changes to this bug.