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>
This commit is contained in:
Chloé Vulquin 2024-03-28 13:44:36 +01:00
parent b258d5f361
commit 16aee2ec38

View file

@ -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 = &current_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);
}