summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '0113-tools-xenstore-add-generic-treewalk-function.patch')
-rw-r--r--0113-tools-xenstore-add-generic-treewalk-function.patch250
1 files changed, 250 insertions, 0 deletions
diff --git a/0113-tools-xenstore-add-generic-treewalk-function.patch b/0113-tools-xenstore-add-generic-treewalk-function.patch
new file mode 100644
index 0000000..b80c574
--- /dev/null
+++ b/0113-tools-xenstore-add-generic-treewalk-function.patch
@@ -0,0 +1,250 @@
+From 83b6c511a5989a83c50daae83c5b5a683d6dc096 Mon Sep 17 00:00:00 2001
+From: Juergen Gross <jgross@suse.com>
+Date: Tue, 13 Sep 2022 07:35:11 +0200
+Subject: [PATCH 113/126] tools/xenstore: add generic treewalk function
+
+Add a generic function to walk the complete node tree. It will start
+at "/" and descend recursively into each child, calling a function
+specified by the caller. Depending on the return value of the user
+specified function the walk will be aborted, continued, or the current
+child will be skipped by not descending into its children.
+
+This is part of XSA-418 / CVE-2022-42321.
+
+Signed-off-by: Juergen Gross <jgross@suse.com>
+Acked-by: Julien Grall <jgrall@amazon.com>
+(cherry picked from commit 0d7c5d19bc27492360196e7dad2b227908564fff)
+---
+ tools/xenstore/xenstored_core.c | 143 +++++++++++++++++++++++++++++---
+ tools/xenstore/xenstored_core.h | 40 +++++++++
+ 2 files changed, 170 insertions(+), 13 deletions(-)
+
+diff --git a/tools/xenstore/xenstored_core.c b/tools/xenstore/xenstored_core.c
+index 4c3897721bdd..7463d0a002d7 100644
+--- a/tools/xenstore/xenstored_core.c
++++ b/tools/xenstore/xenstored_core.c
+@@ -1804,6 +1804,135 @@ static int do_set_perms(const void *ctx, struct connection *conn,
+ return 0;
+ }
+
++static char *child_name(const void *ctx, const char *s1, const char *s2)
++{
++ if (strcmp(s1, "/"))
++ return talloc_asprintf(ctx, "%s/%s", s1, s2);
++ return talloc_asprintf(ctx, "/%s", s2);
++}
++
++static int rm_from_parent(struct connection *conn, struct node *parent,
++ const char *name)
++{
++ size_t off;
++
++ if (!parent)
++ return WALK_TREE_ERROR_STOP;
++
++ for (off = parent->childoff - 1; off && parent->children[off - 1];
++ off--);
++ if (remove_child_entry(conn, parent, off)) {
++ log("treewalk: child entry could not be removed from '%s'",
++ parent->name);
++ return WALK_TREE_ERROR_STOP;
++ }
++ parent->childoff = off;
++
++ return WALK_TREE_OK;
++}
++
++static int walk_call_func(const void *ctx, struct connection *conn,
++ struct node *node, struct node *parent, void *arg,
++ int (*func)(const void *ctx, struct connection *conn,
++ struct node *node, void *arg))
++{
++ int ret;
++
++ if (!func)
++ return WALK_TREE_OK;
++
++ ret = func(ctx, conn, node, arg);
++ if (ret == WALK_TREE_RM_CHILDENTRY && parent)
++ ret = rm_from_parent(conn, parent, node->name);
++
++ return ret;
++}
++
++int walk_node_tree(const void *ctx, struct connection *conn, const char *root,
++ struct walk_funcs *funcs, void *arg)
++{
++ int ret = 0;
++ void *tmpctx;
++ char *name;
++ struct node *node = NULL;
++ struct node *parent = NULL;
++
++ tmpctx = talloc_new(ctx);
++ if (!tmpctx) {
++ errno = ENOMEM;
++ return WALK_TREE_ERROR_STOP;
++ }
++ name = talloc_strdup(tmpctx, root);
++ if (!name) {
++ errno = ENOMEM;
++ talloc_free(tmpctx);
++ return WALK_TREE_ERROR_STOP;
++ }
++
++ /* Continue the walk until an error is returned. */
++ while (ret >= 0) {
++ /* node == NULL possible only for the initial loop iteration. */
++ if (node) {
++ /* Go one step up if ret or if last child finished. */
++ if (ret || node->childoff >= node->childlen) {
++ parent = node->parent;
++ /* Call function AFTER processing a node. */
++ ret = walk_call_func(ctx, conn, node, parent,
++ arg, funcs->exit);
++ /* Last node, so exit loop. */
++ if (!parent)
++ break;
++ talloc_free(node);
++ /* Continue with parent. */
++ node = parent;
++ continue;
++ }
++ /* Get next child of current node. */
++ name = child_name(tmpctx, node->name,
++ node->children + node->childoff);
++ if (!name) {
++ ret = WALK_TREE_ERROR_STOP;
++ break;
++ }
++ /* Point to next child. */
++ node->childoff += strlen(node->children +
++ node->childoff) + 1;
++ /* Descent into children. */
++ parent = node;
++ }
++ /* Read next node (root node or next child). */
++ node = read_node(conn, tmpctx, name);
++ if (!node) {
++ /* Child not found - should not happen! */
++ /* ENOENT case can be handled by supplied function. */
++ if (errno == ENOENT && funcs->enoent)
++ ret = funcs->enoent(ctx, conn, parent, name,
++ arg);
++ else
++ ret = WALK_TREE_ERROR_STOP;
++ if (!parent)
++ break;
++ if (ret == WALK_TREE_RM_CHILDENTRY)
++ ret = rm_from_parent(conn, parent, name);
++ if (ret < 0)
++ break;
++ talloc_free(name);
++ node = parent;
++ continue;
++ }
++ talloc_free(name);
++ node->parent = parent;
++ node->childoff = 0;
++ /* Call function BEFORE processing a node. */
++ ret = walk_call_func(ctx, conn, node, parent, arg,
++ funcs->enter);
++ }
++
++ talloc_free(tmpctx);
++
++ return ret < 0 ? ret : WALK_TREE_OK;
++}
++
+ static struct {
+ const char *str;
+ int (*func)(const void *ctx, struct connection *conn,
+@@ -2206,18 +2335,6 @@ static int keys_equal_fn(void *key1, void *key2)
+ return 0 == strcmp((char *)key1, (char *)key2);
+ }
+
+-
+-static char *child_name(const char *s1, const char *s2)
+-{
+- if (strcmp(s1, "/")) {
+- return talloc_asprintf(NULL, "%s/%s", s1, s2);
+- }
+- else {
+- return talloc_asprintf(NULL, "/%s", s2);
+- }
+-}
+-
+-
+ int remember_string(struct hashtable *hash, const char *str)
+ {
+ char *k = malloc(strlen(str) + 1);
+@@ -2277,7 +2394,7 @@ static int check_store_(const char *name, struct hashtable *reachable)
+ while (i < node->childlen && !ret) {
+ struct node *childnode;
+ size_t childlen = strlen(node->children + i);
+- char * childname = child_name(node->name,
++ char * childname = child_name(NULL, node->name,
+ node->children + i);
+
+ if (!childname) {
+diff --git a/tools/xenstore/xenstored_core.h b/tools/xenstore/xenstored_core.h
+index 1eb3708f82dd..f0fd8c352857 100644
+--- a/tools/xenstore/xenstored_core.h
++++ b/tools/xenstore/xenstored_core.h
+@@ -195,6 +195,7 @@ struct node {
+
+ /* Children, each nul-terminated. */
+ unsigned int childlen;
++ unsigned int childoff; /* Used by walk_node_tree() internally. */
+ char *children;
+
+ /* Allocation information for node currently in store. */
+@@ -334,6 +335,45 @@ void read_state_buffered_data(const void *ctx, struct connection *conn,
+ const struct xs_state_connection *sc);
+ void read_state_node(const void *ctx, const void *state);
+
++/*
++ * Walk the node tree below root calling funcs->enter() and funcs->exit() for
++ * each node. funcs->enter() is being called when entering a node, so before
++ * any of the children of the node is processed. funcs->exit() is being
++ * called when leaving the node, so after all children have been processed.
++ * funcs->enoent() is being called when a node isn't existing.
++ * funcs->*() return values:
++ * < 0: tree walk is stopped, walk_node_tree() returns funcs->*() return value
++ * in case WALK_TREE_ERROR_STOP is returned, errno should be set
++ * WALK_TREE_OK: tree walk is continuing
++ * WALK_TREE_SKIP_CHILDREN: tree walk won't descend below current node, but
++ * walk continues
++ * WALK_TREE_RM_CHILDENTRY: Remove the child entry from its parent and write
++ * the modified parent node back to the data base, implies to not descend
++ * below the current node, but to continue the walk
++ * funcs->*() is allowed to modify the node it is called for in the data base.
++ * In case funcs->enter() is deleting the node, it must not return WALK_TREE_OK
++ * in order to avoid descending into no longer existing children.
++ */
++/* Return values for funcs->*() and walk_node_tree(). */
++#define WALK_TREE_SUCCESS_STOP -100 /* Stop walk early, no error. */
++#define WALK_TREE_ERROR_STOP -1 /* Stop walk due to error. */
++#define WALK_TREE_OK 0 /* No error. */
++/* Return value for funcs->*() only. */
++#define WALK_TREE_SKIP_CHILDREN 1 /* Don't recurse below current node. */
++#define WALK_TREE_RM_CHILDENTRY 2 /* Remove child entry from parent. */
++
++struct walk_funcs {
++ int (*enter)(const void *ctx, struct connection *conn,
++ struct node *node, void *arg);
++ int (*exit)(const void *ctx, struct connection *conn,
++ struct node *node, void *arg);
++ int (*enoent)(const void *ctx, struct connection *conn,
++ struct node *parent, char *name, void *arg);
++};
++
++int walk_node_tree(const void *ctx, struct connection *conn, const char *root,
++ struct walk_funcs *funcs, void *arg);
++
+ #endif /* _XENSTORED_CORE_H */
+
+ /*
+--
+2.37.4
+