* util.c [LINUX] (setbpt): Refactor so that fetching and setting syscall arguments use a common code path and do it in a consistent way. Previous code calls get_arg{0,1}in fork/vfork case, but reads tcp->u_arg[] without get_arg{0,1} in clone case. That looks like it might be a real bug on some architectures. If not, it's still interface abuse. New code is also consistent about the new clone flags being always stored in tcp->u_arg[] afterwards.
Signed-off-by: Jamie Lokier <ja...@shareable.org> --- util.c | 93 ++++++++++++++++++++++++++++++++++++++------------------------- 1 files changed, 56 insertions(+), 37 deletions(-) diff --git a/util.c b/util.c index 82dd9b5..0c4a882 100644 --- a/util.c +++ b/util.c @@ -1475,6 +1475,7 @@ setbpt(struct tcb *tcp) { static int clone_scno[SUPPORTED_PERSONALITIES] = { SYS_clone }; arg_setup_state state; + int real_scno; if (tcp->flags & TCB_BPTSET) { fprintf(stderr, "PANIC: fork breakpoint already set in pid %u\n", @@ -1496,59 +1497,77 @@ setbpt(struct tcb *tcp) } } - switch (known_scno(tcp)) { + real_scno = known_scno(tcp); + + switch (real_scno) { # ifdef SYS_vfork case SYS_vfork: # endif # ifdef SYS_fork case SYS_fork: # endif -# if defined SYS_fork || defined SYS_vfork - if (arg_setup (tcp, &state) < 0 - || get_arg0 (tcp, &state, &tcp->inst[0]) < 0 - || get_arg1 (tcp, &state, &tcp->inst[1]) < 0 - || change_syscall(tcp, clone_scno[current_personality]) < 0 - || set_arg0 (tcp, &state, CLONE_PTRACE|SIGCHLD) < 0 - || set_arg1 (tcp, &state, 0) < 0 - || arg_finish_change (tcp, &state) < 0) - return -1; - tcp->u_arg[arg0_index] = CLONE_PTRACE|SIGCHLD; - tcp->u_arg[arg1_index] = 0; - tcp->flags |= TCB_BPTSET; - return 0; -# endif - case SYS_clone: # ifdef SYS_clone2 case SYS_clone2: # endif - /* ia64 calls directly `clone (CLONE_VFORK | CLONE_VM)' - contrary to x86 SYS_vfork above. Even on x86 we turn the - vfork semantics into plain fork - each application must not - depend on the vfork specifics according to POSIX. We would - hang waiting for the parent resume otherwise. We need to - clear also CLONE_VM but only in the CLONE_VFORK case as - otherwise we would break pthread_create. */ - - if ((arg_setup (tcp, &state) < 0 - || set_arg0 (tcp, &state, - (tcp->u_arg[arg0_index] | CLONE_PTRACE) - & ~(tcp->u_arg[arg0_index] & CLONE_VFORK - ? CLONE_VFORK | CLONE_VM : 0)) < 0 - || arg_finish_change (tcp, &state) < 0)) - return -1; - tcp->flags |= TCB_BPTSET; - tcp->inst[0] = tcp->u_arg[arg0_index]; - tcp->inst[1] = tcp->u_arg[arg1_index]; - return 0; - + break; default: fprintf(stderr, "PANIC: setbpt for syscall %ld on %u???\n", tcp->scno, tcp->pid); + return -1; + } + + if (arg_setup (tcp, &state) < 0 + || get_arg0 (tcp, &state, &tcp->inst[0]) < 0 + || get_arg1 (tcp, &state, &tcp->inst[1]) < 0) + return -1; + + switch (real_scno) { +# ifdef SYS_vfork + case SYS_vfork: +# endif +# ifdef SYS_fork + case SYS_fork: +# endif +# if defined SYS_fork || defined SYS_vfork + /* + * Change to clone(CLONE_PTRACE|SIGCHLD). Even vfork, + * effectively changing it to fork, otherwise we'd + * hang waiting for the parent syscall to return while + * the child is stopped for tracing. All applications + * should accept fork instead, following both POSIX + * and unix tradition. + */ + if (change_syscall(tcp, clone_scno[current_personality]) < 0) + return -1; + tcp->u_arg[arg0_index] = CLONE_PTRACE|SIGCHLD; + tcp->u_arg[arg1_index] = 0; + break; +# endif + + default: /* clone, clone2 */ + /* + * Some archs call `clone (CLONE_VFORK | CLONE_VM)' + * instead of SYS_vfork, but anyone is allowed to use + * CLONE_VFORK. Clear CLONE_VFORK and CLONE_VM, which + * changes it to fork while preserving any other + * requested clone flags. If it's not vfork, CLONE_VM + * must stay for threads. + */ + tcp->u_arg[arg0_index] = tcp->inst[0] | CLONE_PTRACE; + if (tcp->u_arg[arg0_index] & CLONE_VFORK) + tcp->u_arg[arg0_index] &= ~(CLONE_VFORK | CLONE_VM); + tcp->u_arg[arg1_index] = tcp->inst[1]; break; } - return -1; + if (set_arg0 (tcp, &state, tcp->u_arg[0]) < 0 + || set_arg1 (tcp, &state, tcp->u_arg[1]) < 0 + || arg_finish_change (tcp, &state) < 0) + return -1; + + tcp->flags |= TCB_BPTSET; + return 0; } int -- 1.7.0.4 _______________________________________________ uClinux-dev mailing list uClinux-dev@uclinux.org http://mailman.uclinux.org/mailman/listinfo/uclinux-dev This message was resent by uclinux-dev@uclinux.org To unsubscribe see: http://mailman.uclinux.org/mailman/options/uclinux-dev