uClibc vfork()导致分段错误

时间:2014-07-07 05:13:01

标签: linux libc openwrt uclibc

我正在使用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... */

一切似乎都很好。

谢谢大家的帮助!

2 个答案:

答案 0 :(得分:0)

man (3) vfork

  

vfork()函数应该等效于fork(),除非如果vfork()创建的进程修改除了用于存储vfork的返回值的pid_t类型的变量之外的任何数据,则行为未定义( ),或者从调用vfork()的函数返回,或者在成功调用_exit()或exec系列函数之前调用任何其他函数。

因此,在孩子中,您可以拨打_exitexec。就是这样。

答案 1 :(得分: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.