Beispiel #1
0
static void print_stats(void) {
    struct tracy_ll_item *cur;
    cur = ll->head;

    while (cur) {
        printf("Syscall: %s called %ld times.\n", get_syscall_name(cur->id),
                (long)cur->data);

        cur = cur->next;
    }

    return;
}
Beispiel #2
0
		virtual void AddDump(TraceOutput& out)
		{
			out.Print("syscall pre:  %s(", get_syscall_name(fSyscall));

			if (fParameters != NULL) {
				int32 stringIndex = 0;
				const extended_syscall_info& syscallInfo
					= kExtendedSyscallInfos[fSyscall];
				for (int i = 0; i < syscallInfo.parameter_count; i++) {
					const syscall_parameter_info& paramInfo
						= syscallInfo.parameters[i];
					const uint8* data = (uint8*)fParameters + paramInfo.offset;
					uint64 value = 0;
					bool printValue = true;
					switch (paramInfo.type) {
						case B_INT8_TYPE:
							value = *(uint8*)data;
							break;
						case B_INT16_TYPE:
							value = *(uint16*)data;
							break;
						case B_INT32_TYPE:
							value = *(uint32*)data;
							break;
						case B_INT64_TYPE:
							value = *(uint64*)data;
							break;
						case B_POINTER_TYPE:
							value = (uint64)*(void**)data;
							break;
						case B_STRING_TYPE:
							if (stringIndex < MAX_PARAM_STRINGS
								&& fSyscall != SYSCALL_KTRACE_OUTPUT) {
								out.Print("%s\"%s\"",
									(i == 0 ? "" : ", "),
									fParameterStrings[stringIndex++]);
								printValue = false;
							} else
								value = (uint64)*(void**)data;
							break;
					}

					if (printValue)
						out.Print("%s%#" B_PRIx64, (i == 0 ? "" : ", "), value);
				}
			}

			out.Print(")");
		}
Beispiel #3
0
/**
 * Function called when a SIGSYS is caught by the application. It notifies the
 * user that an error has occurred and either terminates or allows the
 * application to continue execution, based on the DEBUGGING_CLOSE symbol.
 */
static void
sigsys_debugging(int nr, siginfo_t *info, void *void_context)
{
  ucontext_t *ctx = (ucontext_t *) (void_context);
  const char *syscall_name;
  int syscall;
#ifdef USE_BACKTRACE
  size_t depth;
  int n_fds, i;
  const int *fds = NULL;
#endif

  (void) nr;

  if (info->si_code != SYS_SECCOMP)
    return;

  if (!ctx)
    return;

  syscall = (int) ctx->uc_mcontext.M_SYSCALL;

#ifdef USE_BACKTRACE
  depth = backtrace(syscall_cb_buf, MAX_DEPTH);
  /* Clean up the top stack frame so we get the real function
   * name for the most recently failing function. */
  clean_backtrace(syscall_cb_buf, depth, ctx);
#endif

  syscall_name = get_syscall_name(syscall);

  tor_log_err_sigsafe("(Sandbox) Caught a bad syscall attempt (syscall ",
                      syscall_name,
                      ")\n",
                      NULL);

#ifdef USE_BACKTRACE
  n_fds = tor_log_get_sigsafe_err_fds(&fds);
  for (i=0; i < n_fds; ++i)
    backtrace_symbols_fd(syscall_cb_buf, (int)depth, fds[i]);
#endif

#if defined(DEBUGGING_CLOSE)
  _exit(1);
#endif // DEBUGGING_CLOSE
}
Beispiel #4
0
		virtual void AddDump(TraceOutput& out)
		{
			out.Print("syscall post: %s() -> %#" B_PRIx64,
				get_syscall_name(fSyscall), fReturnValue);
		}
Beispiel #5
0
/* Safe forking/cloning
 *
 * This function takes over the PRE fase of a child process' fork
 * syscall. It then forks the child in a controlled manor ensuring
 * tracy will be able to trace the forked process.
 * This function returns the pid of the new child in new_child upon success.
 *
 * Upon error the return value will be -1 and errno will be set appropriately.
 *
 * FIXME: This function memleaks a page upon failure in the child atm.
 * TODO: This function needs a lot more error handling than it contains now.
 * FIXME: Due to the PTRACE_CHECK macro's if a ptrace fails, we will not
 * restore the original signal handler for SIGUSR1.
 */
int tracy_safe_fork(struct tracy_child *c, pid_t *new_child)
{
    tracy_child_addr_t mmap_ret;
    int status;
    long ip, orig_syscall, orig_trampy_pid_reg;
    struct TRACY_REGS_NAME args, args_ret;
    const long page_size = sysconf(_SC_PAGESIZE);
    pid_t child_pid;
    struct sigaction act, old_sigusr1, old_sigchld;
    sigset_t set, old_set;
    struct timespec timeout;
    siginfo_t info;
    int is_vforking = 0;

    child_pid = -1;

    tracy_debug_current(c);

/*
    puts("SAFE_FORKING!");
*/
    /* First let's allocate a page in the child which we shall use
     * to write a piece of forkcode (see trampy.c) to.
     */
   tracy_mmap(c, &mmap_ret,
            NULL, page_size,
            PROT_READ | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS,
            -1, 0
            );

    /* I know this is FUBAR, but bear with me
     *
     * Check for an error value in the return code/address.
     */
    if (mmap_ret < ((tracy_child_addr_t)NULL) &&
            mmap_ret > ((tracy_child_addr_t)-4096)) {
        errno = -(long)mmap_ret;
        perror("tracy_mmap");
        return -1;
    }
/*
    printf("mmap addr: %p\n", (void*)mmap_ret);
*/

    /* XXX - Debug
    printf("trampy_get_safe_entry() = %p\ntrampy_get_code_size() = %d\n",
        trampy_get_safe_entry(), trampy_get_code_size());
    */

    /* Write the piece of code to the child process */
    if (tracy_write_mem(c, (void*) mmap_ret,
            trampy_get_safe_entry(),
            trampy_get_code_size()) < 0) {
        perror("tracy_write_mem");
        return -1;
    }

    /* Fetch the registers to store the original forking syscall and more importantly
     * the instruction pointer.
     */
    PTRACE_CHECK(PTRACE_GETREGS, c->pid, 0, &args, -1);

    /* Deny so we can set the IP on the denied post and do our own fork in a
     * controlled environment */
    tracy_deny_syscall(c);
    c->denied_nr = 0;
    PTRACE_CHECK(PTRACE_SYSCALL, c->pid, 0, 0, -1);
/*
    puts("DENIED, in PRE");
*/
    waitpid(c->pid, &status, __WALL);
/*
    puts("AFTER DENIED, entered POST");
*/

    /* Okay, the child is now in POST syscall mode, and it has
     * just executed a bogus syscall (getpid) inserted by deny syscall.
     *
     * Setup a fork syscall and point the processor to the injected code.
     */
    /*
     * XXX: We do not have to modify the syscall NR since we use this function
     * for fork, clone and vfork.
    args.TRACY_SYSCALL_REGISTER = __NR_fork;
    args.TRACY_SYSCALL_N = __NR_fork;
    */
    orig_syscall = args.TRACY_SYSCALL_REGISTER;
    orig_trampy_pid_reg = args.TRAMPY_PID_REG;

    printf(_r("Safe forking syscall"));
#if 0
    /* TODO: Fix for ABI */
    printf(_r("Safe forking syscall:")" "_g("%s")"\n", get_syscall_name(args.TRACY_SYSCALL_REGISTER));
#endif

    /* Check if this is a vfork-type syscall */
    if (orig_syscall == __NR_vfork)
        is_vforking = 1;

    /* Clone can also cause vfork behaviour */

#pragma message "getreg(&args, 0, 0) is abi dependent and wrong"
    if (orig_syscall == __NR_clone && get_reg(&args, 0, 0) & CLONE_VFORK) {
        puts(_b("clone with CLONE_VFORK detected, treating as vfork call"));
        is_vforking = 1;
    }

    /* XXX: TODO: Should we place an ARM PTRACE_SET_SYSCALL here? */

    /* XXX: The IP we store here is the IP in the PRE phase of the parent process.
     * At that moment the IP points to the instruction following de syscall.
     */
    ip = args.TRACY_IP_REG;
    args.TRACY_IP_REG = (long)mmap_ret;

    /*
    printf(_b("Pointer data @ IP 0x%lx: 0x%lx")"\n", ip,  ptrace(PTRACE_PEEKDATA, c->pid, ip, NULL));
    printf(_b("Pointer data @ IP-4 0x%lx: 0x%lx")"\n", ip - 4,  ptrace(PTRACE_PEEKDATA, c->pid, ip - 4, NULL));
    */

    PTRACE_CHECK(PTRACE_SETREGS, c->pid, 0, &args, -1);

/*
    printf("The IP was changed from %p to %p\n", (void*)ip, (void*)mmap_ret);

    puts("POST, Entering PRE");
*/

    PTRACE_CHECK(PTRACE_SYSCALL, c->pid, 0, 0, -1);
    waitpid(c->pid, &status, __WALL);

    /* At this moment the child is in PRE mode in the trampy code,
     * trying to execute a sched_yield, which we shall now make
     * into a fork syscall.
     */
    PTRACE_CHECK(PTRACE_GETREGS, c->pid, 0, &args_ret, -1);
/*
    printf("The IP is now %p\n", (void*)args_ret.TRACY_IP_REG);
    printf("Modifying syscall back to fork\n");
*/

    /* TODO: Replace the following with a single call to
     * tracy_modify_syscall().
     */
    args_ret.TRACY_SYSCALL_REGISTER = orig_syscall;
    args_ret.TRACY_SYSCALL_N = orig_syscall;

    /* This stores our pid in a specific register, which will then be used by
     * the new child to inform us of its existence.
     */
    args_ret.TRAMPY_PID_REG = getpid();

    /* On ARM the syscall number is not included in any register, so we have
     * this special ptrace option to modify the syscall
     */
    #ifdef __arm__
    PTRACE_CHECK(PTRACE_SET_SYSCALL, c->pid, 0, (void*)orig_syscall, -1);
    #endif

    PTRACE_CHECK(PTRACE_SETREGS, c->pid, 0, &args_ret, -1);

/*
    puts("PRE, Entering POST");
*/

    /* Setup the blocking of signals to atomically wait for them after ptrace */
    sigemptyset(&set);
    sigaddset(&set, SIGUSR1);
    sigaddset(&set, SIGCHLD);
    pthread_sigmask(SIG_BLOCK, &set, &old_set);

    /* Finally before we execute an actual fork syscall
     * setup the SIGUSR1 handler which is used by trampy to
     * inform us of vforking children
     */
    act.sa_sigaction = _tracer_fork_signal_handler;
    sigemptyset(&act.sa_mask);
    act.sa_flags = SA_SIGINFO;
    sigaction(SIGUSR1, &act, &old_sigusr1);
    sigaction(SIGCHLD, &act, &old_sigchld);

    /* Now execute the actual fork.
     *
     * Afterwards the parent will immediately come to a halt
     * while the child will wait for us to attach. See 'trampy.c'
     * for more details.
     */
    PTRACE_CHECK(PTRACE_SYSCALL, c->pid, 0, 0, -1);

    /* Now wait for either SIGCHLD from the forker,
     * or SIGUSR1 from the forkee
     */
    /*sigsuspend(&set);*/
    /*pause();*/
    /*sigwait(&set);*/
    /* XXX: The manpage states all threads within the current process must block
     * aforementioned signals, so there might be some trouble caused by using tracy
     * within a multithreaded larger whole. If not all threads block these signals
     * some thread might still handle them before we get to call sigwaitinfo which
     * causes lock-up of this thread in the worst case or otherwise a race condition
     * wherein we never restore the child or the parent in case of vfork().
     */
    while (1) {
        sigwaitinfo(&set, &info);
        /* In case of SIGCHLD the parent or another process might've stopped */
        if (info.si_signo == SIGCHLD && info.si_pid == c->pid) {
            /* The parent has stopped (completed its syscall), in case of vfork
             * this means the vfork failed. If it didn't something that shouldn't
             * happen occurred.
             */
            if (is_vforking) {
                /* FIXME: There must be a more elegant way to handle failure,
                 * which we currently don't do anyway so go.. go.. go..
                 */
                printf(_r("tracy: During vfork(), failure in parent.")"\n");
                child_pid = -1;
                break;
            } else {
                printf(_b("tracy: During fork(), parent returned first.")"\n");
                break;
            }
        }

        /* This is the new child */
        if (info.si_signo == SIGUSR1) {
            printf(_b("Handling SIGUSR1 from process %d, completing safe-fork")
                "\n", info.si_pid);

            /* If we're vforking the parent is now marked frozen */
            if (is_vforking) {
                c->frozen_by_vfork = 1;
                c->orig_trampy_pid_reg = orig_trampy_pid_reg;
                c->orig_pc = ip;
                c->orig_return_code = info.si_pid;
            }

            /* Attach to the new child */
            /*PTRACE_CHECK(PTRACE_ATTACH, info.si_pid, 0, 0, -1);*/
            child_pid = info.si_pid;

            /* Return PID to caller if they're interested. */
            /* XXX: Assignment to child_pid rarely happens, collapse this
             * line with th other somewhere useful.
             */
            if (new_child)
                *new_child = child_pid;
            break;
        }
    }

    /* The trampy register is now restored to the original value */
    args_ret.TRAMPY_PID_REG = orig_trampy_pid_reg;

    /* If we're vforking there is no point in resuming the parent because
     * it is frozen, unless the vfork failed.
     */
    if (!is_vforking || child_pid == -1) {
        waitpid(c->pid, &status, __WALL);

        PTRACE_CHECK(PTRACE_GETREGS, c->pid, 0, &args_ret, -1);

        /*
            printf("The IP is now %p\n", (void*)args_ret.TRACY_IP_REG);
            puts("POST");
        */

        /* FIXME: We don't check if the fork failed
         * which we really should since there is no point in
         * attaching to a failed fork.
         */
        child_pid = args_ret.TRACY_RETURN_CODE;

        /* Return PID to caller if they're interested. */
        if (new_child)
            *new_child = child_pid;

        printf("Fork return value: %d\n", child_pid);

        /* Now point the parent process after the original fork
         * syscall instruction.
         */
        args_ret.TRACY_IP_REG = ip;
        args_ret.TRACY_RETURN_CODE = child_pid;

        PTRACE_CHECK(PTRACE_SETREGS, c->pid, 0, &args_ret, -1);
        printf("Return code set to %d\n", child_pid);

        c->pre_syscall = 0;

        /* TODO Handle possible kill signal from child that might be waiting
         * in the singal set
         */

    } /* End of non-vfork block */

    /* Attach to the new child */
    printf("Attaching to %d...\n", child_pid);

    /* Ptrace guarantees PRE state (? XXX TODO FIXME)*/
    PTRACE_CHECK(PTRACE_ATTACH, child_pid, 0, 0, -1);
    if (waitpid(child_pid, &status, __WALL) == -1) {
        perror("Failure waiting for new child");
    }

/*
    if (ptrace(PTRACE_SETREGS, child_pid, 0, &args))
        perror("SETREGS");
*/

    /* Restore the new child to its original position */
    args.TRACY_IP_REG = ip;
    args.TRACY_RETURN_CODE = 0;

    /* Retrieve stack pointer first.
     * 
     * clone can modify the stack pointer, so the stack pointer
     * needs to be left untouched.
     */
    PTRACE_CHECK(PTRACE_GETREGS, child_pid, 0, &args_ret, -1);
    args.TRACY_STACK_POINTER = args_ret.TRACY_STACK_POINTER;

    /* Now update child registers */
    PTRACE_CHECK(PTRACE_SETREGS, child_pid, 0, &args, -1);

    /* Set enhanced syscall tracing */
    PTRACE_CHECK(PTRACE_SETOPTIONS, child_pid, 0, PTRACE_O_TRACESYSGOOD, -1);

    /* Continue the new child */

    /* PTRACE_CHECK(PTRACE_SYSCALL, child_pid, 0, 0, -1); */

    /* Poll for any remaining SIGUSR1 so this cannot kill us in the
     * original process signal mode.
     */
    timeout.tv_sec = 0;
    timeout.tv_nsec = 0;
    sigdelset(&set, SIGCHLD);
    sigtimedwait(&set, &info, &timeout);

    /* Restore the original signal handlers */
    sigaction(SIGUSR1, &old_sigusr1, NULL);
    sigaction(SIGCHLD, &old_sigchld, NULL);

    /* Restore signal mask settings */
    pthread_sigmask(SIG_SETMASK, &old_set, NULL);

    /* TODO: We should now munmap the pages in both the parent and the child.
     * Unless ofc. we created a thread which shares VM in which case we should
     * munmap only once.
     */

    return 0;
}