diff --git a/pressure-vessel/runtime.c b/pressure-vessel/runtime.c index 62fb34ef2fdd41f2b2c39249cd48802d8770d22f..443bd05f5280bb4d2f186257f13603d488f06027 100644 --- a/pressure-vessel/runtime.c +++ b/pressure-vessel/runtime.c @@ -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; +} diff --git a/pressure-vessel/runtime.h b/pressure-vessel/runtime.h index dfb9f19f47ba299fe111763bdf8cdc04dd570985..6370deea3b411809feeed5bc61f6b7ca6004d93b 100644 --- a/pressure-vessel/runtime.h +++ b/pressure-vessel/runtime.h @@ -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) diff --git a/pressure-vessel/wrap-setup.c b/pressure-vessel/wrap-setup.c index fdcaef856f75687745f849e77daacee1819f35b7..c9982426e1d374c7938dcf5784e0851aea7ee05a 100644 --- a/pressure-vessel/wrap-setup.c +++ b/pressure-vessel/wrap-setup.c @@ -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,