[glib: 7/8] gtestutils: Handle empty argv array passed to g_test_init()




commit a6311f81ee93b772a29ec9396e2853fcc72fb79a
Author: Philip Withnall <pwithnall endlessos org>
Date:   Mon Jan 31 14:55:50 2022 +0000

    gtestutils: Handle empty argv array passed to g_test_init()
    
    This can happen if a caller (ab)uses `execve()` to execute a gtest
    process with an empty `argv` array. `g_test_init()` has to gracefully
    handle such a situation.
    
    Fix a few problem areas in the code, and add a simple test which checks
    that `g_test_init()` doesn’t crash when called with an empty `argv`.
    
    Signed-off-by: Philip Withnall <pwithnall endlessos org>

 glib/gtester.c              |  4 ++--
 glib/gtestutils.c           | 13 ++++++++-----
 glib/tests/testing-helper.c | 18 ++++++++++++++++++
 glib/tests/testing.c        | 36 ++++++++++++++++++++++++++++++++++++
 4 files changed, 64 insertions(+), 7 deletions(-)
---
diff --git a/glib/gtester.c b/glib/gtester.c
index 94cfba641..c48ecaf94 100644
--- a/glib/gtester.c
+++ b/glib/gtester.c
@@ -668,8 +668,8 @@ parse_args (gint    *argc_p,
         }
     }
   /* collapse argv */
-  e = 1;
-  for (i = 1; i < argc; i++)
+  e = 0;
+  for (i = 0; i < argc; i++)
     if (argv[i])
       {
         argv[e++] = argv[i];
diff --git a/glib/gtestutils.c b/glib/gtestutils.c
index 27c72a416..dca4badcf 100644
--- a/glib/gtestutils.c
+++ b/glib/gtestutils.c
@@ -890,7 +890,7 @@ static gboolean    test_debug_log = FALSE;
 static gboolean    test_tap_log = TRUE;  /* default to TAP as of GLib 2.62; see #1619; the non-TAP output 
mode is deprecated */
 static gboolean    test_nonfatal_assertions = FALSE;
 static DestroyEntry *test_destroy_queue = NULL;
-static const char *test_argv0 = NULL;           /* points into global argv */
+static const char *test_argv0 = NULL;           /* (nullable), points into global argv */
 static char       *test_argv0_dirname = NULL;   /* owned by GLib */
 static const char *test_disted_files_dir;       /* points into test_argv0_dirname or an environment variable 
*/
 static const char *test_built_files_dir;        /* points into test_argv0_dirname or an environment variable 
*/
@@ -1138,7 +1138,7 @@ parse_args (gint    *argc_p,
   gchar **argv = *argv_p;
   guint i, e;
 
-  test_argv0 = argv[0];
+  test_argv0 = argv[0];  /* will be NULL iff argc == 0 */
   test_initial_cwd = g_get_current_dir ();
 
   /* parse known args */
@@ -1382,8 +1382,8 @@ parse_args (gint    *argc_p,
   test_paths = g_slist_reverse (test_paths);
 
   /* collapse argv */
-  e = 1;
-  for (i = 1; i < argc; i++)
+  e = 0;
+  for (i = 0; i < argc; i++)
     if (argv[i])
       {
         argv[e++] = argv[i];
@@ -1732,7 +1732,7 @@ void
   g_log_set_default_handler (gtest_default_log_handler, NULL);
   g_test_log (G_TEST_LOG_START_BINARY, g_get_prgname(), test_run_seedstr, 0, NULL);
 
-  test_argv0_dirname = g_path_get_dirname (test_argv0);
+  test_argv0_dirname = (test_argv0 != NULL) ? g_path_get_dirname (test_argv0) : g_strdup (".");
 
   /* Make sure we get the real dirname that the test was run from */
   if (g_str_has_suffix (test_argv0_dirname, "/.libs"))
@@ -3828,6 +3828,9 @@ g_test_trap_subprocess (const char           *test_path,
   test_trap_clear ();
   test_trap_last_subprocess = g_strdup (test_path);
 
+  if (test_argv0 == NULL)
+    g_error ("g_test_trap_subprocess() requires argv0 to be passed to g_test_init()");
+
   argv = g_ptr_array_new ();
   g_ptr_array_add (argv, (char *) test_argv0);
   g_ptr_array_add (argv, "-q");
diff --git a/glib/tests/testing-helper.c b/glib/tests/testing-helper.c
index 7b61e8337..7731538a0 100644
--- a/glib/tests/testing-helper.c
+++ b/glib/tests/testing-helper.c
@@ -102,6 +102,24 @@ main (int   argc,
   argc -= 1;
   argv[argc] = NULL;
 
+  if (g_strcmp0 (argv1, "init-null-argv0") == 0)
+    {
+      int test_argc = 0;
+      char *test_argva[1] = { NULL };
+      char **test_argv = test_argva;
+
+      /* Test that `g_test_init()` can handle being called with an empty argv
+       * and argc == 0. While this isn’t recommended, it is possible for another
+       * process to use execve() to call a gtest process this way, so we’d
+       * better handle it gracefully.
+       *
+       * This test can’t be run after `g_test_init()` has been called normally,
+       * as it isn’t allowed to be called more than once in a process. */
+      g_test_init (&test_argc, &test_argv, NULL);
+
+      return 0;
+    }
+
   g_test_init (&argc, &argv, NULL);
   g_test_set_nonfatal_assertions ();
 
diff --git a/glib/tests/testing.c b/glib/tests/testing.c
index c1fcd8c67..accd5dbfe 100644
--- a/glib/tests/testing.c
+++ b/glib/tests/testing.c
@@ -1585,6 +1585,40 @@ test_tap_summary (void)
   g_ptr_array_unref (argv);
 }
 
+static void
+test_init_no_argv0 (void)
+{
+  const char *testing_helper;
+  GPtrArray *argv;
+  GError *error = NULL;
+  int status;
+  gchar *output;
+
+  g_test_summary ("Test that g_test_init() can be called safely with argc == 0.");
+
+  testing_helper = g_test_get_filename (G_TEST_BUILT, "testing-helper" EXEEXT, NULL);
+
+  argv = g_ptr_array_new ();
+  g_ptr_array_add (argv, (char *) testing_helper);
+  g_ptr_array_add (argv, "init-null-argv0");
+  g_ptr_array_add (argv, NULL);
+
+  /* This has to be spawned manually and can’t be run with g_test_subprocess()
+   * because the test helper can’t be run after `g_test_init()` has been called
+   * in the process. */
+  g_spawn_sync (NULL, (char **) argv->pdata, NULL,
+                G_SPAWN_STDERR_TO_DEV_NULL,
+                NULL, NULL, &output, NULL, &status,
+                &error);
+  g_assert_no_error (error);
+
+  g_spawn_check_wait_status (status, &error);
+  g_assert_no_error (error);
+  g_assert_nonnull (strstr (output, "# random seed:"));
+  g_free (output);
+  g_ptr_array_unref (argv);
+}
+
 int
 main (int   argc,
       char *argv[])
@@ -1682,6 +1716,8 @@ main (int   argc,
   g_test_add_func ("/tap", test_tap);
   g_test_add_func ("/tap/summary", test_tap_summary);
 
+  g_test_add_func ("/init/no_argv0", test_init_no_argv0);
+
   ret = g_test_run ();
 
   /* We can't test for https://gitlab.gnome.org/GNOME/glib/-/issues/2563


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]