summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKaz Kylheku <kaz@kylheku.com>2009-11-14 10:00:37 -0800
committerKaz Kylheku <kaz@kylheku.com>2009-11-14 10:00:37 -0800
commit9a5d3288441c27c9adef949bdf09161446915dec (patch)
tree1a3049fdbeb713d6e75688961004bf74647c7d71
parentfe2f8d7123f22cf7755330ac8b289bc3cd838af3 (diff)
downloadtxr-9a5d3288441c27c9adef949bdf09161446915dec.tar.gz
txr-9a5d3288441c27c9adef949bdf09161446915dec.tar.bz2
txr-9a5d3288441c27c9adef949bdf09161446915dec.zip
Fixes for bug 28086. When constructing a cobj, whose associated
C structure contains obj_t * references, we should initialize that C structure after allocating the cobj. If we initialize the structure first, it may end up having the /only/ references to the objects. In that case, the objects are invisible to the garbage collector. The subsquent allocation of the cobj itself then may invoke gc which will turn these objects into dust. The result is a cobj which contains a handle structure that contains references to free objects. The fix is to allocate the handle structure, then the cobj which is associated with that handle, and then initialize the handle, at which point it is okay if the handle has the only references to some objects. Care must be taken not to let a cobj escape with a partially initialized handle structure, and not to trigger gc between allocating the cobj, and initializing the fields.
-rw-r--r--ChangeLog25
-rw-r--r--hash.c11
-rw-r--r--stream.c12
3 files changed, 41 insertions, 7 deletions
diff --git a/ChangeLog b/ChangeLog
index c3b8dc4d..32a9adca 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,28 @@
+2009-11-14 Kaz Kylheku <kkylheku@gmail.com>
+
+ Fixes for bug 28086. When constructing a cobj, whose associated
+ C structure contains obj_t * references, we should initialize
+ that C structure after allocating the cobj. If we initialize
+ the structure first, it may end up having the /only/ references
+ to the objects. In that case, the objects are invisible to the
+ garbage collector. The subsquent allocation of the cobj itself
+ then may invoke gc which will turn these objects into dust.
+ The result is a cobj which contains a handle structure that
+ contains references to free objects. The fix is to allocate
+ the handle structure, then the cobj which is associated with
+ that handle, and then initialize the handle, at which point it
+ is okay if the handle has the only references to some objects.
+ Care must be taken not to let a cobj escape with a partially
+ initialized handle structure, and not to trigger gc between
+ allocating the cobj, and initializing the fields.
+
+ * hash.c (make_hash): Fix cobj construction order.
+
+ * stream.c (make_stdio_stream): Fix cobj construction order.
+ (make_pipe_stream): Fix cobj construction order. Also
+ noticed and fixed a bug: h->descr field not being initialized
+ in the currently enabled BROKEN_POPEN_GETWC variant of the code.
+
2009-11-13 Kaz Kylheku <kkylheku@gmail.com>
New testcase which does some UTF-8 scanning, Unicode regexes,
diff --git a/hash.c b/hash.c
index 86ceb340..0b7c099d 100644
--- a/hash.c
+++ b/hash.c
@@ -223,11 +223,16 @@ obj_t *make_hash(obj_t *weak_keys, obj_t *weak_vals)
int flags = ((weak_vals != nil) << 1) | (weak_keys != nil);
struct hash *h = (struct hash *) chk_malloc(sizeof *h);
obj_t *mod = num(256);
+ obj_t *table = vector(mod);
+ obj_t *hash = cobj((void *) h, hash_t, &hash_ops);
+
+ vec_set_fill(table, mod);
+
h->flags = (hash_flags_t) flags;
h->modulus = c_num(mod);
- h->table = vector(mod);
- vec_set_fill(h->table, mod);
- return cobj((void *) h, hash_t, &hash_ops);
+ h->table = table;
+
+ return hash;
}
obj_t **gethash_l(obj_t *hash, obj_t *key)
diff --git a/stream.c b/stream.c
index e2618a03..282a5b94 100644
--- a/stream.c
+++ b/stream.c
@@ -582,14 +582,16 @@ static struct strm_ops dir_ops = {
obj_t *make_stdio_stream(FILE *f, obj_t *descr, obj_t *input, obj_t *output)
{
struct stdio_handle *h = (struct stdio_handle *) chk_malloc(sizeof *h);
+ obj_t *stream = cobj((void *) h, stream_t, &stdio_ops.cobj_ops);
h->f = f;
h->descr = descr;
- return cobj((void *) h, stream_t, &stdio_ops.cobj_ops);
+ return stream;
}
obj_t *make_pipe_stream(FILE *f, obj_t *descr, obj_t *input, obj_t *output)
{
struct stdio_handle *h = (struct stdio_handle *) chk_malloc(sizeof *h);
+ obj_t *stream = cobj((void *) h, stream_t, &pipe_ops.cobj_ops);
#ifdef BROKEN_POPEN_GETWC
int dup_fd = dup(fileno(f));
FILE *dup_f = (dup_fd != -1) ? fdopen(dup_fd, output ? "w" : "r") : 0;
@@ -600,7 +602,9 @@ obj_t *make_pipe_stream(FILE *f, obj_t *descr, obj_t *input, obj_t *output)
fclose(dup_f);
else if (dup_fd != -1)
close(dup_fd);
- free(h);
+ /* Don't leave h uninitialized; it is gc-reachable through stream cobj. */
+ h->f = h->f_orig_pipe = 0;
+ h->descr = descr;
uw_throwf(process_error, L"unable to create pipe ~a: ~a/~s", descr,
num(error), string_utf8(strerror(error)), nao);
}
@@ -609,9 +613,9 @@ obj_t *make_pipe_stream(FILE *f, obj_t *descr, obj_t *input, obj_t *output)
h->f = dup_f;
#else
h->f = f;
- h->descr = descr;
#endif
- return cobj((void *) h, stream_t, &pipe_ops.cobj_ops);
+ h->descr = descr;
+ return stream;
}
obj_t *make_string_input_stream(obj_t *string)