Academic Integrity: tutoring, explanations, and feedback — we don’t complete graded work or submit on a student’s behalf.

Add a Static System Call to the Linux Kernel Write a new static system call to i

ID: 3902719 • Letter: A

Question

Add a Static System Call to the Linux Kernel

Write a new static system call to incorporate to your custom Linux Kernel. The system call you write should take one argument and return the process state information for that process, its child processes, child threads and sibling processes. The prototype for your system call will be:

asmlinkage long sys_pinfo(struct pinfo *info);

pid_t is defined in /usr/include/sys/types.h and /usr/include/linux/types.h for user-space programs and in /usr/src/linux/include/linux/types.h (/usr/src/linux/include/uapi/asm-generic/posix_types.h) for kernel-space routines.

Also see /usr/include/asm-generic/posix_types.h



Define struct pinfo in /usr/src/linux/include/linux/pinfo.h as part of your solution (localize it for your test C program):

#include <linux/types.h>

struct pinfo {

pid_t pid;                 /* process id */

long state;                /* current state of process */

long nice;                 /* process nice value */

pid_t parent_pid;          /* process id of parent */

int nr_children;           /* total number of child processes */

int nr_threads;            /* total number of child threads */

pid_t youngest_child_pid;  /* pid of youngest child */

pid_t younger_sibling_pid; /* pid of younger sibling */

pid_t older_sibling_pid;   /* pid of older sibling */

unsigned long start_time;  /* process start time */

long user_time;            /* CPU time spent in user mode */

long sys_time;             /* CPU time spent in system mode */

long cutime;               /* total user time of children */
long cstime;               /* total system time of children */

long uid;                  /* user id of process owner */

char comm[16];             /* name of program executed */

};

Sibling processes are those sharing the same parent. Younger, youngest and older comparisons between processes are made based on their start time.

Your system call should return 0 unless an error occurs. Your code should handle errors that can occur but not handle any errors that cannot occur. At a minimum, your system call should detect the following conditions and respond as described:

If the address for the pinfo structure is null, return -22.

If a value to be set in pinfo is accessible through a pointer which is null, set the corresponding field value in pinfo to -1. For example, the youngest_child_pid should be set to -1 if the process does not have a child. The referenced error code is defined as EINVAL in /usr/src/linux/include/uapi/asm-generic/errno-base.h
/usr/src/linux/include/linux/errno.h
/usr/src/linux/usr/include/asm/errno.h.

Each system call must be assigned a number which becomes its position in the system call vector. You must identify an available spot for your new system call in the vector for x86_64 system calls (see /usr/src/linux/arch/x86/syscalls/syscall_64.tbl and /usr/src/linux/include/uapi/asm-generic/unistd.h)

Linux maintains a list of all processes in a doubly linked list. Each entry in this list is a task_struct structure, which is defined in /usr/src/linux/include/linux/sched.h.

In /usr/src/linux/include/asm-generic/current.h, “current” is defined to inline a function which returns the address of the task_struct of the currently running process. All of the information to be returned in the pinfostructure can be determined by starting with “current”.  Note that current.h includes  /usr/src/linux/include/linux/thread_info.h

See /usr/src/linux/arch/x86/include/asm/thread_info.h

Explanation / Answer

#include <sys/types.h>

#include <signal.h>

#include <errno.h>

#include <sys/param.h>

#include <fcntl.h>

#include <sys/resource.h>

#include <sys/wait.h>

#include <sys/stat.h>

#include <pwd.h>

#include <grp.h>

#include <string.h>

#include <limits.h>

#include <dirent.h>

#ifdef LINUX

# include <asm/unistd.h>

# if defined __NR_tgkill

# define my_tgkill(pid, tid, sig) syscall (__NR_tgkill, (pid), (tid), (sig))

# elif defined __NR_tkill

# define my_tgkill(pid, tid, sig) syscall (__NR_tkill, (tid), (sig))

# else

/* kill() may choose arbitrarily the target task of the process group

while we later wait on a that specific TID. PID process waits become

TID task specific waits for a process under ptrace(2). */

# warning "Neither tkill(2) nor tgkill(2) available, risk of strace hangs!"

# define my_tgkill(pid, tid, sig) kill ((tid), (sig))

# endif

#endif

#if defined(IA64) && defined(LINUX)

# include <asm/ptrace_offsets.h>

#endif

#ifdef USE_PROCFS

#include <poll.h>

#endif

#ifdef SVR4

#include <sys/stropts.h>

#ifdef HAVE_MP_PROCFS

#ifdef HAVE_SYS_UIO_H

#include <sys/uio.h>

#endif

#endif

#endif

extern char **environ;

extern int optind;

extern char *optarg;

// pgbovine

#include "okapi.h"

extern char CDE_exec_mode;

extern char CDE_verbose_mode; // -v option

extern char CDE_exec_streaming_mode; // -s option

extern char CDE_block_net_access; // -n option

extern char CDE_use_linker_from_package; // ON by default, -l option to turn OFF

extern void alloc_tcb_CDE_fields(struct tcb* tcp);

extern void free_tcb_CDE_fields(struct tcb* tcp);

extern void strcpy_redirected_cderoot(char* dst, char* src);

extern void CDE_init_tcb_dir_fields(struct tcb* tcp);

extern FILE* CDE_copied_files_logfile;

extern char* CDE_PACKAGE_DIR;

extern void CDE_clear_options_arrays();

extern void CDE_add_ignore_exact_path(char* p);

extern void CDE_add_ignore_prefix_path(char* p);

int debug = 0, followfork = 1; // pgbovine - turn on followfork by default, can de-activate using the '-f' option

unsigned int ptrace_setoptions = 0;

int dtime = 0, xflag = 0, qflag = 1; // pgbovine - turn on quiet mode (-q) by

// default to shut up terminal line noise

cflag_t cflag = CFLAG_NONE;

static int iflag = 0, interactive = 0, pflag_seen = 0, rflag = 0, tflag = 0;

/*

* daemonized_tracer supports -D option.

* With this option, strace forks twice.

* Unlike normal case, with -D *grandparent* process exec's,

* becoming a traced process. Child exits (this prevents traced process

* from having children it doesn't expect to have), and grandchild

* attaches to grandparent similarly to strace -p PID.

* This allows for more transparent interaction in cases

* when process and its parent are communicating via signals,

* wait() etc. Without -D, strace process gets lodged in between,

* disrupting parent<->child link.

*/

static bool daemonized_tracer = 0;

/* Sometimes we want to print only succeeding syscalls. */

int not_failing_only = 0;

static int exit_code = 0;

static int strace_child = 0;

static char *username = NULL;

uid_t run_uid;

gid_t run_gid;

int acolumn = DEFAULT_ACOLUMN;

int max_strlen = DEFAULT_STRLEN;

static char *outfname = NULL;

FILE *outf;

static int curcol;

struct tcb **tcbtab;

unsigned int nprocs, tcbtabsize;

const char *progname;

extern char **environ;

static int detach(struct tcb *tcp, int sig);

static int trace(void);

static void cleanup(void);

static void interrupt(int sig);

static sigset_t empty_set, blocked_set;

#ifdef HAVE_SIG_ATOMIC_T

static volatile sig_atomic_t interrupted;

#else /* !HAVE_SIG_ATOMIC_T */

static volatile int interrupted;

#endif /* !HAVE_SIG_ATOMIC_T */

#ifdef USE_PROCFS

static struct tcb *pfd2tcb(int pfd);

static void reaper(int sig);

static void rebuild_pollv(void);

static struct pollfd *pollv;

#ifndef HAVE_POLLABLE_PROCFS

static void proc_poll_open(void);

static void proc_poller(int pfd);

struct proc_pollfd {

int fd;

int revents;

int pid;

};

static int poller_pid;

static int proc_poll_pipe[2] = { -1, -1 };

#endif /* !HAVE_POLLABLE_PROCFS */

#ifdef HAVE_MP_PROCFS

#define POLLWANT POLLWRNORM

#else

#define POLLWANT POLLPRI

#endif

#endif /* USE_PROCFS */

static void

usage(ofp, exitval)

FILE *ofp;

int exitval;

{

if (CDE_exec_mode) {

fprintf(ofp,

"CDE: Code, Data, and Environment packaging for Linux "

"Copyright 2010-2012 Philip Guo (philip@pgbovine.net) "

"Full user manual: http://www.pgbovine.net/cde.html "

"Basic usage: cde-exec [command within cde-root/ to run] ");

fprintf(ofp, " Options ");

fprintf(ofp, " -l : Use native dynamic linker on machine (less portable but more robust) ");

fprintf(ofp, " -n : Block network access (blank out bind/connect syscalls) ");

fprintf(ofp, " -f : Do NOT follow forks, so child processes run natively ");

fprintf(ofp, " -s : Streaming mode (ooh, mysterious!) ");

fprintf(ofp, " -i '<file path>' : Ignore the given exact file path ");

fprintf(ofp, " -p '<file path>' : Ignore the given file path prefix ");

fprintf(ofp, " -v : Verbose mode (for debugging) ");

fprintf(ofp, " -V : Print version ");

}

else {

fprintf(ofp,

"CDE: Code, Data, and Environment packaging for Linux "

"Copyright 2010-2012 Philip Guo (philip@pgbovine.net) "

"Full user manual: http://www.pgbovine.net/cde.html "

"Basic usage: cde [command to run and package] ");

fprintf(ofp, " Options ");

fprintf(ofp, " -c : Print the order of files copied into the package in cde-copied-files.log ");

fprintf(ofp, " -o <output dir> : Set a custom output directory instead of "cde-package/" ");

fprintf(ofp, " -f : Do NOT follow forks, so child processes are not packaged ");

fprintf(ofp, " -i '<file path>' : Ignore the given exact file path ");

fprintf(ofp, " -p '<file path>' : Ignore the given file path prefix ");

fprintf(ofp, " -v : Verbose mode (for debugging) ");

fprintf(ofp, " -V : Print version ");

}

exit(exitval);

}

#ifdef SVR4

#ifdef MIPS

void

foobar()

{

}

#endif /* MIPS */

#endif /* SVR4 */

/* Glue for systems without a MMU that cannot provide fork() */

#ifdef HAVE_FORK

# define strace_vforked 0

#else

# define strace_vforked 1

# define fork() vfork()

#endif

static int

set_cloexec_flag(int fd)

{

int flags, newflags;

if ((flags = fcntl(fd, F_GETFD, 0)) < 0)

{

fprintf(stderr, "%s: fcntl F_GETFD: %s ",

progname, strerror(errno));

return -1;

}

newflags = flags | FD_CLOEXEC;

if (flags == newflags)

return 0;

if (fcntl(fd, F_SETFD, newflags) < 0)

{

fprintf(stderr, "%s: fcntl F_SETFD: %s ",

progname, strerror(errno));

return -1;

}

return 0;

}

/*

* When strace is setuid executable, we have to swap uids

* before and after filesystem and process management operations.

*/

static void

swap_uid(void)

{

#ifndef SVR4

int euid = geteuid(), uid = getuid();

if (euid != uid && setreuid(euid, uid) < 0)

{

fprintf(stderr, "%s: setreuid: %s ",

progname, strerror(errno));

exit(1);

}

#endif

}

#if _LFS64_LARGEFILE

# define fopen_for_output fopen64

#else

# define fopen_for_output fopen

#endif

static FILE *

strace_fopen(const char *path, const char *mode)

{

FILE *fp;

swap_uid();

if ((fp = fopen_for_output(path, mode)) == NULL)

fprintf(stderr, "%s: can't fopen '%s': %s ",

progname, path, strerror(errno));

swap_uid();

if (fp && set_cloexec_flag(fileno(fp)) < 0)

{

fclose(fp);

fp = NULL;

}

return fp;

}

static int popen_pid = -1;

#ifndef _PATH_BSHELL

# define _PATH_BSHELL "/bin/sh"

#endif

/*

* We cannot use standard popen(3) here because we have to distinguish

* popen child process from other processes we trace, and standard popen(3)

* does not export its child's pid.

*/

static FILE *

strace_popen(const char *command)

{

int fds[2];

swap_uid();

if (pipe(fds) < 0)

{

fprintf(stderr, "%s: pipe: %s ",

progname, strerror(errno));

swap_uid();

return NULL;

}

if (set_cloexec_flag(fds[1]) < 0)

{

close(fds[0]);

close(fds[1]);

swap_uid();

return NULL;

}

if ((popen_pid = fork()) == -1)

{

fprintf(stderr, "%s: fork: %s ",

progname, strerror(errno));

close(fds[0]);

close(fds[1]);

swap_uid();

return NULL;

}

if (popen_pid)

{

/* parent */

close(fds[0]);

swap_uid();

return fdopen(fds[1], "w");

} else

{

/* child */

close(fds[1]);

if (fds[0] && (dup2(fds[0], 0) || close(fds[0])))

{

fprintf(stderr, "%s: dup2: %s ",

progname, strerror(errno));

_exit(1);

}

execl(_PATH_BSHELL, "sh", "-c", command, NULL);

fprintf(stderr, "%s: execl: %s: %s ",

progname, _PATH_BSHELL, strerror(errno));

_exit(1);

}

}

static int

newoutf(struct tcb *tcp)

{

if (outfname && followfork > 1) {

char name[520 + sizeof(int) * 3];

FILE *fp;

sprintf(name, "%.512s.%u", outfname, tcp->pid);

if ((fp = strace_fopen(name, "w")) == NULL)

return -1;

tcp->outf = fp;

}

return 0;

}

static void

startup_attach(void)

{

int tcbi;

struct tcb *tcp;

/*

* Block user interruptions as we would leave the traced

* process stopped (process state T) if we would terminate in

* between PTRACE_ATTACH and wait4 () on SIGSTOP.

* We rely on cleanup () from this point on.

*/

if (interactive)

sigprocmask(SIG_BLOCK, &blocked_set, NULL);

if (daemonized_tracer) {

pid_t pid = fork();

if (pid < 0) {

_exit(1);

}

if (pid) { /* parent */

/*

* Wait for child to attach to straced process

* (our parent). Child SIGKILLs us after it attached.

* Parent's wait() is unblocked by our death,

* it proceeds to exec the straced program.

*/

pause();

_exit(0); /* paranoia */

}

}

for (tcbi = 0; tcbi < tcbtabsize; tcbi++) {

tcp = tcbtab[tcbi];

if (!(tcp->flags & TCB_INUSE) || !(tcp->flags & TCB_ATTACHED))

continue;

#ifdef LINUX

if (tcp->flags & TCB_CLONE_THREAD)

continue;

#endif

/* Reinitialize the output since it may have changed. */

tcp->outf = outf;

if (newoutf(tcp) < 0)

exit(1);

#ifdef USE_PROCFS

if (proc_open(tcp, 1) < 0) {

fprintf(stderr, "trouble opening proc file ");

droptcb(tcp);

continue;

}

#else /* !USE_PROCFS */

# ifdef LINUX

if (followfork && !daemonized_tracer) {

char procdir[sizeof("/proc/%d/task") + sizeof(int) * 3];

DIR *dir;

sprintf(procdir, "/proc/%d/task", tcp->pid);

dir = opendir(procdir);

if (dir != NULL) {

unsigned int ntid = 0, nerr = 0;

struct dirent *de;

int tid;

while ((de = readdir(dir)) != NULL) {

if (de->d_fileno == 0)

continue;

tid = atoi(de->d_name);

if (tid <= 0)

continue;

++ntid;

if (ptrace(PTRACE_ATTACH, tid, (char *) 1, 0) < 0)

++nerr;

else if (tid != tcbtab[tcbi]->pid) {

tcp = alloctcb(tid);

tcp->flags |= TCB_ATTACHED|TCB_CLONE_THREAD|TCB_FOLLOWFORK;

tcbtab[tcbi]->nchildren++;

tcbtab[tcbi]->nclone_threads++;

tcp->parent = tcbtab[tcbi];

CDE_init_tcb_dir_fields(tcp); // pgbovine - do it AFTER you init parent

}

if (interactive) {

sigprocmask(SIG_SETMASK, &empty_set, NULL);

if (interrupted)

return;

sigprocmask(SIG_BLOCK, &blocked_set, NULL);

}

}

closedir(dir);

ntid -= nerr;

if (ntid == 0) {

perror("attach: ptrace(PTRACE_ATTACH, ...)");

droptcb(tcp);

continue;

}

if (!qflag) {

fprintf(stderr, ntid > 1

? "Process %u attached with %u threads - interrupt to quit "

: "Process %u attached - interrupt to quit ",

tcbtab[tcbi]->pid, ntid);

}

continue;

} /* if (opendir worked) */

} /* if (-f) */

# endif

if (ptrace(PTRACE_ATTACH, tcp->pid, (char *) 1, 0) < 0) {

perror("attach: ptrace(PTRACE_ATTACH, ...)");

droptcb(tcp);

continue;

}

/* INTERRUPTED is going to be checked at the top of TRACE. */

if (daemonized_tracer) {

/*

* It is our grandparent we trace, not a -p PID.

* Don't want to just detach on exit, so...

*/

tcp->flags &= ~TCB_ATTACHED;

/*

* Make parent go away.

* Also makes grandparent's wait() unblock.

*/

kill(getppid(), SIGKILL);

}

#endif /* !USE_PROCFS */

if (!qflag)

fprintf(stderr,

"Process %u attached - interrupt to quit ",

tcp->pid);

}

if (interactive)

sigprocmask(SIG_SETMASK, &empty_set, NULL);

}

static void

startup_child (char **argv)

{

struct stat statbuf;

const char *filename;

char pathname[MAXPATHLEN];

int pid = 0;

struct tcb *tcp;

// pgbovine

char path_to_search[MAXPATHLEN];

path_to_search[0] = '';

filename = argv[0];

if (strchr(filename, '/')) {

if (strlen(filename) > sizeof pathname - 1) {

errno = ENAMETOOLONG;

perror("strace: exec");

exit(1);

}

strcpy(pathname, filename);

}

#ifdef USE_DEBUGGING_EXEC

/*

* Debuggers customarily check the current directory

* first regardless of the path but doing that gives

* security geeks a panic attack.

*/

else if (stat(filename, &statbuf) == 0)

strcpy(pathname, filename);

#endif /* USE_DEBUGGING_EXEC */

else {

const char *path;

int m, n, len;

for (path = getenv("PATH"); path && *path; path += m) {

if (strchr(path, ':')) {

n = strchr(path, ':') - path;

m = n + 1;

}

else

m = n = strlen(path);

if (n == 0) {

if (!getcwd(pathname, MAXPATHLEN))

continue;

len = strlen(pathname);

}

else if (n > sizeof pathname - 1)

continue;

else {

strncpy(pathname, path, n);

len = n;

}

if (len && pathname[len - 1] != '/')

pathname[len++] = '/';

strcpy(pathname + len, filename);

// pgbovine

if (CDE_exec_mode) {

strcpy_redirected_cderoot(path_to_search, pathname);

}

else {

strcpy(path_to_search, pathname);

}

//printf("path_to_search = '%s' ", path_to_search);

if (stat(path_to_search, &statbuf) == 0 &&

/* Accept only regular files

with some execute bits set.

XXX not perfect, might still fail */

S_ISREG(statbuf.st_mode) &&

(statbuf.st_mode & 0111))

break;

}

}

// pgbovine - if we still haven't initialized it yet, do so now

if (path_to_search[0] == '') { // uninit

if (CDE_exec_mode) {

strcpy_redirected_cderoot(path_to_search, pathname);

}

else {

strcpy(path_to_search, pathname);

}

}

if (stat(path_to_search, &statbuf) < 0) {

fprintf(stderr, "%s: %s: command not found (path_to_search=%s) ",

progname, filename, path_to_search);

exit(1);

}

strace_child = pid = fork();

if (pid < 0) {

perror("strace: fork");

cleanup();

exit(1);

}

if ((pid != 0 && daemonized_tracer) /* parent: to become a traced process */

|| (pid == 0 && !daemonized_tracer) /* child: to become a traced process */

) {

pid = getpid();

#ifdef USE_PROCFS

if (outf != stderr) close (fileno (outf));

#ifdef MIPS

/* Kludge for SGI, see proc_open for details. */

sa.sa_handler = foobar;

sa.sa_flags = 0;

sigemptyset(&sa.sa_mask);

sigaction(SIGINT, &sa, NULL);

#endif /* MIPS */

#ifndef FREEBSD

pause();

#else /* FREEBSD */

kill(pid, SIGSTOP); /* stop HERE */

#endif /* FREEBSD */

#else /* !USE_PROCFS */

if (outf!=stderr)

close(fileno (outf));

if (!daemonized_tracer) {

if (ptrace(PTRACE_TRACEME, 0, (char *) 1, 0) < 0) {

perror("strace: ptrace(PTRACE_TRACEME, ...)");

exit(1);

}

if (debug)

kill(pid, SIGSTOP);

}

if (username != NULL || geteuid() == 0) {

uid_t run_euid = run_uid;

gid_t run_egid = run_gid;

if (statbuf.st_mode & S_ISUID)

run_euid = statbuf.st_uid;

if (statbuf.st_mode & S_ISGID)

run_egid = statbuf.st_gid;

/*

* It is important to set groups before we

* lose privileges on setuid.

*/

if (username != NULL) {

if (initgroups(username, run_gid) < 0) {

perror("initgroups");

exit(1);

}

if (setregid(run_gid, run_egid) < 0) {

perror("setregid");

exit(1);

}

if (setreuid(run_uid, run_euid) < 0) {

perror("setreuid");

exit(1);

}

}

}

else

setreuid(run_uid, run_uid);

if (!daemonized_tracer) {

/*

* Induce an immediate stop so that the parent

* will resume us with PTRACE_SYSCALL and display

* this execve call normally.

* Unless of course we're on a no-MMU system where

* we vfork()-ed, so we cannot stop the child.

*/

if (!strace_vforked)

kill(getpid(), SIGSTOP);

} else {

struct sigaction sv_sigchld;

sigaction(SIGCHLD, NULL, &sv_sigchld);

/*

* Make sure it is not SIG_IGN, otherwise wait

* will not block.

*/

signal(SIGCHLD, SIG_DFL);

/*

* Wait for grandchild to attach to us.

* It kills child after that, and wait() unblocks.

*/

alarm(3);

wait(NULL);

alarm(0);

sigaction(SIGCHLD, &sv_sigchld, NULL);

}

#endif /* !USE_PROCFS */

// pgbovine - subtle ... even though we look for the existence of

// path_to_search, we still want to execute pathname, since our

// CDE_begin_execve handler expects an original pristine pathname :)

//printf("execv %s (path_to_search %s) ", pathname, path_to_search);

// sanity check when running from outside of cde-root/, in order to

// prevent confusion ...

extern char cde_exec_from_outside_cderoot;

if (cde_exec_from_outside_cderoot) {

if (strstr(pathname, CDE_ROOT_NAME)) {

fprintf(stderr, "Fatal error: can't cde-exec '%s' from outside of cde-root/ (instead try specifying a program path relative to cde-root/) ", pathname);

exit(1);

}

}

execv(pathname, argv);

perror("strace: exec");

_exit(1);

}

/* We are the tracer. */

tcp = alloctcb(daemonized_tracer ? getppid() : pid);

CDE_init_tcb_dir_fields(tcp); // pgbovine

if (daemonized_tracer) {

/* We want subsequent startup_attach() to attach to it. */

tcp->flags |= TCB_ATTACHED;

}

#ifdef USE_PROCFS

if (proc_open(tcp, 0) < 0) {

fprintf(stderr, "trouble opening proc file ");

cleanup();

exit(1);

}

#endif /* USE_PROCFS */

}

#ifdef LINUX

/*

* Test whether the kernel support PTRACE_O_TRACECLONE et al options.

* First fork a new child, call ptrace with PTRACE_SETOPTIONS on it,

* and then see which options are supported by the kernel.

*/

static int

test_ptrace_setoptions(void)

{

int pid, expected_grandchild = 0, found_grandchild = 0;

const unsigned int test_options = PTRACE_O_TRACECLONE |

PTRACE_O_TRACEFORK |

PTRACE_O_TRACEVFORK;

if ((pid = fork()) < 0)

return -1;

else if (pid == 0) {

if (ptrace(PTRACE_TRACEME, 0, (char *)1, 0) < 0)

_exit(1);

kill(getpid(), SIGSTOP);

_exit(fork() < 0);

}

while (1) {

int status, tracee_pid;

tracee_pid = wait(&status);

if (tracee_pid == -1) {

if (errno == EINTR)

continue;

else if (errno == ECHILD)

break;

perror("test_ptrace_setoptions");

return -1;

}

if (tracee_pid != pid) {

found_grandchild = tracee_pid;

if (ptrace(PTRACE_CONT, tracee_pid, 0, 0) < 0 &&

errno != ESRCH)

kill(tracee_pid, SIGKILL);

}

else if (WIFSTOPPED(status)) {

switch (WSTOPSIG(status)) {

case SIGSTOP:

if (ptrace(PTRACE_SETOPTIONS, pid,

NULL, test_options) < 0) {

kill(pid, SIGKILL);

return -1;

}

break;

case SIGTRAP:

if (status >> 16 == PTRACE_EVENT_FORK) {

long msg = 0;

if (ptrace(PTRACE_GETEVENTMSG, pid,

NULL, (long) &msg) == 0)

expected_grandchild = msg;

}

break;

}

if (ptrace(PTRACE_SYSCALL, pid, 0, 0) < 0 &&

errno != ESRCH)

kill(pid, SIGKILL);

}

}

if (expected_grandchild && expected_grandchild == found_grandchild)

ptrace_setoptions |= test_options;

return 0;

}

#endif

int

main(int argc, char *argv[])

{

struct tcb *tcp;

int c, pid = 0;

int optF = 0;

struct sigaction sa;

static char buf[BUFSIZ];

// pgbovine - make sure this constant is a reasonable number and not something KRAZY

if (MAXPATHLEN > (1024 * 4096)) {

fprintf(stderr, "cde error, MAXPATHLEN is HUGE!!! ");

exit(1);

}

if (!argv[0]) {

fprintf(stderr, "cde error, wha??? ");

exit(1);

}

progname = argv[0];

CDE_clear_options_arrays(); // pgbovine - call this as EARLY as possible so that '-i' and '-p' options work!

// pgbovine - if program name is 'cde-exec', then activate CDE_exec_mode

CDE_exec_mode = (strcmp(basename(progname), "cde-exec") == 0);

/* Allocate the initial tcbtab. */

tcbtabsize = argc; /* Surely enough for all -p args. */

if ((tcbtab = calloc(tcbtabsize, sizeof tcbtab[0])) == NULL) {

fprintf(stderr, "%s: out of memory ", progname);

exit(1);

}

if ((tcbtab[0] = calloc(tcbtabsize, sizeof tcbtab[0][0])) == NULL) {

fprintf(stderr, "%s: out of memory ", progname);

exit(1);

}

for (tcp = tcbtab[0]; tcp < &tcbtab[0][tcbtabsize]; ++tcp)

tcbtab[tcp - tcbtab[0]] = &tcbtab[0][tcp - tcbtab[0]];

outf = stderr;

// pgbovine - set interactive to 0 by default (rather than 1) so that we

// pass signals (e.g., SIGINT caused by Ctrl-C ) through to the child process

//interactive = 1;

interactive = 0;

set_sortby(DEFAULT_SORTBY);

set_personality(DEFAULT_PERSONALITY);

// pgbovine - only track selected system calls

// qualify actually mutates this string, so we can't pass in a constant

//

// syscalls added after Jan 1, 2011:

// utimes,openat,faccessat,fstatat64,fchownat,fchmodat,futimesat,mknodat

// linkat,symlinkat,renameat,readlinkat,mkdirat,unlinkat

//

// syscalls added after August 10, 2011 (mostly minor ones):

// setxattr, lsetxattr, getxattr, lgetxattr, listxattr, llistxattr, removexattr, lremovexattr

char* tmp = strdup("trace=open,execve,stat,stat64,lstat,lstat64,oldstat,oldlstat,link,symlink,unlink,rename,access,creat,chmod,chown,chown32,lchown,lchown32,readlink,utime,truncate,truncate64,chdir,fchdir,mkdir,rmdir,getcwd,mknod,bind,connect,utimes,openat,faccessat,fstatat64,fchownat,fchmodat,futimesat,mknodat,linkat,symlinkat,renameat,readlinkat,mkdirat,unlinkat,setxattr,lsetxattr,getxattr,lgetxattr,listxattr,llistxattr,removexattr,lremovexattr");

qualify(tmp);

free(tmp);

qualify("abbrev=all");

qualify("verbose=all");

qualify("signal=all");

while ((c = getopt(argc, argv,

"+cCdfFhqrtTvVxzlsn"

#ifndef USE_PROCFS

"D"

#endif

"a:e:o:O:S:u:E:i:p:")) != EOF) {

switch (c) {

case 'c':

// pgbovine - hijack for -c option

CDE_copied_files_logfile = fopen("cde-copied-files.log", "w");

/*

if (cflag == CFLAG_BOTH) {

fprintf(stderr, "%s: -c and -C are mutually exclusive options ",

progname);

exit(1);

}

cflag = CFLAG_ONLY_STATS;

*/

break;

case 'C':

if (cflag == CFLAG_ONLY_STATS) {

fprintf(stderr, "%s: -c and -C are mutually exclusive options ",

progname);

exit(1);

}

cflag = CFLAG_BOTH;

break;

case 'd':

debug++;

break;

#ifndef USE_PROCFS

case 'D':

daemonized_tracer = 1;

break;

#endif

case 'F':

optF = 1;

break;

case 'f':

// pgbovine - hijack for -f option meaning to NOT follow forks

// (might be confusing since strace uses -f to mean DO follow forks)

followfork = 0;

break;

case 'h':

usage(stdout, 0);

break;

case 'i':

// pgbovine - hijack for the '-i' option

// for specifying an ignore_exact path on the command line

CDE_add_ignore_exact_path(strdup(optarg));

//iflag++;

break;

case 'q':

qflag++;

break;

case 'r':

rflag++;

tflag++;

break;

case 't':

tflag++;

break;

case 'n':

// pgbovine - hijack for '-n' option

CDE_block_net_access = 1;

break;

case 'T':

dtime++;

break;

case 'x':

xflag++;

break;

case 'v':

// pgbovine - hijack for the '-v' option

//qualify("abbrev=none");

CDE_verbose_mode = 1;

break;

case 'V':

printf("CDE v0.1 ");

exit(0);

break;

case 'z':

not_failing_only = 1;

break;

case 'a':

acolumn = atoi(optarg);

break;

case 'e':

qualify(optarg);

break;

case 'o':

// pgbovine - hijack for the '-o' option

CDE_PACKAGE_DIR = strdup(optarg);

//outfname = strdup(optarg);

break;

case 'O':

set_overhead(atoi(optarg));

break;

case 'l':

// pgbovine - hijack for the '-l' option

CDE_use_linker_from_package = 0;

break;

case 'p':

// pgbovine - hijack for the '-p' option

// actually we no longer support the '-p' option (provenance mode)

// instead, the new '-p' option is to manually specify an ignore_prefix

// on the command line

CDE_add_ignore_prefix_path(strdup(optarg));

/*

if ((pid = atoi(optarg)) <= 0) {

fprintf(stderr, "%s: Invalid process id: %s ",

progname, optarg);

break;

}

if (pid == getpid()) {

fprintf(stderr, "%s: I'm sorry, I can't let you do that, Dave. ", progname);

break;

}

tcp = alloc_tcb(pid, 0);

tcp->flags |= TCB_ATTACHED;

pflag_seen++;

*/

break;

case 's':

// pgbovine - hijack for 's' (streaming mode)

CDE_exec_streaming_mode = 1;

/*

max_strlen = atoi(optarg);

if (max_strlen < 0) {

fprintf(stderr,

"%s: invalid -s argument: %s ",

progname, optarg);

exit(1);

}

*/

break;

case 'S':

set_sortby(optarg);

break;

case 'u':

username = strdup(optarg);

break;

case 'E':

if (putenv(optarg) < 0) {

fprintf(stderr, "%s: out of memory ",

progname);

exit(1);

}

break;

default:

usage(stderr, 1);

break;

}

}

if ((optind == argc) == !pflag_seen)

usage(stderr, 1);

if (pflag_seen && daemonized_tracer) {

fprintf(stderr,

"%s: -D and -p are mutually exclusive options ",

progname);

exit(1);

}

if (!followfork)

followfork = optF;

if (followfork > 1 && cflag) {

fprintf(stderr,

"%s: (-c or -C) and -ff are mutually exclusive options ",

progname);

exit(1);

}

/* See if they want to run as another user. */

if (username != NULL) {

struct passwd *pent;

if (getuid() != 0 || geteuid() != 0) {

fprintf(stderr,

"%s: you must be root to use the -u option ",

progname);

exit(1);

}

if ((pent = getpwnam(username)) == NULL) {

fprintf(stderr, "%s: cannot find user `%s' ",

progname, username);

exit(1);

}

run_uid = pent->pw_uid;

run_gid = pent->pw_gid;

}

else {

run_uid = getuid();

run_gid = getgid();

}

#ifdef LINUX

if (followfork) {

if (test_ptrace_setoptions() < 0) {

fprintf(stderr,

"Test for options supported by PTRACE_SETOPTIONS "

"failed, giving up using this feature. ");

ptrace_setoptions = 0;

}

if (debug)

fprintf(stderr, "ptrace_setoptions = %#x ",

ptrace_setoptions);

}

#endif

Hire Me For All Your Tutoring Needs
Integrity-first tutoring: clear explanations, guidance, and feedback.
Drop an Email at
drjack9650@gmail.com
Chat Now And Get Quote