Skip to content
Snippets Groups Projects
Commit 8196049b authored by Simon McVittie's avatar Simon McVittie
Browse files

pv_wrap_append_preload: Handle libraries specified as a basename

This is a bit complicated, because there are two reasonable things that
people might use LD_PRELOAD for, and in this mode it's particularly
important to distinguish between them.

One is to inject arbitrary code, like MangoHud or fakeroot. In this
case, we want to take the loadable module from the namespace in which
the user initiated pv-wrap. We can do this the same way we deal with
the ${LIB} and ${PLATFORM} dynamic string tokens: load it once per ABI,
pass a separate option to pv-adverb for each one, and let pv-adverb
recombine them.

The other is to work around libraries not being loaded soon enough,
like the way people sometimes use LD_PRELOAD="libpthread.so.0 libGL.so.1"
to force an optirun library to be loaded. In this case, we absolutely
do not want to import the host library of that name into the container
unconditionally, because if we do, it will sabotage our careful efforts
to get the correct instance of libpthread.so.0 to be chosen. In this
case, assume that the user meant "take whatever libpthread.so.0 you
would naturally load, and preload it into each executable".

Resolves: https://github.com/ValveSoftware/steam-runtime/issues/435


Signed-off-by: default avatarSimon McVittie <smcv@collabora.com>
parent 08751fff
No related branches found
No related tags found
1 merge request!337pressure-vessel: Remap preloadable modules better
......@@ -5751,3 +5751,103 @@ pv_runtime_get_modified_app (PvRuntime *self)
else
return NULL;
}
/*
* Return %TRUE if the runtime provides @library, either directly or
* via the graphics-stack provider.
*/
gboolean
pv_runtime_has_library (PvRuntime *self,
const char *library)
{
glnx_autofd int source_files_fd = -1;
gsize i;
g_return_val_if_fail (PV_IS_RUNTIME (self), FALSE);
g_return_val_if_fail (library != NULL, FALSE);
g_debug ("Checking whether runtime has library: %s", library);
for (i = 0; i < PV_N_SUPPORTED_ARCHITECTURES; i++)
{
const PvMultiarchDetails *details = &pv_multiarch_details[i];
g_autoptr(GPtrArray) dirs = NULL;
gsize j;
dirs = pv_multiarch_details_get_libdirs (details,
PV_MULTIARCH_LIBDIRS_FLAGS_NONE);
for (j = 0; j < dirs->len; j++)
{
const char *libdir = g_ptr_array_index (dirs, j);
g_autofree gchar *path = g_build_filename (libdir, library, NULL);
if (self->mutable_sysroot_fd >= 0)
{
glnx_autofd int fd = -1;
fd = _srt_resolve_in_sysroot (self->mutable_sysroot_fd, path,
SRT_RESOLVE_FLAGS_NONE,
NULL, NULL);
if (fd >= 0)
{
g_debug ("-> yes, ${mutable_sysroot}/%s", path);
return TRUE;
}
}
else
{
glnx_autofd int fd = -1;
/* The runtime isn't necessarily a sysroot (it might just be a
* merged /usr) but in practice it'll be close enough: we look
* up each library in /usr/foo and /foo anyway. */
if (source_files_fd < 0)
{
if (!glnx_opendirat (AT_FDCWD, self->source_files, TRUE,
&source_files_fd, NULL))
continue;
}
fd = _srt_resolve_in_sysroot (source_files_fd, path,
SRT_RESOLVE_FLAGS_NONE,
NULL, NULL);
if (fd >= 0)
{
g_debug ("-> yes, ${source_files}/%s", path);
return TRUE;
}
}
/* If the graphics stack provider is not the same as the current
* namespace (in practice this rarely/never happens), we also
* want to steer clear of libraries that only exist in the
* graphics stack provider.
*
* If the graphics stack provider *is* the current namespace,
* and the library doesn't exist in the container runtime, then
* it's OK to use libraries from it in LD_PRELOAD, because there
* is no other version that might have been meant. */
if (self->provider != NULL
&& g_strcmp0 (self->provider->path_in_current_ns, "/") != 0)
{
glnx_autofd int fd = -1;
fd = _srt_resolve_in_sysroot (self->provider->fd, path,
SRT_RESOLVE_FLAGS_NONE,
NULL, NULL);
if (fd >= 0)
{
g_debug ("-> yes, ${provider}/%s", path);
return TRUE;
}
}
}
}
g_debug ("-> no");
return FALSE;
}
......@@ -113,4 +113,7 @@ gboolean pv_runtime_use_shared_sockets (PvRuntime *self,
PvEnviron *container_env,
GError **error);
gboolean pv_runtime_has_library (PvRuntime *self,
const char *library);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (PvRuntime, g_object_unref)
......@@ -502,9 +502,6 @@ append_preload_per_architecture (GPtrArray *argv,
g_autoptr(SrtSystemInfo) system_info = srt_system_info_new (NULL);
gsize i;
g_debug ("Found $LIB or $PLATFORM in \"%s\", splitting architectures",
preload);
if (system_info == NULL)
system_info = srt_system_info_new (NULL);
......@@ -537,10 +534,19 @@ append_preload_per_architecture (GPtrArray *argv,
platform = pv_multiarch_details[i].platforms[0];
mock_path = g_string_new (preload);
g_string_replace (mock_path, "$LIB", lib, 0);
g_string_replace (mock_path, "${LIB}", lib, 0);
g_string_replace (mock_path, "$PLATFORM", platform, 0);
g_string_replace (mock_path, "${PLATFORM}", platform, 0);
if (strchr (preload, '/') == NULL)
{
g_string_printf (mock_path, "/path/to/%s/%s", lib, preload);
}
else
{
g_string_replace (mock_path, "$LIB", lib, 0);
g_string_replace (mock_path, "${LIB}", lib, 0);
g_string_replace (mock_path, "$PLATFORM", platform, 0);
g_string_replace (mock_path, "${PLATFORM}", platform, 0);
}
path = mock_path->str;
/* As a special case, pretend one 64-bit library failed to load,
......@@ -575,6 +581,73 @@ append_preload_per_architecture (GPtrArray *argv,
}
}
static void
append_preload_basename (GPtrArray *argv,
const char *option,
const char *preload,
GStrv env,
PvAppendPreloadFlags flags,
PvRuntime *runtime,
FlatpakExports *exports)
{
gboolean runtime_has_library = FALSE;
if (runtime != NULL)
runtime_has_library = pv_runtime_has_library (runtime, preload);
if (flags & PV_APPEND_PRELOAD_FLAGS_IN_UNIT_TESTS)
{
/* Mock implementation for unit tests: behave as though the
* container has everything except libfakeroot/libfakechroot. */
if (g_str_has_prefix (preload, "libfake"))
runtime_has_library = FALSE;
else
runtime_has_library = TRUE;
}
if (runtime_has_library)
{
/* If the library exists in the container runtime or in the
* stack we imported from the graphics provider, e.g.
* LD_PRELOAD=libpthread.so.0, then we certainly don't want
* to be loading it from the current namespace: that would
* bypass our logic for comparing library versions and picking
* the newest. Just pass through the LD_PRELOAD item into the
* container, and let the dynamic linker in the container choose
* what it means (container runtime or graphics provider as
* appropriate). */
g_debug ("Found \"%s\" in runtime or graphics stack provider, "
"passing %s through as-is",
preload, option);
append_preload_internal (argv,
option,
NULL,
NULL,
preload,
env,
flags,
runtime,
NULL);
}
else
{
/* There's no such library in the container runtime or in the
* graphics provider, so it's OK to inject the version from the
* current namespace. Use the same trick as for ${PLATFORM} to
* turn it into (up to) one absolute path per ABI. */
g_debug ("Did not find \"%s\" in runtime or graphics stack provider, "
"splitting architectures",
preload);
append_preload_per_architecture (argv,
option,
preload,
env,
flags,
runtime,
exports);
}
}
/**
* pv_wrap_append_preload:
* @argv: (element-type filename): Array of command-line options to populate
......@@ -635,19 +708,13 @@ pv_wrap_append_preload (GPtrArray *argv,
case SRT_LOADABLE_KIND_BASENAME:
/* Basenames can't have dynamic string tokens. */
g_warn_if_fail ((loadable_flags & SRT_LOADABLE_FLAGS_DYNAMIC_TOKENS) == 0);
/* Ideally we would handle this more cleverly, but for now,
* pass preload through to the container without adjustment.
* It isn't useful to add it to the FlatpakExports, because it
* isn't an absolute filename. */
append_preload_internal (argv,
append_preload_basename (argv,
option,
NULL,
NULL,
preload,
env,
flags,
runtime,
NULL);
exports);
break;
case SRT_LOADABLE_KIND_PATH:
......@@ -665,6 +732,8 @@ pv_wrap_append_preload (GPtrArray *argv,
}
else if (loadable_flags & SRT_LOADABLE_FLAGS_ABI_DEPENDENT)
{
g_debug ("Found $LIB or $PLATFORM in \"%s\", splitting architectures",
preload);
append_preload_per_architecture (argv,
option,
preload,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment