我正在使用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... */
一切似乎都很好。
谢谢大家的帮助!
答案 0 :(得分:0)
man (3) vfork
vfork()函数应该等效于fork(),除非如果vfork()创建的进程修改除了用于存储vfork的返回值的pid_t类型的变量之外的任何数据,则行为未定义( ),或者从调用vfork()的函数返回,或者在成功调用_exit()或exec系列函数之前调用任何其他函数。
因此,在孩子中,您可以拨打_exit
或exec
。就是这样。
答案 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.