Source-Changes-HG archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[src/trunk]: src/lib/libc/stdlib Implement mark & sweep garbage collection as...
details: https://anonhg.NetBSD.org/src/rev/ef4eff113cb3
branches: trunk
changeset: 758821:ef4eff113cb3
user: tron <tron%NetBSD.org@localhost>
date: Tue Nov 16 17:23:10 2010 +0000
description:
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:
lib/libc/stdlib/_env.c | 73 +++++++++++++++++++++++++++++++++++++++++--------
1 files changed, 60 insertions(+), 13 deletions(-)
diffs (122 lines):
diff -r a5d5ac2d3832 -r ef4eff113cb3 lib/libc/stdlib/_env.c
--- a/lib/libc/stdlib/_env.c Tue Nov 16 14:25:54 2010 +0000
+++ b/lib/libc/stdlib/_env.c Tue Nov 16 17:23:10 2010 +0000
@@ -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. */
Home |
Main Index |
Thread Index |
Old Index