int debugger_syscall(debugger_state *debugger, pid_t child) { long arg1, arg2, arg3, arg4, arg5, result; int syscall, ret = 0; syscall = get_syscall(debugger->pid, &arg1, &arg2, &arg3, &arg4, &arg5); switch(syscall){ case __NR_execve: /* execve never returns */ debugger->handle_trace = debugger_syscall; break; case __NR_ptrace: if(debugger->debugee->pid != 0) arg2 = debugger->debugee->pid; if(!debugger->debugee->in_context) child = debugger->debugee->pid; result = proxy_ptrace(debugger, arg1, arg2, arg3, arg4, child, &ret); syscall_cancel(debugger->pid, result); debugger->handle_trace = debugger_syscall; return(ret); #ifdef __NR_waitpid case __NR_waitpid: #endif case __NR_wait4: if(!debugger_wait(debugger, (int *) arg2, arg3, debugger_syscall, debugger_normal_return, proxy_wait_return)) return(0); break; case __NR_kill: if(!debugger->debugee->in_context) child = debugger->debugee->pid; if(arg1 == debugger->debugee->pid){ result = kill(child, arg2); syscall_cancel(debugger->pid, result); debugger->handle_trace = debugger_syscall; return(0); } else debugger->handle_trace = debugger_normal_return; break; default: debugger->handle_trace = debugger_normal_return; } syscall_continue(debugger->pid); return(0); }
static void trace_process(int pid, char **strtab) { int status; while (1) { wait4(pid, &status, WUNTRACED, NULL); if (get_stopsig(pid, strtab)) break; if (status == 0) break; if (get_syscall(pid, strtab)) break ; ptrace(PTRACE_SINGLESTEP, pid, NULL, 0); } }
void powerpc64_syscall_entry(struct trussinfo *trussinfo, int nargs) { struct ptrace_io_desc iorequest; struct reg regs; struct freebsd_syscall *fsc; struct syscall *sc; void *args; lwpid_t tid; int i, regargs, syscall_num; tid = trussinfo->curthread->tid; if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); return; } /* * FreeBSD has two special kinds of system call redirctions -- * SYS_syscall, and SYS___syscall. The former is the old syscall() * routine, basically; the latter is for quad-aligned arguments. */ regargs = NARGREG; syscall_num = regs.fixreg[0]; args = ®s.fixreg[3]; if (syscall_num == SYS_syscall || syscall_num == SYS___syscall) { args = ®s.fixreg[4]; regargs -= 1; syscall_num = regs.fixreg[3]; } fsc = alloc_fsc(); if (fsc == NULL) return; fsc->number = syscall_num; fsc->name = (syscall_num < 0 || syscall_num >= nsyscalls) ? NULL : syscallnames[syscall_num]; if (!fsc->name) { fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d --\n", syscall_num); } if (fsc->name && (trussinfo->flags & FOLLOWFORKS) && (strcmp(fsc->name, "fork") == 0 || strcmp(fsc->name, "rfork") == 0 || strcmp(fsc->name, "vfork") == 0)) trussinfo->curthread->in_fork = 1; if (nargs == 0) return; fsc->args = malloc((1 + nargs) * sizeof(unsigned long)); if (nargs > regargs) { memmove(&fsc->args[0], args, regargs * sizeof(fsc->args[0])); iorequest.piod_op = PIOD_READ_D; iorequest.piod_offs = (void *)(regs.fixreg[1] + 48); iorequest.piod_addr = &fsc->args[regargs]; iorequest.piod_len = (nargs - regargs) * sizeof(fsc->args[0]); ptrace(PT_IO, tid, (caddr_t)&iorequest, 0); if (iorequest.piod_len == 0) return; } else memmove(&fsc->args[0], args, nargs * sizeof(fsc->args[0])); sc = get_syscall(fsc->name); if (sc) fsc->nargs = sc->nargs; else { #if DEBUG fprintf(trussinfo->outfile, "unknown syscall %s -- setting " "args to %d\n", fsc->name, nargs); #endif fsc->nargs = nargs; } fsc->s_args = calloc(1, (1 + fsc->nargs) * sizeof(char *)); fsc->sc = sc; /* * At this point, we set up the system call arguments. * We ignore any OUT ones, however -- those are arguments that * are set by the system call, and so are probably meaningless * now. This doesn't currently support arguments that are * passed in *and* out, however. */ if (fsc->name) { #if DEBUG fprintf(stderr, "syscall %s(", fsc->name); #endif for (i = 0; i < fsc->nargs; i++) { #if DEBUG fprintf(stderr, "0x%x%s", sc ? fsc->args[sc->args[i].offset] : fsc->args[i], i < (fsc->nargs - 1) ? "," : ""); #endif if (sc && !(sc->args[i].type & OUT)) { fsc->s_args[i] = print_arg(&sc->args[i], fsc->args, 0, trussinfo); } } #if DEBUG fprintf(stderr, ")\n"); #endif } #if DEBUG fprintf(trussinfo->outfile, "\n"); #endif if (fsc->name && (strcmp(fsc->name, "execve") == 0 || strcmp(fsc->name, "exit") == 0)) { /* * XXX * This could be done in a more general * manner but it still wouldn't be very pretty. */ if (strcmp(fsc->name, "execve") == 0) { if ((trussinfo->flags & EXECVEARGS) == 0) { if (fsc->s_args[1]) { free(fsc->s_args[1]); fsc->s_args[1] = NULL; } } if ((trussinfo->flags & EXECVEENVS) == 0) { if (fsc->s_args[2]) { free(fsc->s_args[2]); fsc->s_args[2] = NULL; } } } } trussinfo->curthread->fsc = fsc; }
/* * Function: main * Purpose: * Comments: */ int main(int argc, char *argv[]) { int i; struct sigaction si_sa; int euid, ruid; struct host_entry *cursor; /* * first things first * if anything goes wrong anywhere, we want to be super careful to release * any file descriptor we have on /dev/lkm, and it probably makes sense to * shutdown the raw socket we have too. */ atexit(cleanup); /* * get the effective and the real user id's so we know if we are running * with elevated permissions or not */ euid = geteuid(); ruid = getuid(); /* check for the command line arguments */ if(check_options(argc,argv) == 0) { return -1; } /* * open the raw socket now. i used to have this further down closer to * where it was needed, but because you can't change the euid/uid to and * from root (unless you're the superuser) i can only do this once */ if(options & OPT_IPV4) { if(open_ipmp_sockets4() == 0) return -1; } else if(options & OPT_IPV6) { if(open_ipmp_sockets6() == 0) return -1; } else { if(open_ipmp_sockets4() == 0) return -1; if(open_ipmp_sockets6() == 0) return -1; } /* * revoke the permissions we requested as we only need them to open a raw * socket. this is to reduce the impact of any buffer overflow exploits * that may be present */ if(ruid != euid) { setreuid(ruid, ruid); } /* * we get the pid so we can identify incoming ipmp packets destined for * this instance of ipmp_ping */ pid = getpid(); /* * need to know about the addresses this host has */ learn_localaddresses(); /* * in FreeBSD, the actual ping is done by a kernel module that has a syscall * in it. the kernel module allows the protocol to get a timestamp as close * to when the mbuf is actually sent to ip_output as possible */ #if defined(__FreeBSD__) if((options & OPT_RAW) == 0) { i = get_syscall("ipmp_ping", &syscall_num); if(i != 0) { printerror(i, strerror, "could not get the syscall for ipmp_ping"); return -1; } } #endif /* * the -n option means that the user has supplied a list of hosts to * ping, so we read those entries and put them in an list of hosts with * details regarding each host. the task of putting hosts into the list * is handled by read_hosts_file * * if the -n option isnt specified, we create a list containing just the * one host to ping. this way, all the program logic can be used in a * multitude of situations. */ if(options & OPT_NLANR) { /* * if something went wrong parsing the file, we quit. */ if(read_hosts_file() == 0) { return -1; } } else { /* * if the user didnt specify a host to ping, we bail, telling them * why first... */ if(argc - optind != 1) { usage(0); return -1; } /* * if we can't add a host entry for the host supplied on the command line * tell the user that it couldnt be parse and cleanup */ if(add_host_entry(argv[optind], NULL) == 0) { return -1; } } /* * we now put some handlers into action so if the user ctrl-c's us we have * the opportunity to tell them what we found out first * also, if the user specified a timeout, put an alarm in for that so we * can bail when they tell us to... */ sigemptyset(&si_sa.sa_mask); si_sa.sa_flags = 0; si_sa.sa_handler = alarm_bells; if(sigaction(SIGINT, &si_sa, 0) == -1) { printerror(errno, strerror, "could not set sigaction for SIGINT"); return -1; } if(options & OPT_TIMEOUT) { if(sigaction(SIGALRM, &si_sa, 0) == -1) { printerror(errno, strerror, "could not set sigaction for SIGALRM"); return -1; } } /* * we loop for as long as we have not been told to finish up. * the finish_up loop will exit when * - there has been an alarm set that goes off * - a SIGINT is received (from e.g. Ctrl-C) * - we have got_all_response()'s * * this is not an expensive loop in terms of cpu cycles, as the * recv_echo_response will sleep if there is nothing to recv until we have * told it to stop blocking - typically one second or whatever the between * timeout is. * * the loop does two things: * - sends echo requests * - receives echo responses * * the loop sends packets, pausing for however long the timeout is set for * between packets. this pause is implemented in the recv_echo_response * function in a call to select(2). if we cannot send a request to one of * them, we bail, as this probably means the syscall could not be called. * * the loop recv's the response and associates the packet with a host_entry * we then parse that response for the host entry, and then check if we have * now got all the responses we are looking for. if we have, we exit the * loop by setting the finish_up flag */ sent_all_requests = 0; i = 0; cursor = head; while(finish_up == 0) { while(sent_all_requests == 0) { if(send_echo_request(cursor) != 0) { return -1; } cursor->tx++; if(cursor->tx == count) { cursor = cursor->next; if(cursor == NULL) { sent_all_requests = 1; alarm(timeout); break; } } if(wait_between > 0) { gettimeofday(&wait_between_tv, &tz); timeval_add(&wait_between_tv, wait_between); break; } } while(time_to_send_request() == 0 && finish_up == 0) { if(recv_echo_responses() > 0) { if(got_all_responses() == 1) { finish_up = 1; } } } } /* * if we have been given a list of hosts to ping, we have to print out which * hosts did not give us a reply */ if(options & OPT_NLANR) { show_loss(); } return 0; }
void mips_syscall_entry(struct trussinfo *trussinfo, int nargs) { struct ptrace_io_desc iorequest; struct reg regs; struct freebsd_syscall *fsc; struct syscall *sc; lwpid_t tid; int i, syscall_num; int indir; /* indirect system call */ tid = trussinfo->curthread->tid; if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); return; } indir = 0; syscall_num = regs.r_regs[V0]; if (syscall_num == SYS_syscall) { indir = 1; syscall_num = regs.r_regs[A0]; } fsc = alloc_fsc(); if (fsc == NULL) return; fsc->number = syscall_num; fsc->name = (syscall_num < 0 || syscall_num >= nsyscalls) ? NULL : syscallnames[syscall_num]; if (!fsc->name) { fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d --\n", syscall_num); } if (fsc->name && (trussinfo->flags & FOLLOWFORKS) && (strcmp(fsc->name, "fork") == 0 || strcmp(fsc->name, "pdfork") == 0 || strcmp(fsc->name, "rfork") == 0 || strcmp(fsc->name, "vfork") == 0)) trussinfo->curthread->in_fork = 1; if (nargs == 0) return; fsc->args = malloc((1 + nargs) * sizeof(unsigned long)); #if 0 // XXX iorequest.piod_op = PIOD_READ_D; iorequest.piod_offs = (void *)parm_offset; iorequest.piod_addr = fsc->args; iorequest.piod_len = (1 + nargs) * sizeof(unsigned long); ptrace(PT_IO, tid, (caddr_t)&iorequest, 0); if (iorequest.piod_len == 0) return; #else iorequest.piod_op = PIOD_READ_D; #endif switch (nargs) { default: /* * The OS doesn't seem to allow more than 10 words of * parameters (yay!). So we shouldn't be here. */ warn("More than 10 words (%d) of arguments!\n", nargs); break; case 10: case 9: case 8: case 7: case 6: case 5: /* * If there are 7-10 words of arguments, they are placed * on the stack, as is normal for other processors. * The fall-through for all of these is deliberate!!! */ // XXX BAD constant used here iorequest.piod_op = PIOD_READ_D; iorequest.piod_offs = (void *)(regs.r_regs[SP] + 4 * sizeof(uint32_t)); iorequest.piod_addr = &fsc->args[4]; iorequest.piod_len = (nargs - 4) * sizeof(fsc->args[0]); ptrace(PT_IO, tid, (caddr_t)&iorequest, 0); if (iorequest.piod_len == 0) return; case 4: fsc->args[3] = regs.r_regs[A3]; case 3: fsc->args[2] = regs.r_regs[A2]; case 2: fsc->args[1] = regs.r_regs[A1]; case 1: fsc->args[0] = regs.r_regs[A0]; case 0: break; } if (indir) { memmove(&fsc->args[0], &fsc->args[1], (nargs - 1) * sizeof(fsc->args[0])); } sc = get_syscall(fsc->name); if (sc) fsc->nargs = sc->nargs; else { #if DEBUG fprintf(trussinfo->outfile, "unknown syscall %s -- setting " "args to %d\n", fsc->name, nargs); #endif fsc->nargs = nargs; } fsc->s_args = calloc(1, (1 + fsc->nargs) * sizeof(char *)); fsc->sc = sc; /* * At this point, we set up the system call arguments. * We ignore any OUT ones, however -- those are arguments that * are set by the system call, and so are probably meaningless * now. This doesn't currently support arguments that are * passed in *and* out, however. */ if (fsc->name) { #if DEBUG fprintf(stderr, "syscall %s(", fsc->name); #endif for (i = 0; i < fsc->nargs; i++) { #if DEBUG fprintf(stderr, "0x%x%s", sc ? fsc->args[sc->args[i].offset] : fsc->args[i], i < (fsc->nargs - 1) ? "," : ""); #endif if (sc && !(sc->args[i].type & OUT)) { fsc->s_args[i] = print_arg(&sc->args[i], fsc->args, 0, trussinfo); } } #if DEBUG fprintf(stderr, ")\n"); #endif } #if DEBUG fprintf(trussinfo->outfile, "\n"); #endif trussinfo->curthread->fsc = fsc; }
void arm_syscall_entry(struct trussinfo *trussinfo, int nargs) { struct ptrace_io_desc iorequest; struct reg regs; struct freebsd_syscall *fsc; struct syscall *sc; lwpid_t tid; int i, syscall_num; register_t *ap; tid = trussinfo->curthread->tid; if (ptrace(PT_GETREGS, tid, (caddr_t)®s, 0) < 0) { fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); return; } ap = ®s.r[0]; /* * FreeBSD has two special kinds of system call redirctions -- * SYS_syscall, and SYS___syscall. The former is the old syscall() * routine, basically; the latter is for quad-aligned arguments. */ #ifdef __ARM_EABI__ syscall_num = regs.r[7]; #else if ((syscall_num = ptrace(PT_READ_I, tid, (caddr_t)(regs.r[_REG_PC] - INSN_SIZE), 0)) == -1) { fprintf(trussinfo->outfile, "-- CANNOT READ PC --\n"); return; } syscall_num = syscall_num & 0x000fffff; #endif switch (syscall_num) { case SYS_syscall: syscall_num = *ap++; nargs--; break; case SYS___syscall: syscall_num = ap[_QUAD_LOWWORD]; ap += 2; nargs -= 2; break; } fsc = alloc_fsc(); if (fsc == NULL) return; fsc->number = syscall_num; fsc->name = (syscall_num < 0 || syscall_num >= nsyscalls) ? NULL : syscallnames[syscall_num]; if (!fsc->name) { fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d --\n", syscall_num); } if (fsc->name && (trussinfo->flags & FOLLOWFORKS) && (strcmp(fsc->name, "fork") == 0 || strcmp(fsc->name, "rfork") == 0 || strcmp(fsc->name, "vfork") == 0)) trussinfo->curthread->in_fork = 1; if (nargs == 0) return; fsc->args = malloc((1 + nargs) * sizeof(unsigned long)); switch (nargs) { default: /* * The OS doesn't seem to allow more than 10 words of * parameters (yay!). So we shouldn't be here. */ warn("More than 10 words (%d) of arguments!\n", nargs); break; case 10: case 9: case 8: case 7: case 6: case 5: /* * If there are 7-10 words of arguments, they are placed * on the stack, as is normal for other processors. * The fall-through for all of these is deliberate!!! */ // XXX BAD constant used here iorequest.piod_op = PIOD_READ_D; iorequest.piod_offs = (void *)(regs.r[_REG_SP] + 4 * sizeof(uint32_t)); iorequest.piod_addr = &fsc->args[4]; iorequest.piod_len = (nargs - 4) * sizeof(fsc->args[0]); ptrace(PT_IO, tid, (caddr_t)&iorequest, 0); if (iorequest.piod_len == 0) return; case 4: fsc->args[3] = ap[3]; case 3: fsc->args[2] = ap[2]; case 2: fsc->args[1] = ap[1]; case 1: fsc->args[0] = ap[0]; case 0: break; } sc = NULL; if (fsc->name) sc = get_syscall(fsc->name); if (sc) fsc->nargs = sc->nargs; else { #if DEBUG fprintf(trussinfo->outfile, "unknown syscall %s -- setting " "args to %d\n", fsc->name, nargs); #endif fsc->nargs = nargs; } fsc->s_args = calloc(1, (1 + fsc->nargs) * sizeof(char *)); fsc->sc = sc; /* * At this point, we set up the system call arguments. * We ignore any OUT ones, however -- those are arguments that * are set by the system call, and so are probably meaningless * now. This doesn't currently support arguments that are * passed in *and* out, however. */ if (fsc->name) { #if DEBUG fprintf(stderr, "syscall %s(", fsc->name); #endif for (i = 0; i < fsc->nargs; i++) { #if DEBUG fprintf(stderr, "0x%x%s", sc ? fsc->args[sc->args[i].offset] : fsc->args[i], i < (fsc->nargs - 1) ? "," : ""); #endif if (sc && !(sc->args[i].type & OUT)) { fsc->s_args[i] = print_arg(&sc->args[i], fsc->args, 0, trussinfo); } } #if DEBUG fprintf(stderr, ")\n"); #endif } #if DEBUG fprintf(trussinfo->outfile, "\n"); #endif if (fsc->name != NULL && (strcmp(fsc->name, "execve") == 0 || strcmp(fsc->name, "exit") == 0)) { /* * XXX * This could be done in a more general * manner but it still wouldn't be very pretty. */ if (strcmp(fsc->name, "execve") == 0) { if ((trussinfo->flags & EXECVEARGS) == 0) { if (fsc->s_args[1]) { free(fsc->s_args[1]); fsc->s_args[1] = NULL; } } if ((trussinfo->flags & EXECVEENVS) == 0) { if (fsc->s_args[2]) { free(fsc->s_args[2]); fsc->s_args[2] = NULL; } } } } trussinfo->curthread->fsc = fsc; }
void amd64_linux32_syscall_entry(struct trussinfo *trussinfo, int nargs) { struct reg regs; int syscall_num; int i; struct syscall *sc; cpid = trussinfo->curthread->tid; clear_fsc(); if (ptrace(PT_GETREGS, cpid, (caddr_t)®s, 0) < 0) { fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); return; } syscall_num = regs.r_rax; fsc.number = syscall_num; fsc.name = (syscall_num < 0 || syscall_num >= nsyscalls) ? NULL : linux32_syscallnames[syscall_num]; if (!fsc.name) { fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d --\n", syscall_num); } if (fsc.name && (trussinfo->flags & FOLLOWFORKS) && ((!strcmp(fsc.name, "linux_fork") || !strcmp(fsc.name, "linux_vfork")))) { trussinfo->curthread->in_fork = 1; } if (nargs == 0) return; /* * Linux passes syscall arguments in registers, not * on the stack. Fortunately, we've got access to the * register set. Note that we don't bother checking the * number of arguments. And what does linux do for syscalls * that have more than five arguments? */ fsc.args[0] = regs.r_rbx; fsc.args[1] = regs.r_rcx; fsc.args[2] = regs.r_rdx; fsc.args[3] = regs.r_rsi; fsc.args[4] = regs.r_rdi; sc = get_syscall(fsc.name); if (sc) { fsc.nargs = sc->nargs; } else { #if DEBUG fprintf(trussinfo->outfile, "unknown syscall %s -- setting args to %d\n", fsc.name, nargs); #endif fsc.nargs = nargs; } fsc.s_args = calloc(1, (1+fsc.nargs) * sizeof(char*)); fsc.sc = sc; /* * At this point, we set up the system call arguments. * We ignore any OUT ones, however -- those are arguments that * are set by the system call, and so are probably meaningless * now. This doesn't currently support arguments that are * passed in *and* out, however. */ if (fsc.name) { #if DEBUG fprintf(stderr, "syscall %s(", fsc.name); #endif for (i = 0; i < fsc.nargs; i++) { #if DEBUG fprintf(stderr, "0x%x%s", sc ? fsc.args[sc->args[i].offset] : fsc.args[i], i < (fsc.nargs - 1) ? "," : ""); #endif if (sc && !(sc->args[i].type & OUT)) { fsc.s_args[i] = print_arg(&sc->args[i], fsc.args, 0, trussinfo); } } #if DEBUG fprintf(stderr, ")\n"); #endif } #if DEBUG fprintf(trussinfo->outfile, "\n"); #endif if (fsc.name != NULL && (!strcmp(fsc.name, "linux_execve") || !strcmp(fsc.name, "exit"))) { /* XXX * This could be done in a more general * manner but it still wouldn't be very pretty. */ if (!strcmp(fsc.name, "linux_execve")) { if ((trussinfo->flags & EXECVEARGS) == 0) if (fsc.s_args[1]) { free(fsc.s_args[1]); fsc.s_args[1] = NULL; } if ((trussinfo->flags & EXECVEENVS) == 0) if (fsc.s_args[2]) { free(fsc.s_args[2]); fsc.s_args[2] = NULL; } } } return; }
/* * Run a command passed as an argument, * stop when execle + dynamic loading is done * record mapping to further study ASLR */ int run_aslr_tests(int argc, char *argv, char **envp) { int status,s; pid_t child; siginfo_t si; int nmaps = 0; memset(&si, 0, sizeof(siginfo_t)); /* * We run the given command, wait for the * mapping to be done, and read the map */ if ((child = fork()) < 0) { perror("fork:"); exit(-1); } else if (!child) { // child FILE *f=fopen("/dev/zero","r"); // just in case it starts with a read... stdin=f; ptrace(PTRACE_TRACEME, 0, 0, 0); // drop privileges (if any) // setgid(gid); // setuid(uid); execle((char*)argv, (char*)argv, NULL,envp); perror("execle:"); exit(-1); } while (1) { // Wait for an event while (waitpid(child,&status,0) < 0) { if (errno == ECHILD) { printf(" [!!] Child exited\n"); exit(0); } else if (errno == EINTR) { continue; } else { perror("wait:"); exit(-1); } } s=get_syscall(child); //printf("syscall: %d\n",s); if(still_loading(s) || s == -1){ ptrace(PTRACE_SYSCALL, child, 0, 0); // } else if (s == -1){ // usleep(200); // ptrace(PTRACE_SYSCALL, child, 0, 0); // goto done_tracing; }else{ break; } } // read mapping nmaps = read_maps(child); if (debug_flag) printf("Number of maps read for aslr test: %d\n", nmaps); //kill child kill_pid(child); waitpid(child,&status,0); return 0; }
void i386_syscall_entry(struct trussinfo *trussinfo, int nargs) { struct reg regs; int syscall_num; int i; unsigned int parm_offset; struct syscall *sc = NULL; struct ptrace_io_desc iorequest; cpid = trussinfo->curthread->tid; clear_fsc(); if (ptrace(PT_GETREGS, cpid, (caddr_t)®s, 0) < 0) { fprintf(trussinfo->outfile, "-- CANNOT READ REGISTERS --\n"); return; } parm_offset = regs.r_esp + sizeof(int); /* * FreeBSD has two special kinds of system call redirctions -- * SYS_syscall, and SYS___syscall. The former is the old syscall() * routine, basically; the latter is for quad-aligned arguments. */ syscall_num = regs.r_eax; switch (syscall_num) { case SYS_syscall: syscall_num = ptrace(PT_READ_D, cpid, (caddr_t)parm_offset, 0); parm_offset += sizeof(int); break; case SYS___syscall: syscall_num = ptrace(PT_READ_D, cpid, (caddr_t)parm_offset, 0); parm_offset += sizeof(quad_t); break; } fsc.number = syscall_num; fsc.name = (syscall_num < 0 || syscall_num >= nsyscalls) ? NULL : syscallnames[syscall_num]; if (!fsc.name) { fprintf(trussinfo->outfile, "-- UNKNOWN SYSCALL %d --\n", syscall_num); } if (fsc.name && (trussinfo->flags & FOLLOWFORKS) && ((!strcmp(fsc.name, "fork") || !strcmp(fsc.name, "rfork") || !strcmp(fsc.name, "vfork")))) { trussinfo->curthread->in_fork = 1; } if (nargs == 0) return; fsc.args = malloc((1+nargs) * sizeof(unsigned long)); iorequest.piod_op = PIOD_READ_D; iorequest.piod_offs = (void *)parm_offset; iorequest.piod_addr = fsc.args; iorequest.piod_len = (1+nargs) * sizeof(unsigned long); ptrace(PT_IO, cpid, (caddr_t)&iorequest, 0); if (iorequest.piod_len == 0) return; if (fsc.name) sc = get_syscall(fsc.name); if (sc) { fsc.nargs = sc->nargs; } else { #if DEBUG fprintf(trussinfo->outfile, "unknown syscall %s -- setting args to %d\n", fsc.name, nargs); #endif fsc.nargs = nargs; } fsc.s_args = calloc(1, (1+fsc.nargs) * sizeof(char*)); fsc.sc = sc; /* * At this point, we set up the system call arguments. * We ignore any OUT ones, however -- those are arguments that * are set by the system call, and so are probably meaningless * now. This doesn't currently support arguments that are * passed in *and* out, however. */ if (fsc.name) { #if DEBUG fprintf(stderr, "syscall %s(", fsc.name); #endif for (i = 0; i < fsc.nargs; i++) { #if DEBUG fprintf(stderr, "0x%x%s", sc ? fsc.args[sc->args[i].offset] : fsc.args[i], i < (fsc.nargs - 1) ? "," : ""); #endif if (sc && !(sc->args[i].type & OUT)) { fsc.s_args[i] = print_arg(&sc->args[i], fsc.args, 0, trussinfo); } } #if DEBUG fprintf(stderr, ")\n"); #endif } #if DEBUG fprintf(trussinfo->outfile, "\n"); #endif if (fsc.name != NULL && (!strcmp(fsc.name, "execve") || !strcmp(fsc.name, "exit"))) { /* XXX * This could be done in a more general * manner but it still wouldn't be very pretty. */ if (!strcmp(fsc.name, "execve")) { if ((trussinfo->flags & EXECVEARGS) == 0) if (fsc.s_args[1]) { free(fsc.s_args[1]); fsc.s_args[1] = NULL; } if ((trussinfo->flags & EXECVEENVS) == 0) if (fsc.s_args[2]) { free(fsc.s_args[2]); fsc.s_args[2] = NULL; } } } return; }
void watch(pid_t pid,enum judge_status &judge_status,long &max_used_memory,long &used_time,long error_size) { long init_used_time = used_time; long used_memory; init_syscall_limit(); int status; struct rusage ru; while(1) { pid_t pid_ret = wait4(pid,&status,0,&ru); if(pid_ret != pid) error_report("wait4"); //TLE used_time = init_used_time + ru.ru_utime.tv_sec * 100 + ru.ru_utime.tv_usec / 1000; if(used_time >= time_limit * 1000) { judge_status = JUDGE_TLE; goto send_kill; } //MLE if (language == LANG_JAVA) { // JVM GC ask VM before need, so used kernel page fault times and page size. used_memory = get_page_fault_memory(ru, pid); } else { // other use VmPeak used_memory = get_proc_status(pid, "VmPeak:") << 10; } if(used_memory > max_used_memory) max_used_memory = used_memory; if(max_used_memory > memory_limit) { judge_status = JUDGE_MLE; goto send_kill; } //RE 判断stderr if(get_file_size(code_error_path) > error_size) { judge_status = JUDGE_RE; goto send_kill; } //进程退出 if(WIFEXITED(status)) { int exitcode = WEXITSTATUS(status); if(exitcode != 0) { judge_status = JUDGE_RE; } break; } //未捕捉到信号而中止 if(WIFSIGNALED(status)) { int sigcode = WTERMSIG(status); break; } // 未捕捉信号而暂停 // 子进程被追踪(ptrace)或调用WUN-TRACED时才可能发生 if (WIFSTOPPED(status)) { int sigcode = WSTOPSIG(status); switch (sigcode) { case SIGSTOP: // JAVA 使用 SIGSTOP 等待下次 CPU 运行 if (language != LANG_JAVA) { judge_status = JUDGE_RE; goto send_kill; } case SIGTRAP: // c++ case 语句中不能出现带初始化的变量声明 unsigned long syscall; syscall = get_syscall(pid); //printf("System call: %lu\n", syscall); switch (check_syscall(syscall)) { case 0: judge_status = JUDGE_RE; goto send_kill; case 1: ptrace(PTRACE_SYSCALL, pid, NULL, NULL); break; case 2: //ptrace(PTRACE_SETREGS, pid, NULL, NULL); break; } break; case SIGSEGV: judge_status = JUDGE_MLE; goto send_kill; case SIGCHLD: case SIGALRM: alarm(0); case SIGXCPU: case SIGKILL: judge_status = JUDGE_TLE; goto send_kill; case SIGXFSZ: judge_status = JUDGE_OLE; goto send_kill; default: judge_status = JUDGE_SE; goto send_kill; } } } return; send_kill: ptrace(PTRACE_KILL,pid,NULL,NULL); }
/* * For each tracefile, we first create in-memory structures, then once * we've processed each tracefile completely, we write out the in-memory * structures and free them. */ int main(int argc, char **argv) { FILE *fp; char path[512]; char syscall[512]; char lseek_action_str[512]; char *s; char open_flags_str[64]; void *db_node; int num_io_operations = 0; struct ioshark_header header; struct ioshark_file_operation *disk_file_op; struct in_mem_file_op *in_mem_fop; struct stat st; char *infile, *outfile; struct timeval prev_time; char trace_type[64]; progname = argv[0]; if (argc != 3) { usage(); exit(EXIT_FAILURE); } infile = argv[1]; outfile = argv[2]; if (stat(infile, &st) < 0) { fprintf(stderr, "%s Can't stat %s\n", progname, infile); exit(EXIT_FAILURE); } if (st.st_size == 0) { fprintf(stderr, "%s Empty file %s\n", progname, infile); exit(EXIT_FAILURE); } init_prev_time(&prev_time); init_filename_cache(); fp = fopen(infile, "r"); if (fp == NULL) { fprintf(stderr, "%s Can't open %s\n", progname, infile); exit(EXIT_FAILURE); } while (fgets(in_buf, 2048, fp)) { s = in_buf; while (isspace(*s)) s++; in_mem_fop = malloc(sizeof(struct in_mem_file_op)); disk_file_op = &in_mem_fop->disk_file_op; disk_file_op->delta_us = get_delta_ts(s, &prev_time); get_tracetype(s, trace_type); if (strcmp(trace_type, "strace") == 0) { get_syscall(s, syscall); disk_file_op->file_op = map_syscall(syscall); } else disk_file_op->file_op = map_syscall("ftrace"); get_pathname(s, path, disk_file_op->file_op); db_node = files_db_add(path); disk_file_op->fileno = files_db_get_fileno(db_node); switch (disk_file_op->file_op) { case IOSHARK_LLSEEK: case IOSHARK_LSEEK: get_lseek_offset_action(s, disk_file_op->file_op, &disk_file_op->lseek_offset, lseek_action_str); disk_file_op->lseek_action = map_lseek_action(lseek_action_str); if (disk_file_op->lseek_action == SEEK_SET) files_db_update_size(db_node, disk_file_op->lseek_offset); break; case IOSHARK_PREAD64: case IOSHARK_PWRITE64: get_prw64_offset_len(s, &disk_file_op->prw_offset, (u_int64_t *)&disk_file_op->prw_len); files_db_update_size(db_node, disk_file_op->prw_offset + disk_file_op->prw_len); break; case IOSHARK_READ: case IOSHARK_WRITE: get_rw_len(s, (u_int64_t *)&disk_file_op->rw_len); files_db_add_to_size(db_node, disk_file_op->rw_len); break; case IOSHARK_MMAP: case IOSHARK_MMAP2: get_mmap_offset_len_prot(s, &disk_file_op->mmap_prot, &disk_file_op->mmap_offset, (u_int64_t *)&disk_file_op->mmap_len); files_db_update_size(db_node, disk_file_op->mmap_offset + disk_file_op->mmap_len); break; case IOSHARK_OPEN: disk_file_op->open_mode = 0; get_openat_flags_mode(s, open_flags_str, &disk_file_op->open_mode); disk_file_op->open_flags = map_open_flags(open_flags_str); break; case IOSHARK_FSYNC: case IOSHARK_FDATASYNC: break; case IOSHARK_CLOSE: break; case IOSHARK_MAPPED_PREAD: /* Convert a mmap'ed read into a PREAD64 */ disk_file_op->file_op = IOSHARK_PREAD64; get_ftrace_offset_len(s, &disk_file_op->prw_offset, (u_int64_t *)&disk_file_op->prw_len); files_db_update_size(db_node, disk_file_op->prw_offset + disk_file_op->prw_len); break; default: break; } /* Chain at the end */ num_io_operations++; in_mem_fop->next = NULL; if (in_mem_file_op_head == NULL) { in_mem_file_op_tail = in_mem_fop; in_mem_file_op_head = in_mem_fop; } else { in_mem_file_op_tail->next = in_mem_fop; in_mem_file_op_tail = in_mem_fop; } } fclose(fp); /* * Now we can write everything out to the output tracefile. */ fp = fopen(outfile, "w+"); if (fp == NULL) { fprintf(stderr, "%s Can't open trace.outfile\n", progname); exit(EXIT_FAILURE); } header.num_io_operations = num_io_operations; header.num_files = files_db_get_total_obj(); if (fwrite(&header, sizeof(struct ioshark_header), 1, fp) != 1) { fprintf(stderr, "%s Write error trace.outfile\n", progname); exit(EXIT_FAILURE); } files_db_write_objects(fp); while (in_mem_file_op_head != NULL) { struct in_mem_file_op *temp; disk_file_op = &in_mem_file_op_head->disk_file_op; if (fwrite(disk_file_op, sizeof(struct ioshark_file_operation), 1, fp) != 1) { fprintf(stderr, "%s Write error trace.outfile\n", progname); exit(EXIT_FAILURE); } temp = in_mem_file_op_head; in_mem_file_op_head = in_mem_file_op_head->next; free(temp); } store_filename_cache(); }