* 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

Reply via email to