0

我正在使用 armv7 进行 openwrt 开发并面临由 vfork 引起的段错误。

我编写了一个包含以下部分的小型测试程序:

    ...
    pid_t child_t;
    if((child_t = vfork()) < 0)
    {
        printf("error!\n");
        return -1;
    }
    else if(child_t == 0)
    {
        printf("in child:pid =%d\n",getpid());
        sleep(2);
        _exit(0);
    }
    else
    {
        printf("in parent:child_t id = %d,pid = %d\n",child_t,getpid());
    }
    ...

vfork() 函数总是导致段错误,这是 gdb 调试跟踪:

...
   (gdb) c
       Breakpoint 1, main (argc=1, argv=0xbefffed4) at handler.c:33
       33            if((child_t = vfork()) < 0)
   (gdb) stepi 
       0x00008474 in vfork () at         libpthread/nptl/sysdeps/unix/sysv/linux/arm/../../../../../../../libc/sysdeps/linux/arm/vfo        rk.S:71
   71        SAVE_PID
   (gdb) l
   66    
   67    #else
   68    __vfork:
   69    
   70    #ifdef __NR_vfork
   71        SAVE_PID
   72        DO_CALL (vfork)
   73        RESTORE_PID
   74        cmn    r0, #4096
   75        IT(t, cc)
  (gdb) b     libpthread/nptl/sysdeps/unix/sysv/linux/arm/../../../../../../../libc/sysdeps/linux/arm/vfo    rk.S:72
       Breakpoint 2 at 0xb6fcf930: file     libpthread/nptl/sysdeps/unix/sysv/linux/arm/../../../../../../../libc/sysdeps/linux/arm/vfo     rk.S, line 72.
  (gdb) disassemble
           0x00008584 <+40>:        bl      0x8444 <puts>
     => 0x00008588 <+44>:         bl      0x8474 <vfork>
           0x0000858c <+48>:         str    r0, [r11, #-12]
  (gdb)stepi
     ...
  (gdb) stepi 
       0x00008474 in vfork () at     libpthread/nptl/sysdeps/unix/sysv/linux/arm/../../../../../../../libc/sysdeps/linux/arm/vfo    rk.S:71
       71              SAVE_PID
  (gdb) disassemble 
       Dump of assembler code for function vfork:
       =>  0x00008474 <+0>:   add  r12, pc, #0, 12
              0x00008478 <+4>:   add  r12, r12, #8, 20        ; 0x8000
              0x0000847c <+8>:   ldr    pc, [r12, #796]!        ; 0x31c
  (gdb) stepi
      …
   (gdb) disassemble
        Dump of assembler code for function vfork:
             0x00008474 <+0>:   add  r12, pc, #0, 12
             0x00008478 <+4>:   add  r12, r12, #8, 20        ; 0x8000
       => 0x0000847c <+8>:     ldr    pc, [r12, #796]!        ; 0x31c
    (gdb)c
       Continuing.
       Program received signal SIGSEGV, Segmentation fault.
       0xffff0fe0 in ?? ()
    (gdb)

我还在 vfork.S 找到了 vfork 代码:__vfork:

#ifdef __NR_vfork
     SAVE_PID
     DO_CALL (vfork)
     RESTORE_PID
     cmn    r0, #4096
     IT(t, cc)
 #if defined(__USE_BX__)
    bxcc    lr
 #else
     movcc    pc, lr
 #endif

     /* Check if vfork even exists.  */
    ldr     r1, =-ENOSYS
    teq     r0, r1
    bne     __error
#endif

    /* If we don't have vfork, use fork.  */
    DO_CALL (fork)
    cmn     r0, #4096

    /* Syscall worked.  Return to child/parent */
    IT(t, cc)
#if defined(__USE_BX__)
    bxcc    lr
#else
    movcc   pc, lr
#endif

__error:
    b    __syscall_error
#endif

更多信息 - 当像这样绕过 vfork 时 -

   VFORK_LOCK;

-  if ((pid = vfork()) == 0) {  /* Child of vfork... */

+  // if ((pid = vfork()) == 0) {  /* Child of vfork... */

+        pid = syscall(__NR_fork, NULL);

+  if (pid == 0) {  /* Child of vfork... */

一切似乎都很好。

谢谢大家的帮助!

4

2 回答 2

0

man (3) vfork

vfork() 函数应该等效于 fork(),除了如果由 vfork() 创建的进程修改了任何数据,而不是用于存储来自 vfork() 的返回值的 pid_t 类型的变量,或者从调用 vfork() 的函数返回,或在成功调用 _exit() 或 exec 系列函数之一之前调用任何其他函数。

所以在孩子中你可以调用_exitor exec。而已。

于 2014-07-07T05:38:36.517 回答
0

这里的解决方案是启用 CONFIG_KUSER_HELPER 标志。

来自 CONFIG_USERS_HELPERS。

If all of the binaries and libraries which run on your platform
 are built specifically for your platform, and make no use of
 these helpers, then you can turn this option off to hinder
 such exploits. However, in that case, if a binary or library
 relying on those helpers is run, it will receive a SIGILL signal,
 which will terminate the program.
于 2014-09-09T10:40:27.057 回答