X-Git-Url: https://git.ozlabs.org/?p=ccan;a=blobdiff_plain;f=ccan%2Ftal%2Ftal.c;h=dce9d233a076733f636f42fbc2e02f0956cd364a;hp=d7b7b4b22e6ad34ad54e21f6b8bd2683d97c4337;hb=bb480e553f9d204b85a2e42de0be42c7b80eebda;hpb=5bdda8409e559d1b3485639ed43d3b69e4c3fa62 diff --git a/ccan/tal/tal.c b/ccan/tal/tal.c index d7b7b4b2..dce9d233 100644 --- a/ccan/tal/tal.c +++ b/ccan/tal/tal.c @@ -3,12 +3,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include //#define TAL_DEBUG 1 @@ -102,12 +104,14 @@ static struct group *next_group(struct group *group) return list_entry(group->list.n.next, struct group, list.n); } -static bool atexit_set = false; +static bool initialized = false; + /* This means valgrind can see leaks. */ -static void unlink_null(void) +static void tal_cleanup(void) { struct group *i, *next; + /* Unlink null_parent. */ for (i = next_group(&null_parent.c.group); i != &null_parent.c.group; i = next) { @@ -115,6 +119,15 @@ static void unlink_null(void) freefn(i); } null_parent.c.group.first_child = NULL; + + /* Cleanup any taken pointers. */ + take_cleanup(); +} + +/* For allocation failures inside ccan/take */ +static void take_alloc_failed(const void *p) +{ + tal_free(p); } #ifndef NDEBUG @@ -294,6 +307,7 @@ static struct children *add_child_property(struct tal_hdr *parent, init_group_property(&prop->group, prop, child); list_head_init(&prop->group.list); + update_bounds(&prop->group); } return prop; } @@ -316,8 +330,7 @@ static bool add_child(struct tal_hdr *parent, struct tal_hdr *child) children = add_child_property(parent, child); if (!children) return false; - children->group.list.n.next = children->group.list.n.prev - = &children->group.list.n; + list_head_init(&children->group.list); /* Child links to itself. */ child->next = child; @@ -331,9 +344,10 @@ static bool add_child(struct tal_hdr *parent, struct tal_hdr *child) if (unlikely(!group->first_child)) { assert(group == &children->group); /* This hits on first child appended to null parent. */ - if (unlikely(!atexit_set)) { - atexit(unlink_null); - atexit_set = true; + if (unlikely(!initialized)) { + atexit(tal_cleanup); + take_allocfail(take_alloc_failed); + initialized = true; } /* Link group into this child, make it the first one. */ group->hdr.next = child->prop; @@ -417,7 +431,7 @@ static void del_tree(struct tal_hdr *t) freefn(t); } -void *tal_alloc_(const tal_t *ctx, size_t size, bool clear) +void *tal_alloc_(const tal_t *ctx, size_t size, bool clear, const char *label) { struct tal_hdr *child, *parent = debug_tal(to_tal_hdr_or_null(ctx)); @@ -426,7 +440,7 @@ void *tal_alloc_(const tal_t *ctx, size_t size, bool clear) return NULL; if (clear) memset(from_tal_hdr(child), 0, size); - child->prop = NULL; + child->prop = (void *)label; if (!add_child(parent, child)) { freefn(child); return NULL; @@ -455,6 +469,7 @@ static struct tal_hdr *remove_node(struct tal_hdr *t) /* Are we the only one? */ if (prev == t) { + struct prop_hdr *next = (*prop)->next; struct children *c = group->parent_child; /* Is this the group embedded in the child property? */ if (group == &c->group) { @@ -462,9 +477,9 @@ static struct tal_hdr *remove_node(struct tal_hdr *t) } else { /* Empty group, so free it. */ list_del_from(&c->group.list, &group->list.n); - *prop = group->hdr.next; freefn(group); } + *prop = next; return c->parent; } else { /* Move property to next node. */ @@ -481,6 +496,7 @@ static struct tal_hdr *remove_node(struct tal_hdr *t) void tal_free(const tal_t *ctx) { struct tal_hdr *t; + int saved_errno = errno; if (!ctx) return; @@ -488,6 +504,7 @@ void tal_free(const tal_t *ctx) t = debug_tal(to_tal_hdr(ctx)); remove_node(t); del_tree(t); + errno = saved_errno; } void *tal_steal_(const tal_t *new_parent, const tal_t *ctx) @@ -638,7 +655,12 @@ tal_t *tal_next(const tal_t *root, const tal_t *prev) tal_t *tal_parent(const tal_t *ctx) { struct group *group; - struct tal_hdr *t = debug_tal(to_tal_hdr(ctx)); + struct tal_hdr *t; + + if (!ctx) + return NULL; + + t = debug_tal(to_tal_hdr(ctx)); while (!(group = find_property(t, GROUP))) t = t->next; @@ -648,22 +670,29 @@ tal_t *tal_parent(const tal_t *ctx) return from_tal_hdr(group->parent_child->parent); } -void *tal_realloc_(tal_t *ctx, size_t size) +bool tal_resize_(tal_t **ctxp, size_t size) { struct tal_hdr *old_t, *t, **prev; struct group *group; struct children *child; - old_t = debug_tal(to_tal_hdr(ctx)); + old_t = debug_tal(to_tal_hdr(*ctxp)); + + /* Don't hand silly sizes to realloc. */ + if (size >> (CHAR_BIT*sizeof(size) - 1)) { + call_error("Reallocation size overflow"); + return false; + } t = resizefn(old_t, size + sizeof(struct tal_hdr)); if (!t) { call_error("Reallocation failure"); - tal_free(old_t); - return NULL; + return false; } + + /* If it didn't move, we're done! */ if (t == old_t) - return ctx; + return true; update_bounds(t); /* Fix up linked list pointer. */ @@ -683,35 +712,62 @@ void *tal_realloc_(tal_t *ctx, size_t size) assert(child->parent == old_t); child->parent = t; } - - return from_tal_hdr(debug_tal(t)); + *ctxp = from_tal_hdr(debug_tal(t)); + return true; } char *tal_strdup(const tal_t *ctx, const char *p) { - return tal_memdup(ctx, p, strlen(p)+1); + /* We have to let through NULL for take(). */ + return tal_dup(ctx, char, p, p ? strlen(p) + 1: 1, 0); } char *tal_strndup(const tal_t *ctx, const char *p, size_t n) { + size_t len; char *ret; - if (strlen(p) < n) - n = strlen(p); - ret = tal_memdup(ctx, p, n+1); + /* We have to let through NULL for take(). */ + if (likely(p)) { + len = strlen(p); + if (len > n) + len = n; + } else + len = n; + + ret = tal_dup(ctx, char, p, len, 1); if (ret) - ret[n] = '\0'; + ret[len] = '\0'; return ret; } -void *tal_memdup(const tal_t *ctx, const void *p, size_t n) +void *tal_dup_(const tal_t *ctx, const void *p, size_t n, size_t extra, + const char *label) { void *ret; - if (ctx == TAL_TAKE) - return (void *)p; + /* Beware overflow! */ + if (n + extra < n || n + extra + sizeof(struct tal_hdr) < n) { + call_error("dup size overflow"); + if (taken(p)) + tal_free(p); + return NULL; + } - ret = tal_arr(ctx, char, n); + if (taken(p)) { + if (unlikely(!p)) + return NULL; + if (unlikely(!tal_resize_((void **)&p, n + extra))) { + tal_free(p); + return NULL; + } + if (unlikely(!tal_steal(ctx, p))) { + tal_free(p); + return NULL; + } + return (void *)p; + } + ret = tal_alloc_(ctx, n + extra, false, label); if (ret) memcpy(ret, p, n); return ret; @@ -731,15 +787,16 @@ char *tal_asprintf(const tal_t *ctx, const char *fmt, ...) char *tal_vasprintf(const tal_t *ctx, const char *fmt, va_list ap) { - size_t max = strlen(fmt) * 2; + size_t max; char *buf; int ret; - if (ctx == TAL_TAKE) - buf = tal_arr(tal_parent(fmt), char, max); - else - buf = tal_arr(ctx, char, max); + if (!fmt && taken(fmt)) + return NULL; + /* A decent guess to start. */ + max = strlen(fmt) * 2; + buf = tal_arr(ctx, char, max); while (buf) { va_list ap2; @@ -749,9 +806,12 @@ char *tal_vasprintf(const tal_t *ctx, const char *fmt, va_list ap) if (ret < max) break; - buf = tal_resize(buf, max *= 2); + if (!tal_resize(&buf, max *= 2)) { + tal_free(buf); + buf = NULL; + } } - if (ctx == TAL_TAKE) + if (taken(fmt)) tal_free(fmt); return buf; } @@ -887,7 +947,8 @@ static bool check_node(struct group *group, name = (struct name *)p; break; } - if (p != &null_parent.c.hdr && !in_bounds(p)) + if (p != &null_parent.c.hdr && p != &null_parent.c.group.hdr + && !in_bounds(p)) return check_err(t, errorstr, "has bad property pointer");