only dlopen from the defined search paths

Don't accept absolute library paths that are not in the search path,
skip the ../ in paths to avoid opening arbitrary libraries from
unexpected places.
This commit is contained in:
Wim Taymans 2026-04-06 14:18:22 +02:00
parent 1689b441d3
commit 6bc07dfe0e
7 changed files with 73 additions and 59 deletions

View file

@ -233,43 +233,49 @@ static inline const char *split_walk(const char *str, const char *delimiter, siz
return s;
}
static int load_ladspa_plugin(struct plugin *impl, const char *path)
static void make_search_paths(const char **path, const char **search_dirs)
{
const char *p;
while ((p = strstr(*path, "../")) != NULL)
*path = p + 3;
*search_dirs = getenv("LADSPA_PATH");
if (!*search_dirs)
*search_dirs = "/usr/lib64/ladspa:/usr/lib/ladspa:" LIBDIR;
}
static int load_ladspa_plugin(struct plugin *impl, const char *path, const char *search_dirs)
{
int res = -ENOENT;
const char *p, *state = NULL;
char filename[PATH_MAX];
size_t len;
if (path[0] != '/') {
const char *search_dirs, *p, *state = NULL;
char filename[PATH_MAX];
size_t len;
/*
* set the errno for the case when `ladspa_handle_load_by_path()`
* is never called, which can only happen if the supplied
* LADSPA_PATH contains too long paths
*/
res = -ENAMETOOLONG;
search_dirs = getenv("LADSPA_PATH");
if (!search_dirs)
search_dirs = "/usr/lib64/ladspa:/usr/lib/ladspa:" LIBDIR;
while ((p = split_walk(search_dirs, ":", &len, &state))) {
int namelen;
/*
* set the errno for the case when `ladspa_handle_load_by_path()`
* is never called, which can only happen if the supplied
* LADSPA_PATH contains too long paths
*/
res = -ENAMETOOLONG;
while ((p = split_walk(search_dirs, ":", &len, &state))) {
int namelen;
if (len >= sizeof(filename))
continue;
if (len >= sizeof(filename))
continue;
if (strncmp(path, p, len) == 0)
namelen = snprintf(filename, sizeof(filename), "%s", path);
else
namelen = snprintf(filename, sizeof(filename), "%.*s/%s.so", (int) len, p, path);
if (namelen < 0 || (size_t) namelen >= sizeof(filename))
continue;
res = ladspa_handle_load_by_path(impl, filename);
if (res >= 0)
break;
}
}
else {
res = ladspa_handle_load_by_path(impl, path);
if (namelen < 0 || (size_t) namelen >= sizeof(filename))
continue;
res = ladspa_handle_load_by_path(impl, filename);
if (res >= 0)
break;
}
return res;
}
@ -317,7 +323,7 @@ impl_init(const struct spa_handle_factory *factory,
struct plugin *impl;
uint32_t i;
int res;
const char *path = NULL;
const char *path = NULL, *search_dirs;
handle->get_interface = impl_get_interface;
handle->clear = impl_clear;
@ -335,9 +341,11 @@ impl_init(const struct spa_handle_factory *factory,
if (path == NULL)
return -EINVAL;
if ((res = load_ladspa_plugin(impl, path)) < 0) {
spa_log_error(impl->log, "failed to load plugin '%s': %s",
path, spa_strerror(res));
make_search_paths(&path, &search_dirs);
if ((res = load_ladspa_plugin(impl, path, search_dirs)) < 0) {
spa_log_error(impl->log, "failed to load plugin '%s' in '%s': %s",
path, search_dirs, spa_strerror(res));
return res;
}

View file

@ -21,7 +21,6 @@ context.modules = [
# listed in the environment variable LADSPA_PATH or
# /usr/lib64/ladspa, /usr/lib/ladspa or the system library directory
# as a fallback.
# You might want to use an absolute path here to avoid problems.
plugin = "librnnoise_ladspa"
label = noise_suppressor_stereo
control = {

View file

@ -128,7 +128,7 @@ extern struct spa_handle_factory spa_filter_graph_factory;
* # an example ladspa plugin
* type = ladspa
* name = pitch
* plugin = "/usr/lib64/ladspa/ladspa-rubberband.so"
* plugin = "ladspa-rubberband"
* label = "rubberband-r3-pitchshifter-mono"
* control = {
* # controls are using the ladspa port names as seen in analyseplugin

View file

@ -50,7 +50,6 @@
*
* - `jack.library`: the libjack to load, by default libjack.so.0 is searched in
* LIBJACK_PATH directories and then some standard library paths.
* Can be an absolute path.
* - `jack.server`: the name of the JACK server to tunnel to.
* - `jack.client-name`: the name of the JACK client.
* - `jack.connect`: if jack ports should be connected automatically. Can also be

View file

@ -158,34 +158,36 @@ static inline int weakjack_load_by_path(struct weakjack *jack, const char *path)
static inline int weakjack_load(struct weakjack *jack, const char *lib)
{
int res = -ENOENT;
const char *search_dirs, *p, *state = NULL;
char path[PATH_MAX];
size_t len;
if (lib[0] != '/') {
const char *search_dirs, *p, *state = NULL;
char path[PATH_MAX];
size_t len;
while ((p = strstr(lib, "../")) != NULL)
lib = p + 3;
search_dirs = getenv("LIBJACK_PATH");
if (!search_dirs)
search_dirs = PREFIX "/lib64/:" PREFIX "/lib/:"
"/usr/lib64/:/usr/lib/:" LIBDIR;
search_dirs = getenv("LIBJACK_PATH");
if (!search_dirs)
search_dirs = PREFIX "/lib64/:" PREFIX "/lib/:"
"/usr/lib64/:/usr/lib/:" LIBDIR;
while ((p = pw_split_walk(search_dirs, ":", &len, &state))) {
int pathlen;
res = -ENAMETOOLONG;
if (len >= sizeof(path)) {
res = -ENAMETOOLONG;
continue;
}
while ((p = pw_split_walk(search_dirs, ":", &len, &state))) {
int pathlen;
if (len >= sizeof(path))
continue;
if (strncmp(lib, p, len) == 0)
pathlen = snprintf(path, sizeof(path), "%s", lib);
else
pathlen = snprintf(path, sizeof(path), "%.*s/%s", (int) len, p, lib);
if (pathlen < 0 || (size_t) pathlen >= sizeof(path)) {
res = -ENAMETOOLONG;
continue;
}
if ((res = weakjack_load_by_path(jack, path)) == 0)
break;
}
} else {
res = weakjack_load_by_path(jack, lib);
if (pathlen < 0 || (size_t) pathlen >= sizeof(path))
continue;
if ((res = weakjack_load_by_path(jack, path)) == 0)
break;
}
return res;
}

View file

@ -154,6 +154,9 @@ pw_context_load_module(struct pw_context *context,
NULL
};
while ((p = strstr(name, "../")) != NULL)
name = p + 3;
pw_log_info("%p: name:%s args:%s", context, name, args);
module_dir = getenv("PIPEWIRE_MODULE_DIR");

View file

@ -232,6 +232,9 @@ static struct spa_handle *load_spa_handle(const char *lib,
if (lib == NULL)
lib = sup->support_lib;
while ((p = strstr(lib, "../")) != NULL)
lib = p + 3;
pw_log_debug("load lib:'%s' factory-name:'%s'", lib, factory_name);
plugin = NULL;