Never call putenv() with data on the stack.
authorGuus Sliepen <guus@tinc-vpn.org>
Sun, 3 May 2015 18:06:12 +0000 (20:06 +0200)
committerGuus Sliepen <guus@tinc-vpn.org>
Sun, 10 Apr 2016 15:42:23 +0000 (17:42 +0200)
Even though we are using putenv() here to remove items from the
environment, there is no guarantee that putenv() doesn't add the
argument to the environment anyway. In that case, we have to make sure
that it doesn't go away. We also don't want a memory leak, so keep a
list of things we unputenv()ed around, so we can reuse things.

Thanks to Poul-Henning Kamp for pointing out this problem.

# Conflicts:
# src/process.c

src/script.c

index 55473b1f95e6ea595fe1664740aceefee8684e6e..5ca56737df8eed573a7bc3de36425422aedfff44 100644 (file)
 #include "script.h"
 #include "xalloc.h"
 
+#ifdef HAVE_PUTENV
+static void unputenv(const char *p) {
+       const char *e = strchr(p, '=');
+       if(!e)
+               return;
+       int len = e - p;
+#ifndef HAVE_UNSETENV
+#ifdef HAVE_MINGW
+       // Windows requires putenv("FOO=") to unset %FOO%
+       len++;
+#endif
+#endif
+       char var[len + 1];
+       strncpy(var, p, len);
+       var[len] = 0;
+#ifdef HAVE_UNSETENV
+       unsetenv(var);
+#else
+       // We must keep what we putenv() around in memory.
+       // To do this without memory leaks, keep things in a list and reuse if possible.
+       static list_t list = {};
+       for list_each(char, data, &list) {
+               if(!strcmp(data, var)) {
+                       putenv(data);
+                       return;
+               }
+       }
+       char *data = xstrdup(var);
+       list_insert_tail(&list, data);
+       putenv(data);
+#endif
+}
+#else
+static void putenv(const char *p) {}
+static void unputenv(const char *p) {}
+#endif
+
 bool execute_script(const char *name, char **envp) {
 #ifdef HAVE_SYSTEM
        char scriptname[PATH_MAX];
@@ -67,12 +104,10 @@ bool execute_script(const char *name, char **envp) {
 
        logger(DEBUG_STATUS, LOG_INFO, "Executing script %s", name);
 
-#ifdef HAVE_PUTENV
        /* Set environment */
 
        for(int i = 0; envp[i]; i++)
                putenv(envp[i]);
-#endif
 
        if(scriptinterpreter)
                xasprintf(&command, "%s \"%s\"", scriptinterpreter, scriptname);
@@ -85,15 +120,8 @@ bool execute_script(const char *name, char **envp) {
 
        /* Unset environment */
 
-       for(int i = 0; envp[i]; i++) {
-               char *e = strchr(envp[i], '=');
-               if(e) {
-                       char p[e - envp[i] + 1];
-                       strncpy(p, envp[i], e - envp[i]);
-                       p[e - envp[i]] = '\0';
-                       putenv(p);
-               }
-       }
+       for(int i = 0; envp[i]; i++)
+               unputenv(envp[i]);
 
        if(status != -1) {
 #ifdef WEXITSTATUS