Fixes for environment variable handling on GNO:

* Push/pop environment to make sure it is isolated from our parents and children.
* Make all environment vars (and shell vars) case-insensitive, consistent with GNO's internal handling of environment vars.
* Wrap putenv and unsetenv to make sure they are called with lower-case variable names, which is necessary to maintain consistency between the environ array and the kernel's internal representation of variables.
This commit is contained in:
Stephen Heumann 2014-11-20 13:21:23 -06:00
parent 97eb1defae
commit 4d60cd1043
4 changed files with 77 additions and 18 deletions

View File

@ -481,6 +481,13 @@ off_t xlseek(int fd, off_t offset, int whence) FAST_FUNC;
int xmkstemp(char *template) FAST_FUNC;
off_t fdlength(int fd) FAST_FUNC;
#ifdef __GNO__
void unsetenv_wrapper(const char *var);
int putenv_wrapper(char *string);
# define unsetenv unsetenv_wrapper
# define putenv putenv_wrapper
#endif
uoff_t FAST_FUNC get_volume_size_in_bytes(int fd,
const char *override,
unsigned override_units,

View File

@ -63,6 +63,7 @@ pid_t vfork_and_run(void (*fn)(void*) NORETURN, void *arg) {
*/
long oldmask;
bool environPushed;
sig_t prev_alarm_sig;
pid_t pid;
kvmt *kvm_context;
@ -72,6 +73,9 @@ pid_t vfork_and_run(void (*fn)(void*) NORETURN, void *arg) {
/* Block all signals for now */
oldmask = sigblock(-1);
/* Isolate child process's environment from parent */
environPushed = !environPush();
pid = fork2(fork_thunk, 1024, 0, forked_child_name,
(sizeof(fn) + sizeof(arg) + sizeof(oldmask) + 1) / 2,
fn, arg, oldmask);
@ -106,6 +110,8 @@ pid_t vfork_and_run(void (*fn)(void*) NORETURN, void *arg) {
ret:
sigsetmask(oldmask);
if (environPushed)
environPop();
return pid;
}

View File

@ -335,12 +335,6 @@ char* FAST_FUNC xasprintf(const char *format, ...)
return string_ptr;
}
void FAST_FUNC xsetenv(const char *key, const char *value)
{
if (setenv(key, value, 1))
bb_error_msg_and_die(bb_msg_memory_exhausted);
}
/* Handles "VAR=VAL" strings, even those which are part of environ
* _right now_
*/
@ -363,11 +357,49 @@ void FAST_FUNC bb_unsetenv(const char *var)
free(tp);
}
void FAST_FUNC bb_unsetenv_and_free(char *var)
#ifdef __GNO__
/* We wrap putenv and unsetenv functions to make them fully case-insensitive.
* The libc versions are case-insensitive with respect to the internal
* variable representation maintained by the kernel, but case-sensitive with
* respect to the environ array, with the result that the two representations
* may get out of sync. We avoid this by always converting variable names to
* lower case ourselves. We'd also need to wrap setenv if it were used.
*/
# undef putenv
int putenv_wrapper(char *string)
{
bb_unsetenv(var);
free(var);
int name_len, i;
int retval;
char *string_lowercase = xstrdup(string);
name_len = strcspn(string, "=");
for (i = 0; i < name_len; i++) {
string_lowercase[i] = tolower(string_lowercase[i]);
}
/* We rely on putenv to copy its input string and not store the original,
* which GNO does but POSIX-standard systems don't. */
retval = putenv(string_lowercase);
free(string_lowercase);
return retval;
}
# define putenv putenv_wrapper
# undef unsetenv
void unsetenv_wrapper(const char *var)
{
char *c;
char *var_lowercase = xstrdup(var);
for (c = var_lowercase; *c != 0; c++) {
*c = tolower(*c);
}
unsetenv(var_lowercase);
free(var_lowercase);
}
# define unsetenv unsetenv_wrapper
#endif
// Die with an error message if we can't set gid. (Because resource limits may
// limit this user to a given number of processes, and if that fills up the

View File

@ -375,6 +375,17 @@
#define SPECIAL_VAR_SYMBOL 3
/* Use case-insensitive variable name comparisons on GNO, consistent with
* GNO's internal handling of environment variables.
*/
#ifdef __GNO__
# define varnamecmp strcasecmp
# define varnamencmp strncasecmp
#else
# define varnamecmp strcmp
# define varnamencmp strncmp
#endif
struct variable;
static const char hush_version_str[] ALIGN1 = "HUSH_VERSION="BB_VER;
@ -1860,7 +1871,7 @@ static struct variable **get_ptr_to_local_var(const char *name, unsigned len)
pp = &G.top_var;
while ((cur = *pp) != NULL) {
if (strncmp(cur->varstr, name, len) == 0 && cur->varstr[len] == '=')
if (varnamencmp(cur->varstr, name, len) == 0 && cur->varstr[len] == '=')
return pp;
pp = &cur->next;
}
@ -1876,7 +1887,7 @@ static const char* FAST_FUNC get_local_var_value(const char *name)
char **cpp = G.expanded_assignments;
while (*cpp) {
char *cp = *cpp;
if (strncmp(cp, name, len) == 0 && cp[len] == '=')
if (varnamencmp(cp, name, len) == 0 && cp[len] == '=')
return cp + len + 1;
cpp++;
}
@ -1886,11 +1897,11 @@ static const char* FAST_FUNC get_local_var_value(const char *name)
if (vpp)
return (*vpp)->varstr + len + 1;
if (strcmp(name, "PPID") == 0)
if (varnamecmp(name, "PPID") == 0)
return utoa(G.root_ppid);
// bash compat: UID? EUID?
#if ENABLE_HUSH_RANDOM_SUPPORT
if (strcmp(name, "RANDOM") == 0)
if (varnamecmp(name, "RANDOM") == 0)
return utoa(next_random(&G.random_gen));
#endif
return NULL;
@ -1933,7 +1944,7 @@ static int set_local_var(char *str, int flg_export, int local_lvl, int flg_read_
name_len = eq_sign - str + 1; /* including '=' */
var_pp = &G.top_var;
while ((cur = *var_pp) != NULL) {
if (strncmp(cur->varstr, str, name_len) != 0) {
if (varnamencmp(cur->varstr, str, name_len) != 0) {
var_pp = &cur->next;
continue;
}
@ -2010,7 +2021,7 @@ static int set_local_var(char *str, int flg_export, int local_lvl, int flg_read_
exp:
if (flg_export == 1)
cur->flg_export = 1;
if (name_len == 4 && cur->varstr[0] == 'P' && cur->varstr[1] == 'S')
if (name_len == 4 && varnamencmp(cur->varstr, "PS", 2) == 0)
cmdedit_update_prompt();
if (cur->flg_export) {
if (flg_export == -1) {
@ -2040,7 +2051,7 @@ static int unset_local_var_len(const char *name, int name_len)
return EXIT_SUCCESS;
var_pp = &G.top_var;
while ((cur = *var_pp) != NULL) {
if (strncmp(cur->varstr, name, name_len) == 0 && cur->varstr[name_len] == '=') {
if (varnamencmp(cur->varstr, name, name_len) == 0 && cur->varstr[name_len] == '=') {
if (cur->flg_read_only) {
bb_error_msg("%s: readonly variable", name);
return EXIT_FAILURE;
@ -2048,7 +2059,7 @@ static int unset_local_var_len(const char *name, int name_len)
*var_pp = cur->next;
debug_printf_env(("%s: unsetenv '%s'\n", "unset_local_var_len", cur->varstr));
bb_unsetenv(cur->varstr);
if (name_len == 3 && cur->varstr[0] == 'P' && cur->varstr[1] == 'S')
if (name_len == 3 && varnamencmp(cur->varstr, "PS", 2) == 0)
cmdedit_update_prompt();
if (!cur->max_len)
free(cur->varstr);
@ -5869,7 +5880,7 @@ static void re_execute_shell(char ***to_free, const char *s,
*pp++ = (char *) G.argv0_for_re_execing;
*pp++ = param_buf;
for (cur = G.top_var; cur; cur = cur->next) {
if (strcmp(cur->varstr, hush_version_str) == 0)
if (varnamecmp(cur->varstr, hush_version_str) == 0)
continue;
if (cur->flg_read_only) {
*pp++ = (char *) "-R";
@ -8081,6 +8092,9 @@ int hush_main(int argc, char **argv)
INIT_G();
hush_exec_path = get_exec_path();
#ifdef __GNO__
/* Make sure our environment is isolated from the parent process's */
if (environPush() == 0)
atexit(environPop);
environInit();
#endif
if (EXIT_SUCCESS != 0) /* if EXIT_SUCCESS == 0, it is already done */