[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH v3 12/12] xenstore: handle memory allocation failures in xenstored
Check for failures when allocating new memory in xenstored. Unfortunately there are several conditions which might render those checks void: It might be impossible to send an "ENOMEM" response as this requires allocating some memory. Signed-off-by: Juergen Gross <jgross@xxxxxxxx> --- tools/xenstore/xenstored_core.c | 185 ++++++++++++++++++++++++++------- tools/xenstore/xenstored_domain.c | 10 ++ tools/xenstore/xenstored_transaction.c | 20 ++++ tools/xenstore/xenstored_watch.c | 12 +++ 4 files changed, 187 insertions(+), 40 deletions(-) diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c index f89a636..d880afa 100644 --- a/tools/xenstore/xenstored_core.c +++ b/tools/xenstore/xenstored_core.c @@ -149,8 +149,10 @@ void trace(const char *fmt, ...) va_start(arglist, fmt); str = talloc_vasprintf(NULL, fmt, arglist); va_end(arglist); - dummy = write(tracefd, str, strlen(str)); - talloc_free(str); + if (str) { + dummy = write(tracefd, str, strlen(str)); + talloc_free(str); + } } static void trace_io(const struct connection *conn, @@ -392,7 +394,16 @@ static struct node *read_node(struct connection *conn, const void *ctx, } node = talloc(ctx, struct node); + if (!node) { + errno = ENOMEM; + return NULL; + } node->name = talloc_strdup(node, name); + if (!node->name) { + talloc_free(node); + errno = ENOMEM; + return NULL; + } node->parent = NULL; node->tdb = tdb_context(conn); talloc_steal(node, data.dptr); @@ -490,35 +501,46 @@ static enum xs_perm_type perm_for_conn(struct connection *conn, */ static char *get_parent(const void *ctx, const char *node) { + char *parent; char *slash = strrchr(node + 1, '/'); - if (!slash) - return talloc_strdup(ctx, "/"); - return talloc_asprintf(ctx, "%.*s", (int)(slash - node), node); + + parent = slash ? talloc_asprintf(ctx, "%.*s", (int)(slash - node), node) + : talloc_strdup(ctx, "/"); + if (!parent) + errno = ENOMEM; + + return parent; } /* * What do parents say? * Temporary memory allocations are done with ctx. */ -static enum xs_perm_type ask_parents(struct connection *conn, const void *ctx, - const char *name) +static int ask_parents(struct connection *conn, const void *ctx, + const char *name, enum xs_perm_type *perm) { struct node *node; do { name = get_parent(ctx, name); + if (!name) + return errno; node = read_node(conn, ctx, name); if (node) break; + if (errno == ENOMEM) + return errno; } while (!streq(name, "/")); /* No permission at root? We're in trouble. */ if (!node) { corrupt(conn, "No permissions file at root"); - return XS_PERM_NONE; + *perm = XS_PERM_NONE; + return 0; } - return perm_for_conn(conn, node->perms, node->num_perms); + *perm = perm_for_conn(conn, node->perms, node->num_perms); + return 0; } /* @@ -532,11 +554,15 @@ static int errno_from_parents(struct connection *conn, const void *ctx, const char *node, int errnum, enum xs_perm_type perm) { + enum xs_perm_type parent_perm = XS_PERM_NONE; + /* We always tell them about memory failures. */ if (errnum == ENOMEM) return errnum; - if (ask_parents(conn, ctx, node) & perm) + if (ask_parents(conn, ctx, node, &parent_perm)) + return errno; + if (parent_perm & perm) return errnum; return EACCES; } @@ -566,7 +592,7 @@ struct node *get_node(struct connection *conn, } } /* Clean up errno if they weren't supposed to know. */ - if (!node) + if (!node && errno != ENOMEM) errno = errno_from_parents(conn, ctx, name, errno, perm); return node; } @@ -656,11 +682,29 @@ void send_reply(struct connection *conn, enum xsd_sockmsg_type type, } else { /* Message is a child of the connection for auto-cleanup. */ bdata = new_buffer(conn); + + /* + * Allocation failure here is unfortunate: we have no way to + * tell anybody about it. + */ + if (!bdata) + return; } if (len <= DEFAULT_BUFFER_SIZE) bdata->buffer = bdata->default_buffer; else bdata->buffer = talloc_array(bdata, char, len); + if (!bdata->buffer) { + if (type == XS_WATCH_EVENT) { + /* Same as above: no way to tell someone. */ + talloc_free(bdata); + return; + } + /* re-establish request buffer for sending ENOMEM. */ + conn->in = bdata; + send_error(conn, ENOMEM); + return; + } /* Update relevant header fields and fill in the message body. */ bdata->hdr.msg.type = type; @@ -669,6 +713,8 @@ void send_reply(struct connection *conn, enum xsd_sockmsg_type type, /* Queue for later transmission. */ list_add_tail(&bdata->list, &conn->out_list); + + return; } /* Some routines (write, mkdir, etc) just need a non-error return */ @@ -730,6 +776,8 @@ static char *perms_to_strings(const void *ctx, strings = talloc_realloc(ctx, strings, char, *len + strlen(buffer) + 1); + if (!strings) + return NULL; strcpy(strings + *len, buffer); *len += strlen(buffer) + 1; } @@ -876,6 +924,9 @@ static struct node *construct_node(struct connection *conn, const void *ctx, struct node *parent, *node; char *children, *parentname = get_parent(ctx, name); + if (!parentname) + return NULL; + /* If parent doesn't exist, create it. */ parent = read_node(conn, parentname, parentname); if (!parent) @@ -1086,9 +1137,15 @@ static int _rm(struct connection *conn, const void *ctx, struct node *node, /* Delete from parent first, then if we crash, the worst that can happen is the child will continue to take up space, but will otherwise be unreachable. */ - struct node *parent = read_node(conn, ctx, get_parent(ctx, name)); + struct node *parent; + char *parentname = get_parent(ctx, name); + + if (!parentname) + return errno; + + parent = read_node(conn, ctx, parentname); if (!parent) - return EINVAL; + return (errno == ENOMEM) ? ENOMEM : EINVAL; if (!delete_child(conn, parent, basename(name))) return EINVAL; @@ -1114,19 +1171,24 @@ static int do_rm(struct connection *conn, struct buffered_data *in) struct node *node; int ret; char *name; + char *parentname; node = get_node_canonicalized(conn, in, onearg(in), &name, XS_PERM_WRITE); if (!node) { /* Didn't exist already? Fine, if parent exists. */ if (errno == ENOENT) { - node = read_node(conn, in, get_parent(in, name)); + parentname = get_parent(in, name); + if (!parentname) + return errno; + node = read_node(conn, in, parentname); if (node) { send_ack(conn, XS_RM); return 0; } /* Restore errno, just in case. */ - errno = ENOENT; + if (errno != ENOMEM) + errno = ENOENT; } return errno; } @@ -1186,6 +1248,8 @@ static int do_set_perms(struct connection *conn, struct buffered_data *in) num--; perms = talloc_array(node, struct xs_permissions, num); + if (!perms) + return ENOMEM; if (!xs_strings_to_perms(perms, num, permstr)) return errno; @@ -1319,7 +1383,7 @@ static void handle_input(struct connection *conn) if (!conn->in) { conn->in = new_buffer(conn); - /* In case of no memory just try it next time again. */ + /* In case of no memory just try it again next time. */ if (!conn->in) return; } @@ -1327,26 +1391,29 @@ static void handle_input(struct connection *conn) /* Not finished header yet? */ if (in->inhdr) { - bytes = conn->read(conn, in->hdr.raw + in->used, - sizeof(in->hdr) - in->used); - if (bytes < 0) - goto bad_client; - in->used += bytes; - if (in->used != sizeof(in->hdr)) - return; + if (in->used != sizeof(in->hdr)) { + bytes = conn->read(conn, in->hdr.raw + in->used, + sizeof(in->hdr) - in->used); + if (bytes < 0) + goto bad_client; + in->used += bytes; + if (in->used != sizeof(in->hdr)) + return; - if (in->hdr.msg.len > XENSTORE_PAYLOAD_MAX) { - syslog(LOG_ERR, "Client tried to feed us %i", - in->hdr.msg.len); - goto bad_client; + if (in->hdr.msg.len > XENSTORE_PAYLOAD_MAX) { + syslog(LOG_ERR, "Client tried to feed us %i", + in->hdr.msg.len); + goto bad_client; + } } if (in->hdr.msg.len <= DEFAULT_BUFFER_SIZE) in->buffer = in->default_buffer; else in->buffer = talloc_array(in, char, in->hdr.msg.len); + /* In case of no memory just try it again next time. */ if (!in->buffer) - goto bad_client; + return; in->used = 0; in->inhdr = false; } @@ -1469,6 +1536,9 @@ static void manual_node(const char *name, const char *child) struct xs_permissions perms = { .id = 0, .perms = XS_PERM_NONE }; node = talloc_zero(NULL, struct node); + if (!node) + barf_perror("Could not allocate initial node %s", name); + node->name = name; node->perms = &perms; node->num_perms = 1; @@ -1506,6 +1576,8 @@ static void setup_structure(void) { char *tdbname; tdbname = talloc_strdup(talloc_autofree_context(), xs_daemon_tdb()); + if (!tdbname) + barf_perror("Could not create tdbname"); if (!(tdb_flags & TDB_INTERNAL)) tdb_ctx = tdb_open_ex(tdbname, 0, tdb_flags, O_RDWR, 0, @@ -1584,11 +1656,14 @@ static char *child_name(const char *s1, const char *s2) } -static void remember_string(struct hashtable *hash, const char *str) +static int remember_string(struct hashtable *hash, const char *str) { char *k = malloc(strlen(str) + 1); + + if (!k) + return 0; strcpy(k, str); - hashtable_insert(hash, k, (void *)1); + return hashtable_insert(hash, k, (void *)1); } @@ -1615,14 +1690,23 @@ static void check_store_(const char *name, struct hashtable *reachable) struct hashtable * children = create_hashtable(16, hash_from_key_fn, keys_equal_fn); - remember_string(reachable, name); + if (!remember_string(reachable, name)) { + hashtable_destroy(children, 0); + log("check_store: ENOMEM"); + return; + } while (i < node->childlen) { + struct node *childnode; size_t childlen = strlen(node->children + i); char * childname = child_name(node->name, node->children + i); - struct node *childnode = read_node(NULL, childname, - childname); + + if (!childname) { + log("check_store: ENOMEM"); + break; + } + childnode = read_node(NULL, childname, childname); if (childnode) { if (hashtable_search(children, childname)) { @@ -1636,11 +1720,16 @@ static void check_store_(const char *name, struct hashtable *reachable) } } else { - remember_string(children, childname); + if (!remember_string(children, + childname)) { + log("check_store: ENOMEM"); + talloc_free(childnode); + talloc_free(childname); + break; + } check_store_(childname, reachable); } - } - else { + } else if (errno != ENOMEM) { log("check_store: No child '%s' found!\n", childname); @@ -1648,7 +1737,8 @@ static void check_store_(const char *name, struct hashtable *reachable) remove_child_entry(NULL, node, i); i -= childlen + 1; } - } + } else + log("check_store: ENOMEM"); talloc_free(childnode); talloc_free(childname); @@ -1658,14 +1748,15 @@ static void check_store_(const char *name, struct hashtable *reachable) hashtable_destroy(children, 0 /* Don't free values (they are all (void *)1) */); talloc_free(node); - } - else { + } else if (errno != ENOMEM) { /* Impossible, because no database should ever be without the root, and otherwise, we've just checked in our caller (which made a recursive call to get here). */ log("check_store: No child '%s' found: impossible!", name); - } + } else + log("check_store: ENOMEM"); + } @@ -1678,6 +1769,11 @@ static int clean_store_(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA val, struct hashtable *reachable = private; char * name = talloc_strndup(NULL, key.dptr, key.dsize); + if (!name) { + log("clean_store: ENOMEM"); + return 1; + } + if (!hashtable_search(reachable, name)) { log("clean_store: '%s' is orphaned!", name); if (recovery) { @@ -1707,6 +1803,11 @@ static void check_store(void) struct hashtable * reachable = create_hashtable(16, hash_from_key_fn, keys_equal_fn); + if (!reachable) { + log("check_store: ENOMEM"); + return; + } + log("Checking store ..."); check_store_(root, reachable); clean_store(reachable); @@ -1763,10 +1864,14 @@ static void init_sockets(int **psock, int **pro_sock) /* Create sockets for them to listen to. */ *psock = sock = talloc(talloc_autofree_context(), int); + if (!sock) + barf_perror("No memory when creating sockets"); *sock = socket(PF_UNIX, SOCK_STREAM, 0); if (*sock < 0) barf_perror("Could not create socket"); *pro_sock = ro_sock = talloc(talloc_autofree_context(), int); + if (!ro_sock) + barf_perror("No memory when creating sockets"); *ro_sock = socket(PF_UNIX, SOCK_STREAM, 0); if (*ro_sock < 0) barf_perror("Could not create socket"); diff --git a/tools/xenstore/xenstored_domain.c b/tools/xenstore/xenstored_domain.c index fefed5e..5322280 100644 --- a/tools/xenstore/xenstored_domain.c +++ b/tools/xenstore/xenstored_domain.c @@ -279,10 +279,15 @@ static struct domain *new_domain(void *context, unsigned int domid, int rc; domain = talloc(context, struct domain); + if (!domain) + return NULL; + domain->port = 0; domain->shutdown = 0; domain->domid = domid; domain->path = talloc_domain_path(domain, domid); + if (!domain->path) + return NULL; list_add(&domain->list, &domains); talloc_set_destructor(domain, destroy_domain); @@ -294,6 +299,9 @@ static struct domain *new_domain(void *context, unsigned int domid, domain->port = rc; domain->conn = new_connection(writechn, readchn); + if (!domain->conn) + return NULL; + domain->conn->domain = domain; domain->conn->id = domid; @@ -498,6 +506,8 @@ int do_get_domain_path(struct connection *conn, struct buffered_data *in) return EINVAL; path = talloc_domain_path(conn, atoi(domid_str)); + if (!path) + return errno; send_reply(conn, XS_GET_DOMAIN_PATH, path, strlen(path) + 1); diff --git a/tools/xenstore/xenstored_transaction.c b/tools/xenstore/xenstored_transaction.c index d314cd5..ff05f95 100644 --- a/tools/xenstore/xenstored_transaction.c +++ b/tools/xenstore/xenstored_transaction.c @@ -119,6 +119,11 @@ void add_change_node(struct connection *conn, struct node *node, bool recurse) } i = talloc(trans, struct changed_node); + if (!i) { + /* All we can do is let the transaction fail. */ + generation++; + return; + } i->node = talloc_strdup(i, node->name); i->recurse = recurse; list_add_tail(&i->list, &trans->changes); @@ -163,11 +168,16 @@ int do_transaction_start(struct connection *conn, struct buffered_data *in) /* Attach transaction to input for autofree until it's complete */ trans = talloc_zero(in, struct transaction); + if (!trans) + return ENOMEM; + INIT_LIST_HEAD(&trans->changes); INIT_LIST_HEAD(&trans->changed_domains); trans->generation = generation; trans->tdb_name = talloc_asprintf(trans, "%s.%p", xs_daemon_tdb(), trans); + if (!trans->tdb_name) + return ENOMEM; trans->tdb = tdb_copy(tdb_context(conn), trans->tdb_name); if (!trans->tdb) return errno; @@ -246,6 +256,11 @@ void transaction_entry_inc(struct transaction *trans, unsigned int domid) } d = talloc(trans, struct changed_domain); + if (!d) { + /* Let the transaction fail. */ + generation++; + return; + } d->domid = domid; d->nbentry = 1; list_add_tail(&d->list, &trans->changed_domains); @@ -262,6 +277,11 @@ void transaction_entry_dec(struct transaction *trans, unsigned int domid) } d = talloc(trans, struct changed_domain); + if (!d) { + /* Let the transaction fail. */ + generation++; + return; + } d->domid = domid; d->nbentry = -1; list_add_tail(&d->list, &trans->changed_domains); diff --git a/tools/xenstore/xenstored_watch.c b/tools/xenstore/xenstored_watch.c index 94251db..0dc5a40 100644 --- a/tools/xenstore/xenstored_watch.c +++ b/tools/xenstore/xenstored_watch.c @@ -111,6 +111,8 @@ static void add_event(struct connection *conn, len = strlen(name) + 1 + strlen(watch->token) + 1; data = talloc_array(ctx, char, len); + if (!data) + return; strcpy(data, name); strcpy(data + strlen(name) + 1, watch->token); send_reply(conn, XS_WATCH_EVENT, data, len); @@ -165,6 +167,8 @@ int do_watch(struct connection *conn, struct buffered_data *in) } else { relative = !strstarts(vec[0], "/"); vec[0] = canonicalize(conn, in, vec[0]); + if (!vec[0]) + return ENOMEM; if (!is_valid_nodename(vec[0])) return EINVAL; } @@ -180,8 +184,14 @@ int do_watch(struct connection *conn, struct buffered_data *in) return E2BIG; watch = talloc(conn, struct watch); + if (!watch) + return ENOMEM; watch->node = talloc_strdup(watch, vec[0]); watch->token = talloc_strdup(watch, vec[1]); + if (!watch->node || !watch->token) { + talloc_free(watch); + return ENOMEM; + } if (relative) watch->relative_path = get_implicit_path(conn); else @@ -210,6 +220,8 @@ int do_unwatch(struct connection *conn, struct buffered_data *in) return EINVAL; node = canonicalize(conn, in, vec[0]); + if (!node) + return ENOMEM; list_for_each_entry(watch, &conn->watches, list) { if (streq(watch->node, node) && streq(watch->token, vec[1])) { list_del(&watch->list); -- 2.6.6 _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxx https://lists.xen.org/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |