summaryrefslogtreecommitdiff
path: root/lib/libc/stdlib
diff options
context:
space:
mode:
authortron <tron@NetBSD.org>2010-11-16 17:23:10 +0000
committertron <tron@NetBSD.org>2010-11-16 17:23:10 +0000
commitd0e6f50de394e73fbcb69ef388223b604ceeab8e (patch)
treeeb119729026733e200862ae94cb8a6437e5a2ea7 /lib/libc/stdlib
parentc68f5328ef5a1ed927007caa315b37a0688f7859 (diff)
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;".
Diffstat (limited to 'lib/libc/stdlib')
-rw-r--r--lib/libc/stdlib/_env.c73
1 files changed, 60 insertions, 13 deletions
diff --git a/lib/libc/stdlib/_env.c b/lib/libc/stdlib/_env.c
index 014ce5ddb0c..88f8373e74c 100644
--- a/lib/libc/stdlib/_env.c
+++ b/lib/libc/stdlib/_env.c
@@ -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 @@ __allocenvvar(size_t length)
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 @@ __canoverwriteenvvar(char *envvar, size_t length)
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 @@ __getenvslot(const char *name, size_t l_name, bool allocate)
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 @@ __getenvslot(const char *name, size_t l_name, bool allocate)
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. */