(root)/
glibc-2.38/
posix/
tst-wordexp-nocmd.c
       1  /* Test for (lack of) command execution in wordexp.
       2     Copyright (C) 1997-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  /* This test optionally counts PIDs in a PID namespace to detect
      20     forks.  Without kernel support for that, it will merely look at the
      21     error codes from wordexp to check that no command execution
      22     happens.  */
      23  
      24  #include <sched.h>
      25  #include <stdbool.h>
      26  #include <stdio.h>
      27  #include <stdlib.h>
      28  #include <support/check.h>
      29  #include <support/namespace.h>
      30  #include <support/xunistd.h>
      31  #include <wordexp.h>
      32  
      33  /* Set to true if the test runs in a PID namespace and can therefore
      34     use next_pid below.  */
      35  static bool pid_tests_supported;
      36  
      37  /* The next PID, as returned from next_pid below.  Only meaningful if
      38     pid_tests_supported.  */
      39  static pid_t expected_pid;
      40  
      41  /* Allocate the next PID and return it.  The process is terminated.
      42     Note that the test itself advances the next PID.  */
      43  static pid_t
      44  next_pid (void)
      45  {
      46    pid_t pid = xfork ();
      47    if (pid == 0)
      48      _exit (0);
      49    xwaitpid (pid, NULL, 0);
      50    return pid;
      51  }
      52  
      53  /* Check that evaluating PATTERN with WRDE_NOCMD results in
      54     EXPECTED_ERROR.  */
      55  static void
      56  expect_failure (const char *pattern, int expected_error)
      57  {
      58    printf ("info: testing pattern: %s\n", pattern);
      59    wordexp_t w;
      60    TEST_COMPARE (wordexp (pattern, &w, WRDE_NOCMD), expected_error);
      61    if (pid_tests_supported)
      62      TEST_COMPARE (expected_pid++, next_pid ());
      63  }
      64  
      65  /* Run all the tests.  Invoked with different IFS values.  */
      66  static void
      67  run_tests (void)
      68  {
      69    /* Integer overflow in division.  */
      70    {
      71      static const char *const numbers[] = {
      72        "0",
      73        "1",
      74        "65536",
      75        "2147483648",
      76        "4294967296"
      77        "9223372036854775808",
      78        "18446744073709551616",
      79        "170141183460469231731687303715884105728",
      80        "340282366920938463463374607431768211456",
      81        NULL
      82      };
      83  
      84      for (const char *const *num = numbers; *num != NULL; ++num)
      85        {
      86          wordexp_t w;
      87          char pattern[256];
      88          snprintf (pattern, sizeof (pattern), "$[(-%s)/(-1)]", *num);
      89          int ret = wordexp (pattern, &w, WRDE_NOCMD);
      90          if (ret == 0)
      91            {
      92              /* If the call is successful, the result must match the
      93                 original number.  */
      94              TEST_COMPARE (w.we_wordc, 1);
      95              TEST_COMPARE_STRING (w.we_wordv[0], *num);
      96              TEST_COMPARE_STRING (w.we_wordv[1], NULL);
      97              wordfree (&w);
      98            }
      99          else
     100            /* Otherwise, the test must fail with a syntax error.  */
     101            TEST_COMPARE (ret, WRDE_SYNTAX);
     102  
     103          /* In both cases, command execution is not permitted.  */
     104          if (pid_tests_supported)
     105            TEST_COMPARE (expected_pid++, next_pid ());
     106        }
     107    }
     108  
     109    /* (Lack of) command execution tests.  */
     110  
     111    expect_failure ("$(ls)", WRDE_CMDSUB);
     112  
     113    /* Test for CVE-2014-7817. We test 3 combinations of command
     114       substitution inside an arithmetic expression to make sure that
     115       no commands are executed and error is returned.  */
     116    expect_failure ("$((`echo 1`))", WRDE_CMDSUB);
     117    expect_failure ("$((1+`echo 1`))", WRDE_CMDSUB);
     118    expect_failure ("$((1+$((`echo 1`))))", WRDE_CMDSUB);
     119  
     120    expect_failure ("$[1/0]", WRDE_SYNTAX); /* BZ 18100.  */
     121  }
     122  
     123  static void
     124  subprocess (void *closure)
     125  {
     126    expected_pid = 2;
     127    if (pid_tests_supported)
     128      TEST_COMPARE (expected_pid++, next_pid ());
     129  
     130    /* Check that triggering command execution via wordexp results in a
     131       PID increase.  */
     132    if (pid_tests_supported)
     133      {
     134        wordexp_t w;
     135        TEST_COMPARE (wordexp ("$(echo Test)", &w, 0), 0);
     136        TEST_COMPARE (w.we_wordc, 1);
     137        TEST_COMPARE_STRING (w.we_wordv[0], "Test");
     138        TEST_COMPARE_STRING (w.we_wordv[1], NULL);
     139        wordfree (&w);
     140  
     141        pid_t n = next_pid ();
     142        printf ("info: self-test resulted in PID %d (processes created: %d)\n",
     143                (int) n, (int) (n - expected_pid));
     144        TEST_VERIFY (n > expected_pid);
     145        expected_pid = n + 1;
     146    }
     147  
     148    puts ("info: testing without IFS");
     149    unsetenv ("IFS");
     150    run_tests ();
     151  
     152    puts ("info: testing with IFS");
     153    TEST_COMPARE (setenv ("IFS", " \t\n", 1), 0);
     154    run_tests ();
     155  }
     156  
     157  static int
     158  do_test (void)
     159  {
     160    support_become_root ();
     161  
     162  #ifdef CLONE_NEWPID
     163    if (unshare (CLONE_NEWPID) != 0)
     164      printf ("warning: unshare (CLONE_NEWPID) failed: %m\n"
     165              "warning: This leads to reduced test coverage.\n");
     166    else
     167      pid_tests_supported = true;
     168  #else
     169    printf ("warning: CLONE_NEWPID not available.\n"
     170            "warning: This leads to reduced test coverage.\n");
     171  #endif
     172  
     173    /* CLONE_NEWPID only has an effect after fork.  */
     174    support_isolate_in_subprocess (subprocess, NULL);
     175  
     176    return 0;
     177  }
     178  
     179  #include <support/test-driver.c>