From 16aee2ec3829b37f4db623ab9ddab1b789bdd2e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Chlo=C3=A9=20Vulquin?= Date: Thu, 28 Mar 2024 13:44:36 +0100 Subject: [PATCH] xcursor: catch theme inheritance loops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- cursor/xcursor.c | 112 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 77 insertions(+), 35 deletions(-) diff --git a/cursor/xcursor.c b/cursor/xcursor.c index 6766c564..0d5761f2 100644 --- a/cursor/xcursor.c +++ b/cursor/xcursor.c @@ -726,6 +726,78 @@ load_all_cursors_from_dir(const char *path, int size, closedir(dir); } +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, + 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 = ¤t_node; + + xcursor_path = xcursor_library_path(); + for (path = xcursor_path; + path; + path = xcursor_next_path(path)) { + dir = xcursor_build_theme_dir(path, theme); + if (!dir) + continue; + + full = xcursor_build_fullname(dir, "cursors", ""); + load_all_cursors_from_dir(full, size, load_callback, + user_data); + free(full); + + if (!inherits) { + full = xcursor_build_fullname(dir, "", "index.theme"); + inherits = xcursor_theme_inherits(full); + free(full); + } + + free(dir); + } + + 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 @@ -750,39 +822,9 @@ xcursor_load_theme(const char *theme, int size, void (*load_callback)(struct xcursor_images *, void *), void *user_data) { - char *full, *dir; - char *inherits = NULL; - const char *path, *i; - char *xcursor_path; - - if (!theme) - theme = "default"; - - xcursor_path = xcursor_library_path(); - for (path = xcursor_path; - path; - path = xcursor_next_path(path)) { - dir = xcursor_build_theme_dir(path, theme); - if (!dir) - continue; - - full = xcursor_build_fullname(dir, "cursors", ""); - load_all_cursors_from_dir(full, size, load_callback, - user_data); - free(full); - - if (!inherits) { - full = xcursor_build_fullname(dir, "", "index.theme"); - inherits = xcursor_theme_inherits(full); - free(full); - } - - free(dir); - } - - for (i = inherits; i; i = xcursor_next_path(i)) - xcursor_load_theme(i, size, load_callback, user_data); - - free(inherits); - free(xcursor_path); + xcursor_load_theme_protected(theme, + size, + load_callback, + user_data, + NULL); }