diff --git a/plugins/sudoers/env.c b/plugins/sudoers/env.c index a5b9f8487..0d2a8bcfa 100644 --- a/plugins/sudoers/env.c +++ b/plugins/sudoers/env.c @@ -239,6 +239,7 @@ env_init(char * const envp[]) if (envp == NULL) { /* Free the old envp we allocated, if any. */ + sudoers_gc_remove(GC_PTR, env.old_envp); free(env.old_envp); /* Reset to initial state but keep a pointer to what we allocated. */ @@ -261,6 +262,7 @@ env_init(char * const envp[]) sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); debug_return_bool(false); } + sudoers_gc_add(GC_PTR, env.envp); #ifdef ENV_DEBUG memset(env.envp, 0, env.env_size * sizeof(char *)); #endif @@ -268,6 +270,7 @@ env_init(char * const envp[]) env.envp[len] = NULL; /* Free the old envp we allocated, if any. */ + sudoers_gc_remove(GC_PTR, env.old_envp); free(env.old_envp); env.old_envp = NULL; } @@ -332,9 +335,13 @@ sudo_putenv_nodebug(char *str, bool dupcheck, bool overwrite) errno = EOVERFLOW; return -1; } + sudoers_gc_remove(GC_PTR, env.envp); nenvp = reallocarray(env.envp, nsize, sizeof(char *)); - if (nenvp == NULL) + if (nenvp == NULL) { + sudoers_gc_add(GC_PTR, env.envp); return -1; + } + sudoers_gc_add(GC_PTR, nenvp); env.envp = nenvp; env.env_size = nsize; #ifdef ENV_DEBUG @@ -893,6 +900,7 @@ rebuild_env(void) didvar = 0; env.env_len = 0; env.env_size = 128; + sudoers_gc_remove(GC_PTR, env.old_envp); free(env.old_envp); env.old_envp = env.envp; env.envp = reallocarray(NULL, env.env_size, sizeof(char *)); @@ -902,6 +910,7 @@ rebuild_env(void) env.env_size = 0; goto bad; } + sudoers_gc_add(GC_PTR, env.envp); #ifdef ENV_DEBUG memset(env.envp, 0, env.env_size * sizeof(char *)); #else diff --git a/plugins/sudoers/gc.c b/plugins/sudoers/gc.c index 3615ebfcb..2b127e873 100644 --- a/plugins/sudoers/gc.c +++ b/plugins/sudoers/gc.c @@ -24,6 +24,7 @@ #include #include +#include #include "sudoers.h" @@ -61,6 +62,7 @@ sudoers_gc_add(enum sudoers_gc_types type, void *v) gc->u.ptr = v; break; case GC_VECTOR: + case GC_EDIT_ARGS: gc->u.vec = v; break; default: @@ -76,16 +78,62 @@ sudoers_gc_add(enum sudoers_gc_types type, void *v) #endif /* NO_LEAKS */ } +bool +sudoers_gc_remove(enum sudoers_gc_types type, void *v) +{ #ifdef NO_LEAKS -static void + struct sudoers_gc_entry *gc, *prev = NULL; + debug_decl(sudoers_gc_remove, SUDOERS_DEBUG_UTIL); + + if (v == NULL) + debug_return_bool(false); + + SLIST_FOREACH(gc, &sudoers_gc_list, entries) { + switch (gc->type) { + case GC_PTR: + if (gc->u.ptr == v) + goto found; + break; + case GC_VECTOR: + case GC_EDIT_ARGS: + if (gc->u.vec == v) + goto found; + break; + default: + sudo_warnx("unexpected garbage type %d in %p", gc->type, gc); + } + prev = gc; + } + /* If this happens, there is a bug in the g/c code. */ + sudo_warnx("%s: unable to find %p, type %d", __func__, v, type); +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + abort(); +#else + debug_return_bool(false); +#endif +found: + if (prev == NULL) + SLIST_REMOVE_HEAD(&sudoers_gc_list, entries); + else + SLIST_REMOVE_AFTER(prev, entries); + free(gc); + + debug_return_bool(true); +#else + return true; +#endif /* NO_LEAKS */ +} + +void sudoers_gc_run(void) { +#ifdef NO_LEAKS struct sudoers_gc_entry *gc; char **cur; debug_decl(sudoers_gc_run, SUDOERS_DEBUG_UTIL); /* Collect garbage. */ - while ((gc = SLIST_FIRST(&sudoers_gc_list))) { + while ((gc = SLIST_FIRST(&sudoers_gc_list)) != NULL) { SLIST_REMOVE_HEAD(&sudoers_gc_list, entries); switch (gc->type) { case GC_PTR: @@ -98,14 +146,24 @@ sudoers_gc_run(void) free(gc->u.vec); free(gc); break; + case GC_EDIT_ARGS: + for (cur = gc->u.vec; *cur != NULL; cur++) { + /* The "--" separates dynamic from non-dynamic args. */ + if (strcmp(*cur, "--") == 0) + break; + free(*cur); + } + free(gc->u.vec); + free(gc); + break; default: sudo_warnx("unexpected garbage type %d", gc->type); } } debug_return; -} #endif /* NO_LEAKS */ +} /* XXX - currently unused */ void diff --git a/plugins/sudoers/policy.c b/plugins/sudoers/policy.c index 44b0d9f03..30e0f052b 100644 --- a/plugins/sudoers/policy.c +++ b/plugins/sudoers/policy.c @@ -607,6 +607,7 @@ sudoers_policy_store_result(bool accepted, char *argv[], char *envp[], command_info = calloc(55, sizeof(char *)); if (command_info == NULL) goto oom; + sudoers_gc_add(GC_VECTOR, command_info); if (safe_cmnd != NULL) { command_info[info_len] = sudo_new_key_val("command", safe_cmnd); @@ -877,10 +878,6 @@ sudoers_policy_store_result(bool accepted, char *argv[], char *envp[], } #endif /* HAVE_SELINUX */ - /* Free on exit; they are not available in the close function. */ - sudoers_gc_add(GC_VECTOR, envp); - sudoers_gc_add(GC_VECTOR, command_info); - /* Fill in exec environment info. */ *(exec_args->argv) = argv; *(exec_args->envp) = envp; diff --git a/plugins/sudoers/regress/fuzz/fuzz_policy.c b/plugins/sudoers/regress/fuzz/fuzz_policy.c index b75fd3bb6..261cc4057 100644 --- a/plugins/sudoers/regress/fuzz/fuzz_policy.c +++ b/plugins/sudoers/regress/fuzz/fuzz_policy.c @@ -334,8 +334,6 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) /* Call policy check function */ sudoers_policy.check_policy(argv.len, argv.entries, env_add.entries, &command_info, &argv_out, &user_env_out, &errstr); - - /* XXX - may need to free argv_out and user_env_out */ break; default: /* fatal or usage error */ @@ -374,7 +372,6 @@ done: free(sudo_user.cmnd_args); free(sudo_user.cmnd_safe); free(sudo_user.cmnd_stat); - /* XXX - sudo_user.env_vars */ #ifdef HAVE_SELINUX free(sudo_user.role); free(sudo_user.type); @@ -388,7 +385,11 @@ done: free(sudo_user.gids); memset(&sudo_user, 0, sizeof(sudo_user)); + /* XXX - Init twice to free everything. */ env_init(NULL); + env_init(NULL); + + sudoers_gc_run(); free_dynamic_array(&plugin_args); free_dynamic_array(&settings); diff --git a/plugins/sudoers/sudoers.c b/plugins/sudoers/sudoers.c index c9fa26480..4f3c68794 100644 --- a/plugins/sudoers/sudoers.c +++ b/plugins/sudoers/sudoers.c @@ -369,7 +369,7 @@ sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[], sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); goto done; } - sudoers_gc_add(GC_VECTOR, NewArgv); + sudoers_gc_add(GC_PTR, NewArgv); NewArgv[0] = user_cmnd; NewArgv[1] = NULL; } else { @@ -380,7 +380,7 @@ sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[], sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory")); goto done; } - sudoers_gc_add(GC_VECTOR, NewArgv); + sudoers_gc_add(GC_PTR, NewArgv); NewArgv++; /* reserve an extra slot for --login */ memcpy(NewArgv, argv, argc * sizeof(char *)); NewArgv[NewArgc] = NULL; @@ -733,7 +733,7 @@ sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[], env_editor ? env_editor : def_editor); goto bad; } - sudoers_gc_add(GC_VECTOR, edit_argv); + sudoers_gc_add(GC_EDIT_ARGS, edit_argv); NewArgv = edit_argv; NewArgc = edit_argc; diff --git a/plugins/sudoers/sudoers.h b/plugins/sudoers/sudoers.h index b8e5019ac..bc0815755 100644 --- a/plugins/sudoers/sudoers.h +++ b/plugins/sudoers/sudoers.h @@ -444,10 +444,13 @@ bool expand_tilde(char **path, const char *user); enum sudoers_gc_types { GC_UNKNOWN, GC_VECTOR, + GC_EDIT_ARGS, GC_PTR }; bool sudoers_gc_add(enum sudoers_gc_types type, void *ptr); +bool sudoers_gc_remove(enum sudoers_gc_types type, void *ptr); void sudoers_gc_init(void); +void sudoers_gc_run(void); /* rcstr.c */ char *rcstr_dup(const char *src);