summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChloé Vulquin <code@toast.bunkerlabs.net>2024-03-28 13:44:36 +0100
committerChloé Vulquin <code@toast.bunkerlabs.net>2024-04-24 12:28:38 +0200
commit16aee2ec3829b37f4db623ab9ddab1b789bdd2e2 (patch)
tree2ab88bccf4d87192c5ff2bc77bc39ca9f08ec848
parentb258d5f36137088e5cb5ae097db7964290da7d55 (diff)
xcursor: catch theme inheritance loops
As of currently, when an xcursor theme depends on itself or another theme that will eventually depend on it, `xcursor_load_theme` will recurse infinitely while processing the inherits. This change introduces a stack-allocated linked list of visited nodes by name, and skips any already visited nodes in the inherit list. Side effects: * Since the linked list is stack-allocated, there is a potential for an overflow if there is a very long list of dependencies. If this turns out to be a legitimate concern, the linked list is trivial to convert to being heap-allocated. * There is an existing linked list (technically doubly linked list) implementation in the wayland codebase. As of currently, the xcursor codebase does not refer to it. Consequently, this change writes a minimal single linked list implementation to utilize directly. This changeset fixes #317. Signed-off-by: Chloé Vulquin <toast@bunkerlabs.net>
-rw-r--r--cursor/xcursor.c90
1 files changed, 66 insertions, 24 deletions
diff --git a/cursor/xcursor.c b/cursor/xcursor.c
index 6766c56..0d5761f 100644
--- a/cursor/xcursor.c
+++ b/cursor/xcursor.c
@@ -726,38 +726,45 @@ load_all_cursors_from_dir(const char *path, int size,
closedir(dir);
}
-/** Load all the cursor of a theme
- *
- * This function loads all the cursor images of a given theme and its
- * inherited themes. Each cursor is loaded into an struct xcursor_images object
- * which is passed to the caller's load callback. If a cursor appears
- * more than once across all the inherited themes, the load callback
- * will be called multiple times, with possibly different struct xcursor_images
- * object which have the same name. The user is expected to destroy the
- * struct xcursor_images objects passed to the callback with
- * xcursor_images_destroy().
- *
- * \param theme The name of theme that should be loaded
- * \param size The desired size of the cursor images
- * \param load_callback A callback function that will be called
- * for each cursor loaded. The first parameter is the struct xcursor_images
- * object representing the loaded cursor and the second is a pointer
- * to data provided by the user.
- * \param user_data The data that should be passed to the load callback
- */
-void
-xcursor_load_theme(const char *theme, int size,
+struct xcursor_nodelist {
+ size_t nodelen;
+ const char *node;
+ struct xcursor_nodelist *next;
+};
+
+static bool
+nodelist_contains(struct xcursor_nodelist *nodelist, const char *s, size_t ss)
+{
+ struct xcursor_nodelist *vi;
+
+ for (vi = nodelist; vi && vi->node; vi = vi->next) {
+ if (vi->nodelen == ss && !strncmp(s, vi->node, vi->nodelen))
+ return true;
+ }
+ return false;
+}
+
+static void
+xcursor_load_theme_protected(const char *theme, int size,
void (*load_callback)(struct xcursor_images *, void *),
- void *user_data)
+ void *user_data,
+ struct xcursor_nodelist *visited_nodes)
{
char *full, *dir;
char *inherits = NULL;
const char *path, *i;
char *xcursor_path;
+ size_t si;
+ struct xcursor_nodelist current_node;
if (!theme)
theme = "default";
+ current_node.next = visited_nodes;
+ current_node.node = theme;
+ current_node.nodelen = strlen(theme);
+ visited_nodes = &current_node;
+
xcursor_path = xcursor_library_path();
for (path = xcursor_path;
path;
@@ -780,9 +787,44 @@ xcursor_load_theme(const char *theme, int size,
free(dir);
}
- for (i = inherits; i; i = xcursor_next_path(i))
- xcursor_load_theme(i, size, load_callback, user_data);
+ for (i = inherits; i; i = xcursor_next_path(i)) {
+ si = strlen(i);
+ if (nodelist_contains(visited_nodes, i, si))
+ continue;
+ xcursor_load_theme_protected(i, size, load_callback, user_data, visited_nodes);
+ }
free(inherits);
free(xcursor_path);
}
+
+/** Load all the cursor of a theme
+ *
+ * This function loads all the cursor images of a given theme and its
+ * inherited themes. Each cursor is loaded into an struct xcursor_images object
+ * which is passed to the caller's load callback. If a cursor appears
+ * more than once across all the inherited themes, the load callback
+ * will be called multiple times, with possibly different struct xcursor_images
+ * object which have the same name. The user is expected to destroy the
+ * struct xcursor_images objects passed to the callback with
+ * xcursor_images_destroy().
+ *
+ * \param theme The name of theme that should be loaded
+ * \param size The desired size of the cursor images
+ * \param load_callback A callback function that will be called
+ * for each cursor loaded. The first parameter is the struct xcursor_images
+ * object representing the loaded cursor and the second is a pointer
+ * to data provided by the user.
+ * \param user_data The data that should be passed to the load callback
+ */
+void
+xcursor_load_theme(const char *theme, int size,
+ void (*load_callback)(struct xcursor_images *, void *),
+ void *user_data)
+{
+ xcursor_load_theme_protected(theme,
+ size,
+ load_callback,
+ user_data,
+ NULL);
+}