From 6cf28d55cd7fd76dcd54e5052d6f8df2b39dca8f Mon Sep 17 00:00:00 2001 From: Matthew Flatt Date: Fri, 27 Feb 2015 08:17:18 -0700 Subject: [PATCH] fix memory-management problem in putenv Problem reported by Sergey Pinaev: free(oldbuffer) should be called AFTER putenv(buffer). Also, respond correctly to an (unlikely) putenv() failure. --- racket/src/racket/src/string.c | 47 +++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/racket/src/racket/src/string.c b/racket/src/racket/src/string.c index a123f99dce..8df08b4339 100644 --- a/racket/src/racket/src/string.c +++ b/racket/src/racket/src/string.c @@ -2367,12 +2367,12 @@ static Scheme_Object *sch_getenv(int argc, Scheme_Object *argv[]) #ifndef DOS_FILE_SYSTEM static int sch_unix_putenv(const char *var, const char *val, const intptr_t varlen, const intptr_t vallen) { char *buffer; - intptr_t total_length; + intptr_t total_length, r; total_length = varlen + vallen + 2; if (val) { #ifdef MZ_PRECISE_GC - /* Can't put moveable string into array. */ + /* Can't put movable string into environ array */ buffer = malloc(total_length); #else buffer = (char *)scheme_malloc_atomic(total_length); @@ -2385,27 +2385,38 @@ static int sch_unix_putenv(const char *var, const char *val, const intptr_t varl buffer = NULL; } -#ifdef MZ_PRECISE_GC - { - /* Free old, if in table: */ - char *oldbuffer; - oldbuffer = (char *)putenv_str_table_get(var); - if (oldbuffer) - free(oldbuffer); - } -#endif - - /* if precise the buffer needs to be remembered so it can be freed */ - /* if not precise the buffer needs to be rooted so it doesn't get collected prematurely */ - putenv_str_table_put_name(var, buffer); - if (buffer) - return putenv(buffer); + r = putenv(buffer); else { /* on some platforms, unsetenv() returns void */ unsetenv(var); - return 0; + r = 0; } + + if (!r) { +#ifdef MZ_PRECISE_GC + char *oldbuffer; + + /* Will free old, if in table: */ + oldbuffer = (char *)putenv_str_table_get(var); +#endif + + /* If precise GC, the buffer needs to be remembered so it can be freed; + otherwise, the buffer needs to be referenced so it doesn't get GCed */ + putenv_str_table_put_name(var, buffer); + +#ifdef MZ_PRECISE_GC + if (oldbuffer) + free(oldbuffer); +#endif + } else { +#ifdef MZ_PRECISE_GC + if (buffer) + free(buffer); +#endif + } + + return r; } #endif