From f44e0abd5382f519cc6fa69850c34e6e9c09d5f5 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Mon, 19 Oct 2026 16:31:54 +0200 Subject: [PATCH] conf,ucm,topology: reject relative paths that escape their base directory Apply the lexical snd_relative_path_check() guard to more places. Signed-off-by: Jaroslav Kysela --- src/conf.c | 18 ++++++++++++++++-- src/topology/data.c | 5 +++++ src/ucm/main.c | 6 ++++++ src/ucm/parser.c | 33 +++++++++++++++++++++++---------- src/ucm/ucm_subs.c | 4 ++++ 5 files changed, 54 insertions(+), 12 deletions(-) diff --git a/src/conf.c b/src/conf.c index ef152289..d0e4ec90 100644 --- a/src/conf.c +++ b/src/conf.c @@ -809,7 +809,14 @@ static int get_char_skip_comments(input_t *input) if (!strncmp(str, "searchdir:", 10)) { /* directory to search included files */ - char *tmp = _snd_config_path(str + 10); + char *tmp; + err = snd_relative_path_check(str + 10); + if (err < 0) { + snd_error(CONFIG, "Search dir path '%s' escapes the configuration directory", str + 10); + free(str); + return err; + } + tmp = _snd_config_path(str + 10); free(str); if (tmp == NULL) return -ENOMEM; @@ -835,7 +842,14 @@ static int get_char_skip_comments(input_t *input) if (!strncmp(str, "confdir:", 8)) { /* file in the specified directory */ - char *tmp = _snd_config_path(str + 8); + char *tmp; + err = snd_relative_path_check(str + 8); + if (err < 0) { + snd_error(CONFIG, "Config dir path '%s' escapes the configuration directory", str + 8); + free(str); + return err; + } + tmp = _snd_config_path(str + 8); free(str); if (tmp == NULL) return -ENOMEM; diff --git a/src/topology/data.c b/src/topology/data.c index 1753d0c7..144af631 100644 --- a/src/topology/data.c +++ b/src/topology/data.c @@ -175,6 +175,11 @@ static int tplg_parse_data_file(snd_config_t *cfg, struct tplg_elem *elem) if (snd_config_get_string(cfg, &value) < 0) return -EINVAL; + if (snd_relative_path_check(value) < 0) { + snd_error(TOPOLOGY, "data file path '%s' escapes the topology directory", value); + return -EINVAL; + } + /* prepend alsa config directory to path */ if (env) snprintf(filename, sizeof(filename), "%s/%s", env, value); diff --git a/src/ucm/main.c b/src/ucm/main.c index d2b3c2d9..c31d8f98 100644 --- a/src/ucm/main.c +++ b/src/ucm/main.c @@ -563,6 +563,12 @@ static int execute_sysw(const char *sysw) return -EINVAL; } + if (snd_relative_path_check(s) < 0) { + snd_error(UCM, "sysw path '%s' escapes the sysfs root", s); + free(s); + return -EINVAL; + } + e = uc_mgr_sysfs_root(); if (e == NULL) { free(s); diff --git a/src/ucm/parser.c b/src/ucm/parser.c index 4cde9194..fdd39902 100644 --- a/src/ucm/parser.c +++ b/src/ucm/parser.c @@ -46,13 +46,19 @@ static int parse_sequence(snd_use_case_mgr_t *uc_mgr, /* * compose the absolute ucm filename */ -static void ucm_filename(char *fn, size_t fn_len, long version, - const char *dir, const char *file) +static int ucm_filename(char *fn, size_t fn_len, long version, + const char *dir, const char *file) { const char *env = getenv(version > 1 ? ALSA_CONFIG_UCM2_VAR : ALSA_CONFIG_UCM_VAR); if (file[0] == '/') file++; + if (snd_relative_path_check(file) < 0 || + (dir && snd_relative_path_check(dir) < 0)) { + snd_error(UCM, "config file path '%s%s%s' escapes the configuration directory", + dir ?: "", dir ? "/" : "", file); + return -EINVAL; + } if (env == NULL) snprintf(fn, fn_len, "%s/%s/%s%s%s", snd_config_topdir(), version > 1 ? "ucm2" : "ucm", @@ -60,6 +66,7 @@ static void ucm_filename(char *fn, size_t fn_len, long version, else snprintf(fn, fn_len, "%s/%s%s%s", env, dir ?: "", dir ? "/" : "", file); + return 0; } /* @@ -71,9 +78,11 @@ int uc_mgr_config_load_file(snd_use_case_mgr_t *uc_mgr, char filename[PATH_MAX]; int err; - ucm_filename(filename, sizeof(filename), uc_mgr->conf_format, - file[0] == '/' ? NULL : uc_mgr->conf_dir_name, - file); + err = ucm_filename(filename, sizeof(filename), uc_mgr->conf_format, + file[0] == '/' ? NULL : uc_mgr->conf_dir_name, + file); + if (err < 0) + return err; err = uc_mgr_config_load(uc_mgr->conf_format, filename, cfg, optional); if (err < 0) { if (!optional || (err != -ENOENT && err != -EACCES)) @@ -842,9 +851,11 @@ static int parse_libconfig1(snd_use_case_mgr_t *uc_mgr, snd_config_t *cfg) } else { char filename[PATH_MAX]; - ucm_filename(filename, sizeof(filename), uc_mgr->conf_format, - file[0] == '/' ? NULL : uc_mgr->conf_dir_name, - file); + err = ucm_filename(filename, sizeof(filename), uc_mgr->conf_format, + file[0] == '/' ? NULL : uc_mgr->conf_dir_name, + file); + if (err < 0) + return err; err = uc_mgr_config_load_into(uc_mgr->conf_format, filename, uc_mgr->local_config, false); if (err < 0) return err; @@ -3438,7 +3449,8 @@ static int parse_toplevel_path(snd_use_case_mgr_t *uc_mgr, goto __next; } - ucm_filename(fn, sizeof(fn), version, dir, file); + if (ucm_filename(fn, sizeof(fn), version, dir, file) < 0) + goto __next; snd_trace(UCM, "probing configuration file '%s'", fn); if (access(fn, R_OK) == 0 && lstat64(fn, &st) == 0) { if (S_ISLNK(st.st_mode)) { @@ -3785,7 +3797,8 @@ int uc_mgr_scan_master_configs(const char **_list[]) d_name = namelist[i]->d_name; snprintf(fn, sizeof(fn), "%s.conf", d_name); - ucm_filename(filename, sizeof(filename), 2, d_name, fn); + if (ucm_filename(filename, sizeof(filename), 2, d_name, fn) < 0) + continue; #ifdef HAVE_EACCESS if (eaccess(filename, R_OK)) #else diff --git a/src/ucm/ucm_subs.c b/src/ucm/ucm_subs.c index c8b13648..e4a238a9 100644 --- a/src/ucm/ucm_subs.c +++ b/src/ucm/ucm_subs.c @@ -736,6 +736,10 @@ static char *rval_sysfs_main(snd_use_case_mgr_t *uc_mgr, const char *top_path, c } if (id[0] == '/') id++; + if (snd_relative_path_check(id) < 0) { + snd_error(UCM, "sysfs path '%s' escapes the sysfs root", id); + return NULL; + } if (top_path) snprintf(path, sizeof(path), "%s/%s/%s", e, top_path, id); else -- 2.52.0