From 8e932792c917d11545c2953b35159149f7411eca Mon Sep 17 00:00:00 2001 From: Alexander Monakov Date: Sun, 3 Sep 2017 22:12:20 +0300 Subject: overhaul environment functions Rewrite environment access functions to slim down code, fix bugs and avoid invoking undefined behavior. * avoid using int-typed iterators where size_t would be correct; * use strncmp instead of memcmp consistently; * tighten prologues by invoking __strchrnul; * handle NULL environ. putenv: * handle "=value" input via unsetenv too (will return -1/EINVAL); * rewrite and simplify __putenv; fix the leak caused by failure to deallocate entry added by preceding setenv when called from putenv. setenv: * move management of libc-allocated entries to this translation unit, and use no-op weak symbols in putenv/unsetenv; unsetenv: * rewrite; this fixes UB caused by testing a free'd pointer against NULL on entry to subsequent loops. Not changed: Failure to extend allocation tracking array (previously __env_map, now env_alloced) is ignored rather than causing to report -1/ENOMEM to the caller; the worst-case consequence is leaking this allocation when it is removed or replaced in a subsequent environment access. Initially UB in unsetenv was reported by Alexander Cherepanov. Using a weak alias to avoid pulling in malloc via unsetenv was suggested by Rich Felker. --- src/env/putenv.c | 76 ++++++++++++++++++++++++-------------------------------- 1 file changed, 33 insertions(+), 43 deletions(-) (limited to 'src/env/putenv.c') diff --git a/src/env/putenv.c b/src/env/putenv.c index 71530426..fa4a4ddc 100644 --- a/src/env/putenv.c +++ b/src/env/putenv.c @@ -1,58 +1,48 @@ #include #include +#include "libc.h" -extern char **__environ; -char **__env_map; +char *__strchrnul(const char *, int); -int __putenv(char *s, int a) -{ - int i=0, j=0; - char *z = strchr(s, '='); - char **newenv = 0; - char **newmap = 0; - static char **oldenv; +static void dummy(char *old, char *new) {} +weak_alias(dummy, __env_rm_add); - if (!z) return unsetenv(s); - if (z==s) return -1; - for (; __environ[i] && memcmp(s, __environ[i], z-s+1); i++); - if (a) { - if (!__env_map) { - __env_map = calloc(2, sizeof(char *)); - if (__env_map) __env_map[0] = s; - } else { - for (; __env_map[j] && __env_map[j] != __environ[i]; j++); - if (!__env_map[j]) { - newmap = realloc(__env_map, sizeof(char *)*(j+2)); - if (newmap) { - __env_map = newmap; - __env_map[j] = s; - __env_map[j+1] = NULL; - } - } else { - free(__env_map[j]); - __env_map[j] = s; +int __putenv(char *s, size_t l, char *r) +{ + size_t i=0; + if (__environ) { + for (char **e = __environ; *e; e++, i++) + if (!strncmp(s, *e, l+1)) { + char *tmp = *e; + *e = s; + __env_rm_add(tmp, r); + return 0; } - } } - if (!__environ[i]) { - newenv = malloc(sizeof(char *)*(i+2)); - if (!newenv) { - if (a && __env_map) __env_map[j] = 0; - return -1; - } - memcpy(newenv, __environ, sizeof(char *)*i); - newenv[i] = s; - newenv[i+1] = 0; - __environ = newenv; + static char **oldenv; + char **newenv; + if (__environ == oldenv) { + newenv = realloc(oldenv, sizeof *newenv * (i+2)); + if (!newenv) goto oom; + } else { + newenv = malloc(sizeof *newenv * (i+2)); + if (!newenv) goto oom; + if (i) memcpy(newenv, __environ, sizeof *newenv * i); free(oldenv); - oldenv = __environ; } - - __environ[i] = s; + newenv[i] = s; + newenv[i+1] = 0; + __environ = oldenv = newenv; + if (r) __env_rm_add(0, r); return 0; +oom: + free(r); + return -1; } int putenv(char *s) { - return __putenv(s, 0); + size_t l = __strchrnul(s, '=') - s; + if (!l || !s[l]) return unsetenv(s); + return __putenv(s, l, 0); } -- cgit v1.2.1