Module Name: src Committed By: tron Date: Tue Nov 16 17:23:10 UTC 2010
Modified Files: src/lib/libc/stdlib: _env.c Log Message: Implement mark & sweep garbage collection as suggested by Enami Tsugutomo on "current-users" mailing list. Garbage collection is performed if: 1.) We previously allocated memory for the environment array which is no longer used because the application overwrote "environ". 2.) We find a non-NULL pointer in the allocated environment array after the end of the environment. This happens if the applications attempts to clear the environment with something like "environ[0] = NULL;". To generate a diff of this commit: cvs rdiff -u -r1.3 -r1.4 src/lib/libc/stdlib/_env.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/lib/libc/stdlib/_env.c diff -u src/lib/libc/stdlib/_env.c:1.3 src/lib/libc/stdlib/_env.c:1.4 --- src/lib/libc/stdlib/_env.c:1.3 Tue Nov 16 03:02:20 2010 +++ src/lib/libc/stdlib/_env.c Tue Nov 16 17:23:10 2010 @@ -1,4 +1,4 @@ -/* $NetBSD: _env.c,v 1.3 2010/11/16 03:02:20 enami Exp $ */ +/* $NetBSD: _env.c,v 1.4 2010/11/16 17:23:10 tron Exp $ */ /*- * Copyright (c) 2010 The NetBSD Foundation, Inc. @@ -29,6 +29,11 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#include <sys/cdefs.h> +#if defined(LIBC_SCCS) && !defined(lint) +__RCSID("$NetBSD: _env.c,v 1.4 2010/11/16 17:23:10 tron Exp $"); +#endif /* LIBC_SCCS and not lint */ + #include <sys/rbtree.h> #include <assert.h> @@ -49,6 +54,7 @@ typedef struct { rb_node_t rb_node; size_t length; + uint8_t marker; char data[]; } env_node_t; @@ -168,6 +174,7 @@ node = malloc(sizeof(*node) + length); if (node != NULL) { node->length = length; + node->marker = 0; rb_tree_insert_node(&env_tree, node); return node->data; } else { @@ -191,6 +198,50 @@ return (node != NULL && length <= node->length); } +/* Free all allocated environment variables that are no longer used. */ +static void +__scrubenv(void) +{ + static uint8_t marker = 0; + size_t num_entries; + env_node_t *node, *next; + + while (++marker == 0); + + /* Mark all nodes which are currently used. */ + for (num_entries = 0; environ[num_entries] != NULL; num_entries++) { + node = rb_tree_find_node(&env_tree, environ[num_entries]); + if (node != NULL) + node->marker = marker; + } + + /* Free all nodes which are currently not used. */ + for (node = RB_TREE_MIN(&env_tree); node != NULL; node = next) { + next = rb_tree_iterate(&env_tree, node, RB_DIR_RIGHT); + + if (node->marker != marker) { + rb_tree_remove_node(&env_tree, node); + free(node); + } + } + + /* Deal with the environment array itself. */ + if (environ == allocated_environ) { + /* Clear out spurious entries in the environment. */ + (void)memset(&environ[num_entries + 1], 0, + (allocated_environ_size - num_entries - 1) * + sizeof(*environ)); + } else { + /* + * The environment array was not allocated by "libc". + * Free our array if we allocated one. + */ + free(allocated_environ); + allocated_environ = NULL; + allocated_environ_size = 0; + } +} + /* * Get a (new) slot in the environment. This function must be called with * the environment write locked. @@ -201,6 +252,10 @@ size_t new_size, num_entries, required_size; char **new_environ; + /* Does the environ need scrubbing? */ + if (environ != allocated_environ && allocated_environ != NULL) + __scrubenv(); + /* Search for an existing environment variable of the given name. */ num_entries = 0; while (environ[num_entries] != NULL) { @@ -220,18 +275,10 @@ required_size = num_entries + 1; if (environ == allocated_environ && required_size < allocated_environ_size) { - size_t offset; - - /* - * Scrub the environment if somebody erased its contents by - * e.g. setting environ[0] to NULL. - */ - offset = required_size; - while (offset < allocated_environ_size && - environ[offset] != NULL) { - __freeenvvar(environ[offset]); - environ[offset] = NULL; - offset++; + /* Does the environment need scrubbing? */ + if (required_size < allocated_environ_size && + allocated_environ[required_size] != NULL) { + __scrubenv(); } /* Return a free slot. */