From 48e44b347fdbb3f0de75740201dec61b9295d231 Mon Sep 17 00:00:00 2001
From: Simon McVittie <smcv@collabora.com>
Date: Wed, 14 Dec 2022 18:58:39 +0000
Subject: [PATCH] pv-wrap: Accept LD_AUDIT and LD_PRELOAD as delimited lists

This makes it easier to set them up in the wrapper shell scripts without
making use of bash-specific features, which are not completely portable.

For LD_PRELOAD, the separator is either a colon or a space; for LD_AUDIT,
the separator is a colon. There is no facility for escaping the
separators. This matches the behaviour of the environment variables in
glibc ld.so, as implemented in elf/rtld.c.

The usual reason to prefer separate command-line arguments over parsing
is that a single merged argument either requires an escape mechanism or
cannot represent certain data (for instance loadable modules with a colon
in their filenames), but there is no need to try to support things that
glibc doesn't, and glibc doesn't support loadable modules with the
separator in their filenames anyway.

Signed-off-by: Simon McVittie <smcv@collabora.com>
---
 pressure-vessel/wrap.1.md |  8 +++++
 pressure-vessel/wrap.c    | 62 ++++++++++++++++++++++++++++++++++++---
 2 files changed, 66 insertions(+), 4 deletions(-)

diff --git a/pressure-vessel/wrap.1.md b/pressure-vessel/wrap.1.md
index a684b80ea..55c1047a6 100644
--- a/pressure-vessel/wrap.1.md
+++ b/pressure-vessel/wrap.1.md
@@ -121,6 +121,10 @@ pressure-vessel-wrap - run programs in a bubblewrap container
     will be run in a different container or on the host system, then the
     path of the *MODULE* will be adjusted as necessary.
 
+`--ld-audits` *MODULE*[**:**_MODULE_...]
+:   Same as `--ld-audit`, but the argument is a colon-delimited string
+    (the same as `$LD_AUDIT` itself).
+
 `--ld-preload` *MODULE*
 :   Add *MODULE* from the current execution environment to `LD_PRELOAD`
     when executing *COMMAND*. If *COMMAND* is run in a container, or if
@@ -128,6 +132,10 @@ pressure-vessel-wrap - run programs in a bubblewrap container
     will be run in a different container or on the host system, then the
     path of the *MODULE* will be adjusted as necessary.
 
+`--ld-preloads` *MODULE*[**:**_MODULE_...]
+:   Same as `--ld-preload`, but the argument is a string delimited by
+    colons and/or spaces (the same as `$LD_PRELOAD` itself).
+
 `--only-prepare`
 :   Prepare the runtime, but do not actually run *COMMAND*.
     With `--copy-runtime`, the prepared runtime will appear in
diff --git a/pressure-vessel/wrap.c b/pressure-vessel/wrap.c
index 68a9dc9f6..66c4f259c 100644
--- a/pressure-vessel/wrap.c
+++ b/pressure-vessel/wrap.c
@@ -488,9 +488,22 @@ wrap_preload_module_clear (gpointer p)
 static gboolean
 opt_ld_something (PreloadVariableIndex which,
                   const char *value,
+                  const char *separators,
                   GError **error)
 {
-  WrapPreloadModule module = { which, g_strdup (value) };
+  g_auto(GStrv) tokens = NULL;
+  size_t i;
+
+  if (separators != NULL)
+    {
+      tokens = g_strsplit_set (value, separators, 0);
+    }
+  else
+    {
+      tokens = g_new0 (char *, 2);
+      tokens[0] = g_strdup (value);
+      tokens[1] = NULL;
+    }
 
   if (opt_preload_modules == NULL)
     {
@@ -498,7 +511,16 @@ opt_ld_something (PreloadVariableIndex which,
       g_array_set_clear_func (opt_preload_modules, wrap_preload_module_clear);
     }
 
-  g_array_append_val (opt_preload_modules, module);
+  for (i = 0; tokens[i] != NULL; i++)
+    {
+      WrapPreloadModule module = { which, g_steal_pointer (&tokens[i]) };
+
+      if (module.preload[0] == '\0')
+        wrap_preload_module_clear (&module);
+      else
+        g_array_append_val (opt_preload_modules, module);
+    }
+
   return TRUE;
 }
 
@@ -508,7 +530,18 @@ opt_ld_audit_cb (const gchar *option_name,
                  gpointer data,
                  GError **error)
 {
-  return opt_ld_something (PRELOAD_VARIABLE_INDEX_LD_AUDIT, value, error);
+  return opt_ld_something (PRELOAD_VARIABLE_INDEX_LD_AUDIT, value, NULL, error);
+}
+
+static gboolean
+opt_ld_audits_cb (const gchar *option_name,
+                  const gchar *value,
+                  gpointer data,
+                  GError **error)
+{
+  /* "The items in the list are colon-separated, and there is no support
+   * for escaping the separator." —ld.so(8) */
+  return opt_ld_something (PRELOAD_VARIABLE_INDEX_LD_AUDIT, value, ":", error);
 }
 
 static gboolean
@@ -517,7 +550,18 @@ opt_ld_preload_cb (const gchar *option_name,
                    gpointer data,
                    GError **error)
 {
-  return opt_ld_something (PRELOAD_VARIABLE_INDEX_LD_PRELOAD, value, error);
+  return opt_ld_something (PRELOAD_VARIABLE_INDEX_LD_PRELOAD, value, NULL, error);
+}
+
+static gboolean
+opt_ld_preloads_cb (const gchar *option_name,
+                    const gchar *value,
+                    gpointer data,
+                    GError **error)
+{
+  /* "The items of the list can be separated by spaces or colons, and
+   * there is no support for escaping either separator." —ld.so(8) */
+  return opt_ld_something (PRELOAD_VARIABLE_INDEX_LD_PRELOAD, value, ": ", error);
 }
 
 static gboolean
@@ -876,11 +920,21 @@ static GOptionEntry options[] =
     "Add MODULE from current execution environment to LD_AUDIT when "
     "executing COMMAND.",
     "MODULE" },
+  { "ld-audits", '\0',
+    G_OPTION_FLAG_FILENAME, G_OPTION_ARG_CALLBACK, &opt_ld_audits_cb,
+    "Add MODULEs from current execution environment to LD_AUDIT when "
+    "executing COMMAND. Modules are separated by colons.",
+    "MODULE[:MODULE...]" },
   { "ld-preload", '\0',
     G_OPTION_FLAG_FILENAME, G_OPTION_ARG_CALLBACK, &opt_ld_preload_cb,
     "Add MODULE from current execution environment to LD_PRELOAD when "
     "executing COMMAND.",
     "MODULE" },
+  { "ld-preloads", '\0',
+    G_OPTION_FLAG_FILENAME, G_OPTION_ARG_CALLBACK, &opt_ld_preloads_cb,
+    "Add MODULEs from current execution environment to LD_PRELOAD when "
+    "executing COMMAND. Modules are separated by colons and/or spaces.",
+    "MODULE[:MODULE...]" },
   { "pass-fd", '\0',
     G_OPTION_FLAG_NONE, G_OPTION_ARG_CALLBACK, opt_pass_fd_cb,
     "Let the launched process inherit the given fd.",
-- 
GitLab