diff options
-rw-r--r-- | ChangeLog.txt | 15 | ||||
-rw-r--r-- | guts/README | 16 | ||||
-rw-r--r-- | guts/fork.c | 17 | ||||
-rwxr-xr-x | maketables | 68 | ||||
-rwxr-xr-x | makewrappers | 10 | ||||
-rw-r--r-- | pseudo.1 | 10 | ||||
-rw-r--r-- | pseudo.h | 10 | ||||
-rw-r--r-- | pseudo_client.c | 42 | ||||
-rw-r--r-- | pseudo_db.c | 2 | ||||
-rw-r--r-- | pseudo_util.c | 58 | ||||
-rw-r--r-- | pseudo_wrappers.c | 350 | ||||
-rw-r--r-- | templates/wrapfuncs.c | 2 | ||||
-rw-r--r-- | templates/wrapfuncs.h | 8 | ||||
-rw-r--r-- | templates/wrapper_table | 11 | ||||
-rwxr-xr-x | test/test-forkoff-env_i.sh | 30 | ||||
-rwxr-xr-x | test/test-forkoff.sh | 18 | ||||
-rw-r--r-- | wrapfuncs.in | 5 |
17 files changed, 595 insertions, 77 deletions
diff --git a/ChangeLog.txt b/ChangeLog.txt index 8e737e4..121fe12 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -1,3 +1,18 @@ +2010-12-07: + * (seebs) whitespace fixes + * (seebs) improve fork and PSEUDO_DISABLED + * (seebs) add support for clone(2) + * (mhatle) rework/improve clone(2) support + * (mhatle) add test code for PSEUDO_DISABLED + +2010-12-02: + * (seebs) rework of fork/exec, add PSEUDO_DISABLED + +2010-11-30: + * (seebs) move *_t types to a separate file. + * (seebs) remove unused tables from pseudo_db.c + * (seebs) cleanup .gitignore + 2010-11-17: * (seebs) add "Futures.txt" notes about future development plans * (seebs) split some of the templating code out of makewrappers diff --git a/guts/README b/guts/README index 8bfbfe6..0a1fe5f 100644 --- a/guts/README +++ b/guts/README @@ -83,7 +83,7 @@ wrappers: close dup dup2 - excl* (all redirect through execve) + execl* (no guts implementations; see pseudo_wrappers.c) execv execve execvp @@ -97,7 +97,6 @@ wrappers: fcntl fork link - vfork The following functions don't have any direct database interactions, but are used to simulate the permissions system: @@ -206,3 +205,16 @@ needed because we don't actually track or manage extended attributes, but a few programs attempt to use *setxattr() to set regular permissions, and only use a regular chmod if the *setxattr() call returns -1 and sets errno to ENOTSUP. + + fgetxattr + flistxattr + fremovexattr + fsetxattr + getxattr + lgetxattr + listxattr + llistxattr + lremovexattr + lsetxattr + removexattr + setxattr diff --git a/guts/fork.c b/guts/fork.c index 4a91f7b..fa23be1 100644 --- a/guts/fork.c +++ b/guts/fork.c @@ -6,10 +6,19 @@ * wrap_fork(void) { * int rc = -1; */ - rc = real_fork(); - if (rc == 0) - pseudo_client_reset(); - + if (real_fork) { + rc = real_fork(); + /* special case: we may want to enable or disable + * pseudo in the child process + */ + if (rc == 0) { + pseudo_setupenv(); + pseudo_client_reset(); + } + } else { + /* rc was initialized to the "failure" value */ + pseudo_enosys("fork"); + } /* return rc; * } */ @@ -50,8 +50,6 @@ value. (This is for consistency with C array bounds.) import glob import sys -import re -import datetime import string from templatefile import TemplateFile @@ -63,11 +61,11 @@ class DataType: source = file(path) definition = source.readline().rstrip() self.name, qualifiers = string.split(definition, ': ', 2) - if '; ' in qualifiers: + if '; ' in qualifiers: self.prefix, columns = string.split(qualifiers, '; ') - else: - self.prefix = qualifiers - columns = [] + else: + self.prefix = qualifiers + columns = [] if len(columns): self.columns = [] columns = string.split(columns, ', ') @@ -80,23 +78,23 @@ class DataType: else: self.columns = [] self.data = [] - self.comments = [] + self.comments = [] for line in source.readlines(): item = {} - if line.startswith('#'): - self.comments.append(line.rstrip().replace('#', '')) - continue + if line.startswith('#'): + self.comments.append(line.rstrip().replace('#', '')) + continue # first entry on the line is the "real" name/id, following hunks # are additional columns cols = string.split(line.rstrip(), ', ') item["name"] = cols.pop(0) - item["upper"] = item["name"].replace('-', '_').upper() + item["upper"] = item["name"].replace('-', '_').upper() column_list = [] for col in self.columns: if len(cols) > 0: - column_list.append({"name":col["name"], "value":cols.pop(0)}) + column_list.append({"name":col["name"], "value":cols.pop(0)}) else: - column_list.append({"name":col["name"], "value":col["default"]}) + column_list.append({"name":col["name"], "value":col["default"]}) item["cols"] = column_list self.data.append(item) @@ -110,22 +108,22 @@ class DataType: return attr def __repr__(self): - str = "" - str += "type: %s_t" % self.name - str += " %s_ENUM\n" % self.prefix + out = "" + out += "type: %s_t" % self.name + out += " %s_ENUM\n" % self.prefix for col in self.columns: - str += "\tcol: %s (%s)\n" % (col["name"], col["value"]) + out += "\tcol: %s (%s)\n" % (col["name"], col["value"]) for item in self.data: - str += "item: %s\n" % item["name"] + out += "item: %s\n" % item["name"] for col in item["cols"]: - str += "\t%s(%s)\n" % (col["name"], col["value"]) - return str + out += "\t%s(%s)\n" % (col["name"], col["value"]) + return out def comment(self): - if len(self.comments): - return '/*' + '\n *'.join(self.comments) + ' */\n' - else: - return '' + if len(self.comments): + return '/*' + '\n *'.join(self.comments) + ' */\n' + else: + return '' def names(self): return ',\n\t'.join('"%s"' % x["name"] for x in self.data) @@ -141,7 +139,7 @@ class DataType: decl_lines.append('\t"%s",' % col["value"]) for item in self.data: decl_lines.append('\t"%s",' % item["cols"][column]["value"]) - decl_lines.append('\tNULL') + decl_lines.append('\tNULL') decl_lines.append("};") column = column + 1 return '\n'.join(decl_lines) @@ -149,21 +147,21 @@ class DataType: def column_funcs(self): decl_lines = [] for col in self.columns: - decl_lines.append('extern const char *') - decl_lines.append('pseudo_%s_%s(pseudo_%s_t id) {' % - (self.name, col["name"], self.name)) - decl_lines.append('\tif (id < 0 || id >= %s_MAX)' % (self.prefix)) - decl_lines.append('\t\treturn "%s";' % col["value"]) - decl_lines.append('\treturn %s_id_to_%s[id];' % - (self.name, col["name"])) - decl_lines.append('}') + decl_lines.append('extern const char *') + decl_lines.append('pseudo_%s_%s(pseudo_%s_t id) {' % + (self.name, col["name"], self.name)) + decl_lines.append('\tif (id < 0 || id >= %s_MAX)' % (self.prefix)) + decl_lines.append('\t\treturn "%s";' % col["value"]) + decl_lines.append('\treturn %s_id_to_%s[id];' % + (self.name, col["name"])) + decl_lines.append('}') return '\n'.join(decl_lines) def column_protos(self): decl_lines = [] for col in self.columns: - decl_lines.append('extern const char *pseudo_%s_%s(pseudo_%s_t id);' % - (self.name, col["name"], self.name)) + decl_lines.append('extern const char *pseudo_%s_%s(pseudo_%s_t id);' % + (self.name, col["name"], self.name)) return '\n'.join(decl_lines) def main(): diff --git a/makewrappers b/makewrappers index 5ec58f8..fba2fce 100755 --- a/makewrappers +++ b/makewrappers @@ -33,8 +33,6 @@ class ArgumentList: self.variadic_decl = "" self.variadic_start = "" self.variadic_end = "" - self.prologue_call_real = \ - "/* pass the call on to the underlying syscall */" # (void) is an empty list, not a list of a single argument which is void if text == "void": return @@ -68,11 +66,8 @@ class ArgumentList: (self.variadic_arg.name, self.variadic_arg.type) else: - self.variadic_end = "va_end(ap);\n" - self.prologue_call_real = ('/* no way to pass a ... */' - '\n\t\t\t\tassert(!"cannot chain ' - 'to real versions of variadic' - 'functions");') + # lie blatantly; we don't handle this case + self.variadic = False # for a wrap function, the outer foo() wrapper will convert to a va_list, # but the inner wrap_foo() just passes the va_list through. @@ -212,6 +207,7 @@ class Function: self.dirfd = 'AT_FDCWD' self.flags = '0' self.paths_to_munge = [] + self.nowrappers = None # used for the copyright date when creating stub functions self.date = datetime.date.today().year @@ -400,6 +400,16 @@ in the .I var/pseudo directory, while clients log to standard error. .TP 8 +.B PSEUDO_DISABLED +If this variable is set to a value that doesn't look like f, F, n, N, or +a numeric zero, the +.I pseudo +client library does not modify the behavior of called functions, though it +continues to intercept them and block signals while processing them. This +variable is reevaluated on every call to +.IR fork(2) ,\ clone(2) +or related functions. +.TP 8 .B PSEUDO_ENOSYS_ABORT If this variable is set, the .I pseudo @@ -20,17 +20,16 @@ #include <stdlib.h> #include <fcntl.h> -struct pseudo_variables { char * key; size_t key_len; char * value; }; - void pseudo_dump_env(char **envp); -int pseudo_set_value(const char * key, const char * value); -char * pseudo_get_value(const char * key); +int pseudo_set_value(const char *key, const char *value); +char *pseudo_get_value(const char *key); #include "pseudo_tables.h" extern void pseudo_debug_verbose(void); extern void pseudo_debug_terse(void); extern int pseudo_util_debug_fd; +extern int pseudo_disabled; #ifndef NDEBUG extern int pseudo_debug_real(int, char *, ...) __attribute__ ((format (printf, 2, 3))); #define pseudo_debug pseudo_debug_real @@ -62,6 +61,9 @@ extern ssize_t pseudo_path_max(void); extern int pseudo_etc_file(const char *filename, char *realname, int flags, char **search, int dircount); #define PSEUDO_ETC_FILE(name, realname, flags) pseudo_etc_file((name), (realname), (flags), (char *[]) { pseudo_chroot, pseudo_passwd }, 2) +/* refresh environment variables from internals */ +extern void pseudo_reinit_environment(void); + extern char *pseudo_version; #ifndef PSEUDO_BINDIR diff --git a/pseudo_client.c b/pseudo_client.c index b169259..e30d3e7 100644 --- a/pseudo_client.c +++ b/pseudo_client.c @@ -58,6 +58,8 @@ char *pseudo_chroot = NULL; char *pseudo_passwd = NULL; size_t pseudo_chroot_len = 0; char *pseudo_cwd_rel = NULL; +/* used for PSEUDO_DISABLED */ +int pseudo_disabled = 0; static char **fd_paths = NULL; static int nfds = 0; @@ -311,12 +313,50 @@ pseudo_client_close(int fd) { void pseudo_client_reset() { + char *env_disabled = NULL; + pseudo_antimagic(); pseudo_new_pid(); if (connect_fd != -1) { close(connect_fd); connect_fd = -1; } + + /* in child processes, PSEUDO_DISABLED may have become set to + * some truthy value, in which case we'd disable pseudo, + * or it may have gone away, in which case we'd enable + * pseudo. + */ + env_disabled = getenv("PSEUDO_DISABLED"); + if (env_disabled) { + int actually_disabled = 1; + switch (*env_disabled) { + case 'f': + case 'F': + case 'n': + case 'N': + actually_disabled = 0; + break; + case '0': + actually_disabled = atoi(env_disabled); + break; + } + if (actually_disabled) { + if (!pseudo_disabled) { + pseudo_antimagic(); + pseudo_disabled = 1; + } + env_disabled = "1"; + } else { + if (pseudo_disabled) { + pseudo_magic(); + pseudo_disabled = 0; + } + env_disabled = "0"; + } + pseudo_set_value("PSEUDO_DISABLED", env_disabled); + } + if (!pseudo_inited) { char *env; @@ -354,6 +394,8 @@ pseudo_client_reset() { pseudo_inited = 1; } pseudo_client_getcwd(); + /* make sure environment variables are back in sync */ + pseudo_reinit_environment(); pseudo_magic(); } diff --git a/pseudo_db.c b/pseudo_db.c index 1a1b1a3..225dd37 100644 --- a/pseudo_db.c +++ b/pseudo_db.c @@ -820,7 +820,7 @@ pdb_query(char *stmt_type, pseudo_query_t *traits, unsigned long fields, int uni sqlite3_stmt *stmt; int done_any = 0; int field = 0; - char *order_by = "id"; + const char *order_by = "id"; char *order_dir = "ASC"; int rc; pseudo_query_field_t f; diff --git a/pseudo_util.c b/pseudo_util.c index 2e75e69..97c9911 100644 --- a/pseudo_util.c +++ b/pseudo_util.c @@ -39,6 +39,12 @@ #include "pseudo_ipc.h" #include "pseudo_db.h" +struct pseudo_variables { + char *key; + size_t key_len; + char *value; +}; + /* The order below is not arbitrary, but based on an assumption * of how often things will be used. */ @@ -58,6 +64,7 @@ static struct pseudo_variables pseudo_env[] = { { "PSEUDO_ENOSYS_ABORT", 19, NULL }, { "PSEUDO_NOSYMLINKEXP", 19, NULL }, { "PSEUDO_RELOADED", 15, NULL }, + { "PSEUDO_DISABLED", 15, NULL }, { NULL, 0, NULL } /* Magic terminator */ }; @@ -69,12 +76,13 @@ static struct pseudo_variables pseudo_env[] = { * program starts playing with things, so we need to do our * best to handle that case. */ -int _in_init = -1; /* Not yet run */ +static int _pseudo_in_init = -1; /* Not yet run */ static void _libpseudo_init(void) __attribute__ ((constructor)); #if 0 -static void dump_env(char **envp) { +static void +dump_env(char **envp) { size_t i = 0; for (i = 0; envp[i]; i++) { pseudo_debug(0,"dump_envp: [%d]%s\n", (int) i, envp[i]); @@ -84,18 +92,27 @@ static void dump_env(char **envp) { pseudo_debug(0,"dump_envp: {%d}%s=%s\n", (int) i, pseudo_env[i].key, pseudo_env[i].value); } - pseudo_debug(0, "dump_envp: _in_init %d\n", _in_init); + pseudo_debug(0, "dump_envp: _in_init %d\n", _pseudo_in_init); } #endif +void +pseudo_reinit_environment(void) { + _pseudo_in_init = 0; + _libpseudo_init(); +} + /* Caller must free memory! */ -char * pseudo_get_value(const char * key) { +char * +pseudo_get_value(const char *key) { size_t i = 0; char * value; - if (_in_init == -1) _libpseudo_init(); + if (_pseudo_in_init == -1) + _libpseudo_init(); - for (i = 0; pseudo_env[i].key && memcmp(pseudo_env[i].key, key, pseudo_env[i].key_len + 1); i++) ; + for (i = 0; pseudo_env[i].key && memcmp(pseudo_env[i].key, key, pseudo_env[i].key_len + 1); i++) + ; /* Check if the environment has it and we don't ... * if so, something went wrong... so we'll attempt to recover @@ -103,8 +120,10 @@ char * pseudo_get_value(const char * key) { if (pseudo_env[i].key && !pseudo_env[i].value && getenv(pseudo_env[i].key)) _libpseudo_init(); - if (pseudo_env[i].value) value = strdup(pseudo_env[i].value); - else value = NULL; + if (pseudo_env[i].value) + value = strdup(pseudo_env[i].value); + else + value = NULL; if (!pseudo_env[i].key) pseudo_diag("Unknown variable %s.\n", key); @@ -113,17 +132,20 @@ char * pseudo_get_value(const char * key) { } /* We make a copy, so the original values should be freed. */ -int pseudo_set_value(const char * key, const char * value) { +int +pseudo_set_value(const char *key, const char *value) { int rc = 0; size_t i = 0; - if (_in_init == -1) _libpseudo_init(); + if (_pseudo_in_init == -1) + _libpseudo_init(); for (i = 0; pseudo_env[i].key && memcmp(pseudo_env[i].key, key, pseudo_env[i].key_len + 1); i++) ; if (pseudo_env[i].key) { - if (pseudo_env[i].value) free(pseudo_env[i].value); + if (pseudo_env[i].value) + free(pseudo_env[i].value); if (value) { char *new = strdup(value); if (new) @@ -134,25 +156,25 @@ int pseudo_set_value(const char * key, const char * value) { } else pseudo_env[i].value = NULL; } else { - if (!_in_init) pseudo_diag("Unknown variable %s.\n", key); + if (!_pseudo_in_init) pseudo_diag("Unknown variable %s.\n", key); rc = -EINVAL; } return rc; } -static void _libpseudo_init(void) { +static void +_libpseudo_init(void) { size_t i = 0; - _in_init = 1; + _pseudo_in_init = 1; for (i = 0; pseudo_env[i].key; i++) { - if (pseudo_env[i].key) - if (getenv(pseudo_env[i].key)) - pseudo_set_value(pseudo_env[i].key, getenv(pseudo_env[i].key)); + if (getenv(pseudo_env[i].key)) + pseudo_set_value(pseudo_env[i].key, getenv(pseudo_env[i].key)); } - _in_init = 0; + _pseudo_in_init = 0; } /* 5 = ridiculous levels of duplication diff --git a/pseudo_wrappers.c b/pseudo_wrappers.c index dbcfac8..0321fe2 100644 --- a/pseudo_wrappers.c +++ b/pseudo_wrappers.c @@ -246,3 +246,353 @@ pseudo_populate_wrappers(void) { return done; } +static char ** +execl_to_v(va_list ap, const char *argv0, char *const **envp) { + size_t i = 0; + size_t alloc_size = 256; + + char **argv = malloc((sizeof *argv) * alloc_size); + + if (!argv) { + pseudo_debug(1, "execl failed: couldn't allocate memory for %lu arguments\n", + (unsigned long) alloc_size); + return NULL; + } + argv[i++] = (char *) argv0; + + while (argv[i-1]) { + argv[i++] = va_arg(ap, char *const); + if (i > alloc_size - 1) { + alloc_size = alloc_size + 256; + argv = realloc(argv, (sizeof *argv) * alloc_size); + if (!argv) { + pseudo_debug(1, "execl failed: couldn't allocate memory for %lu arguments\n", + (unsigned long) alloc_size); + return NULL; + } + } + } + if (envp) { + *envp = va_arg(ap, char **); + } + return argv; +} + +/* The following wrappers require Special Handling */ + +int +execl(const char *file, const char *arg, ...) { + sigset_t saved; + va_list ap; + char **argv; + + int rc = -1; + + va_start(ap, arg); + argv = execl_to_v(ap, arg, 0); + va_end(ap); + if (!argv) { + errno = ENOMEM; + return -1; + } + + pseudo_debug(4, "called: execl\n"); + pseudo_sigblock(&saved); + if (pseudo_getlock()) { + errno = EBUSY; + sigprocmask(SIG_SETMASK, &saved, NULL); + return -1; + } + if (pseudo_populate_wrappers()) { + int save_errno; + if (antimagic > 0) { + if (real_execv) { + /* use execv to emulate */ + rc = (*real_execv)(file, argv); + } else { + /* rc was initialized to the "failure" value */ + pseudo_enosys("execl"); + } + } else { + + /* exec*() use this to restore the sig mask */ + pseudo_saved_sigmask = saved; + rc = wrap_execv(file, argv); + } + + save_errno = errno; + pseudo_droplock(); + sigprocmask(SIG_SETMASK, &saved, NULL); + pseudo_debug(4, "completed: execl\n"); + errno = save_errno; + free(argv); + return rc; + } else { + pseudo_droplock(); + sigprocmask(SIG_SETMASK, &saved, NULL); + pseudo_debug(4, "completed: execl\n"); + /* rc was initialized to the "failure" value */ + pseudo_enosys("execl"); + free(argv); + return rc; + } +} + +int +execlp(const char *file, const char *arg, ...) { + sigset_t saved; + va_list ap; + char **argv; + + int rc = -1; + + va_start(ap, arg); + argv = execl_to_v(ap, arg, 0); + va_end(ap); + if (!argv) { + errno = ENOMEM; + return -1; + } + + pseudo_debug(4, "called: execlp\n"); + pseudo_sigblock(&saved); + if (pseudo_getlock()) { + errno = EBUSY; + sigprocmask(SIG_SETMASK, &saved, NULL); + return -1; + } + if (pseudo_populate_wrappers()) { + int save_errno; + if (antimagic > 0) { + if (real_execvp) { + /* use execv to emulate */ + rc = (*real_execvp)(file, argv); + } else { + /* rc was initialized to the "failure" value */ + pseudo_enosys("execlp"); + } + } else { + + /* exec*() use this to restore the sig mask */ + pseudo_saved_sigmask = saved; + rc = wrap_execvp(file, argv); + } + + save_errno = errno; + pseudo_droplock(); + sigprocmask(SIG_SETMASK, &saved, NULL); + pseudo_debug(4, "completed: execlp\n"); + errno = save_errno; + free(argv); + return rc; + } else { + pseudo_droplock(); + sigprocmask(SIG_SETMASK, &saved, NULL); + pseudo_debug(4, "completed: execlp\n"); + /* rc was initialized to the "failure" value */ + pseudo_enosys("execlp"); + free(argv); + return rc; + } +} + +int +execle(const char *file, const char *arg, ...) { + sigset_t saved; + va_list ap; + char **argv; + char **envp; + + int rc = -1; + + va_start(ap, arg); + argv = execl_to_v(ap, arg, (char *const **)&envp); + va_end(ap); + if (!argv) { + errno = ENOMEM; + return -1; + } + + pseudo_debug(4, "called: execle\n"); + pseudo_sigblock(&saved); + if (pseudo_getlock()) { + errno = EBUSY; + sigprocmask(SIG_SETMASK, &saved, NULL); + return -1; + } + if (pseudo_populate_wrappers()) { + int save_errno; + if (antimagic > 0) { + if (real_execve) { + /* use execve to emulate */ + rc = (*real_execve)(file, argv, envp); + } else { + /* rc was initialized to the "failure" value */ + pseudo_enosys("execl"); + } + } else { + + /* exec*() use this to restore the sig mask */ + pseudo_saved_sigmask = saved; + rc = wrap_execve(file, argv, envp); + } + + save_errno = errno; + pseudo_droplock(); + sigprocmask(SIG_SETMASK, &saved, NULL); + pseudo_debug(4, "completed: execle\n"); + errno = save_errno; + free(argv); + return rc; + } else { + pseudo_droplock(); + sigprocmask(SIG_SETMASK, &saved, NULL); + pseudo_debug(4, "completed: execle\n"); + /* rc was initialized to the "failure" value */ + pseudo_enosys("execle"); + free(argv); + return rc; + } +} + +int +fork(void) { + sigset_t saved; + + int rc = -1; + + pseudo_debug(4, "called: fork\n"); + pseudo_sigblock(&saved); + if (pseudo_getlock()) { + errno = EBUSY; + sigprocmask(SIG_SETMASK, &saved, NULL); + return -1; + } + if (pseudo_populate_wrappers()) { + int save_errno; + + rc = wrap_fork(); + + save_errno = errno; + + pseudo_droplock(); + sigprocmask(SIG_SETMASK, &saved, NULL); + pseudo_debug(4, "completed: fork\n"); + errno = save_errno; + return rc; + } else { + pseudo_droplock(); + sigprocmask(SIG_SETMASK, &saved, NULL); + pseudo_debug(4, "completed: fork\n"); + /* rc was initialized to the "failure" value */ + pseudo_enosys("fork"); + + return rc; + } +} + +int +vfork(void) { + /* we don't provide support for the distinct semantics + * of vfork() + */ + return fork(); +} + +int +wrap_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, ...) { + /* unused */ + return 0; +} + +int +clone(int (*fn)(void *), void *child_stack, int flags, void *arg, ...) { + sigset_t saved; + va_list ap; + pid_t *pid; + struct user_desc *tls; + pid_t *ctid; + + int rc = -1; + + va_start(ap, arg); + pid = va_arg(ap, pid_t *); + tls = va_arg(ap, struct user_desc *); + ctid = va_arg(ap, pid_t *); + va_end(ap); + + + pseudo_debug(4, "called: clone\n"); + pseudo_sigblock(&saved); + if (pseudo_getlock()) { + errno = EBUSY; + sigprocmask(SIG_SETMASK, &saved, NULL); + return -1; + } + if (pseudo_populate_wrappers()) { + int save_errno; + int save_disabled = pseudo_disabled; + /* because clone() doesn't actually continue in this function, we + * can't check the return and fix up environment variables in the + * child. Instead, we have to temporarily do any fixup, then possibly + * undo it later. UGH! + */ + pseudo_debug(1, "client resetting for clone(2) call\n"); + if (real_clone) { + pseudo_setupenv(); + pseudo_client_reset(); + /* call the real syscall */ + rc = (*real_clone)(fn, child_stack, flags, arg, pid, tls, ctid); + + /* if we got here, we're the parent process. And if we changed + * pseudo_disabled because of the environment, now we want to + * bring it back. We can't use the normal path for this in + * pseudo_client_reset() because that would trust the environment + * variable, which was intended only to modify the behavior of + * the child process. + */ + if (save_disabled != pseudo_disabled) { + if (pseudo_disabled) { + pseudo_disabled = 0; + pseudo_magic(); + } else { + pseudo_disabled = 1; + pseudo_antimagic(); + } + } + } else { + /* rc was initialized to the "failure" value */ + pseudo_enosys("clone"); + } + + save_errno = errno; + pseudo_droplock(); + sigprocmask(SIG_SETMASK, &saved, NULL); + pseudo_debug(4, "completed: clone\n"); + errno = save_errno; + return rc; + } else { + pseudo_droplock(); + sigprocmask(SIG_SETMASK, &saved, NULL); + pseudo_debug(4, "completed: clone\n"); + /* rc was initialized to the "failure" value */ + pseudo_enosys("clone"); + + return rc; + } +} + +static int (*real_fork)(void) = NULL; +static int (*real_execlp)(const char *file, const char *arg, ...) = NULL; +static int (*real_execl)(const char *file, const char *arg, ...) = NULL; +static int (*real_execle)(const char *file, const char *arg, ...) = NULL; +static int (*real_clone)(int (*)(void *), void *, int, void *, ...) = NULL; + +static int +wrap_fork(void) { + int rc = -1; + +#include "guts/fork.c" + + return rc; +} diff --git a/templates/wrapfuncs.c b/templates/wrapfuncs.c index 935561b..c0170d9 100644 --- a/templates/wrapfuncs.c +++ b/templates/wrapfuncs.c @@ -34,7 +34,7 @@ ${name}(${decl_args}) { int save_errno; if (antimagic > 0) { if (real_$name) { - ${prologue_call_real} + /* call the real syscall */ ${rc_assign} (*real_${name})(${call_args}); } else { /* rc was initialized to the "failure" value */ diff --git a/templates/wrapfuncs.h b/templates/wrapfuncs.h index 63ba903..8088d09 100644 --- a/templates/wrapfuncs.h +++ b/templates/wrapfuncs.h @@ -8,3 +8,11 @@ /* ${comment} */ static ${type} wrap_${name}(${wrap_args}); static ${type} (*real_${name})(${decl_args}); +@footer +/* special cases: functions with manually-written wrappers */ + +/* int fork(void) */ +static int wrap_fork(void); +static int (*real_fork)(void); +static int wrap_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, ...); +static int (*real_clone)(int (*fn)(void *), void *child_stack, int flags, void *arg, ...); diff --git a/templates/wrapper_table b/templates/wrapper_table index a16dca9..f1a3220 100644 --- a/templates/wrapper_table +++ b/templates/wrapper_table @@ -16,5 +16,16 @@ static struct { (int (*)(void)) wrap_${name} }, @footer + /* special cases: Functions which need manually-coded wrappers */ + { /* int fork(void); */ + "fork", + (int (**)(void)) &real_fork, + (int (*)(void)) wrap_fork + }, + { /* int clone(int (*fn)(void *), void *, int, void, ...); */ + "clone", + (int (**)(void)) &real_clone, + (int (*)(void)) wrap_clone + }, { NULL, NULL, NULL }, }; diff --git a/test/test-forkoff-env_i.sh b/test/test-forkoff-env_i.sh new file mode 100755 index 0000000..1e679aa --- /dev/null +++ b/test/test-forkoff-env_i.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +# Verify normal operation... +uid=`env -i id -u` +gid=`env -i id -g` +if [ $uid -ne 0 -o $gid -ne 0 ]; then + exit 1 +fi + +export PSEUDO_DISABLED=1 +# Verify we dropped OUT of pseudo control, even with env -i +# This checks that env -i replacement functionality still works +# as expected +uid=`env -i id -u` +gid=`env -i id -g` +if [ $uid -eq 0 -o $gid -eq 0 ]; then + exit 1 +fi + +export PSEUDO_DISABLED=1 +# Verify we can change PSEUDO_DISABLED, even with env -i +# This checks that env -i replacement functionality still works +# as expected +uid=`env -i PSEUDO_DISABLED=0 id -u` +gid=`env -i PSEUDO_DISABLED=0 id -g` +if [ $uid -ne 0 -o $gid -ne 0 ]; then + exit 1 +fi + +exit 0 diff --git a/test/test-forkoff.sh b/test/test-forkoff.sh new file mode 100755 index 0000000..13a42a4 --- /dev/null +++ b/test/test-forkoff.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +# Verify normal operation... +uid=`id -u` +gid=`id -g` +if [ $uid -ne 0 -o $gid -ne 0 ]; then + exit 1 +fi + +export PSEUDO_DISABLED=1 +# Verify we dropped OUT of pseudo control +uid=`id -u` +gid=`id -g` +if [ $uid -eq 0 -o $gid -eq 0 ]; then + exit 1 +fi + +exit 0 diff --git a/wrapfuncs.in b/wrapfuncs.in index 17742ac..eb9280e 100644 --- a/wrapfuncs.in +++ b/wrapfuncs.in @@ -32,8 +32,6 @@ int rmdir(const char *path); /* flags=AT_SYMLINK_NOFOLLOW */ int chdir(const char *path); int fchdir(int dirfd); int fcntl(int fd, int cmd, ...{struct flock *lock}); -int fork(void); -int vfork(void); # just so we know the inums of symlinks int symlink(const char *oldname, const char *newpath); /* flags=AT_SYMLINK_NOFOLLOW */ int symlinkat(const char *oldname, int dirfd, const char *newpath); /* flags=AT_SYMLINK_NOFOLLOW */ @@ -88,9 +86,6 @@ char *tmpnam(char *s); int truncate(const char *path, off_t length); int utime(const char *path, const struct utimbuf *buf); int utimes(const char *path, const struct timeval *times); -int execl(const char *file, const char *arg, ...); -int execlp(const char *file, const char *arg, ...); -int execle(const char *file, const char *arg, ...); int execv(const char *file, char *const *argv); int execve(const char *file, char *const *argv, char *const *envp); int execvp(const char *file, char *const *argv); |