From patchwork Tue Oct 8 11:04:41 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 1173203 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=sourceware.org (client-ip=209.132.180.131; helo=sourceware.org; envelope-from=libc-alpha-return-105728-incoming=patchwork.ozlabs.org@sourceware.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; secure) header.d=sourceware.org header.i=@sourceware.org header.b="YeKRpz6q"; dkim-atps=neutral Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 46nZHs3ndFz9sPT for ; Tue, 8 Oct 2019 22:04:57 +1100 (AEDT) DomainKey-Signature: a=rsa-sha1; c=nofws; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:from:to:subject:date:message-id:mime-version :content-type; q=dns; s=default; b=xrzjJl9vBkhrq6g4j4dHCsXWcnRvE KuWx4P+eWeigh3AqYHKzZQ3tBZBs7EmZcy+YUHhW3Tbv7gj79uJAX0+j9k3hfAWB m2y9aosmfCezRwaOXI67Xw4xviPndYsta78qf4prgWJn1wV41EirIhyN/YnFPpbe hPuOHfYCtALbxo= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:from:to:subject:date:message-id:mime-version :content-type; s=default; bh=dEhNqxWf0gQha8AC6mfqh2OfZyI=; b=YeK Rpz6q/rxGjQRANsZp1fM1fuZ06qHPPoV76xd3k2MM36E47yVtCOgHu03Cktfh39H zi/UMx89gSjV1bbTkEEgVXwXBD2c44qkul5FZR7zc2/coMYRn11frLQuRl+pm6KM drcWd8YlQmrCaOgjAXmghHjcA/ts4brw695MHEZg= Received: (qmail 1400 invoked by alias); 8 Oct 2019 11:04:51 -0000 Mailing-List: contact libc-alpha-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: libc-alpha-owner@sourceware.org Delivered-To: mailing list libc-alpha@sourceware.org Received: (qmail 1386 invoked by uid 89); 8 Oct 2019 11:04:51 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-18.5 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_SHORT, SPF_HELO_PASS, SPF_PASS autolearn=ham version=3.3.1 spammy=2177, Clean X-HELO: mx1.redhat.com From: Florian Weimer To: libc-alpha@sourceware.org Subject: [PATCH] wordexp: Split out command execution tests from posix/wordexp-test Date: Tue, 08 Oct 2019 13:04:41 +0200 Message-ID: <8736g3cxbq.fsf@oldenburg2.str.redhat.com> User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/26.2 (gnu/linux) MIME-Version: 1.0 Once wordexp switches to posix_spawn, testing for command execution based on fork handlers will not work anymore. Therefore, move these subtests into a new test, posix/tst-wordexp-nocmd, which uses a different form of command execution detection, based on PID namespaces. 2019-10-08 Florian Weimer * posix/Makefile (tests): Add tst-wordexp-nocmd. * posix/wordexp-test.c (__app_register_atfork): Remove function. (registered_forks): Remove variable. (register_fork): Remove function. (test_case): Remove WRDE_NOCMD tests. (main): Do not set up fork tracking. Remove integer overflow in division tests. (testit): Do not check for unexpected forks. * posix/tst-wordexp-nocmd.c: New file containing the tests removed from posix/wordexp-test.c. Reviewed-by: Adhemerval Zanella diff --git a/posix/Makefile b/posix/Makefile index 0579596d9e..fe031eb450 100644 --- a/posix/Makefile +++ b/posix/Makefile @@ -100,7 +100,8 @@ tests := test-errno tstgetopt testfnm runtests runptests \ tst-posix_fadvise tst-posix_fadvise64 \ tst-sysconf-empty-chroot tst-glob_symlinks tst-fexecve \ tst-glob-tilde test-ssize-max tst-spawn4 bug-regex37 \ - bug-regex38 tst-regcomp-truncated tst-spawn-chdir + bug-regex38 tst-regcomp-truncated tst-spawn-chdir \ + tst-wordexp-nocmd tests-internal := bug-regex5 bug-regex20 bug-regex33 \ tst-rfc3484 tst-rfc3484-2 tst-rfc3484-3 \ tst-glob_lstat_compat tst-spawn4-compat diff --git a/posix/tst-wordexp-nocmd.c b/posix/tst-wordexp-nocmd.c new file mode 100644 index 0000000000..b2f64c819f --- /dev/null +++ b/posix/tst-wordexp-nocmd.c @@ -0,0 +1,179 @@ +/* Test for (lack of) command execution in wordexp. + Copyright (C) 1997-2019 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +/* This test optionally counts PIDs in a PID namespace to detect + forks. Without kernel support for that, it will merely look at the + error codes from wordexp to check that no command execution + happens. */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Set to true if the test runs in a PID namespace and can therefore + use next_pid below. */ +static bool pid_tests_supported; + +/* The next PID, as returned from next_pid below. Only meaningful if + pid_tests_supported. */ +static pid_t expected_pid; + +/* Allocate the next PID and return it. The process is terminated. + Note that the test itself advances the next PID. */ +static pid_t +next_pid (void) +{ + pid_t pid = xfork (); + if (pid == 0) + _exit (0); + xwaitpid (pid, NULL, 0); + return pid; +} + +/* Check that evaluating PATTERN with WRDE_NOCMD results in + EXPECTED_ERROR. */ +static void +expect_failure (const char *pattern, int expected_error) +{ + printf ("info: testing pattern: %s\n", pattern); + wordexp_t w; + TEST_COMPARE (wordexp (pattern, &w, WRDE_NOCMD), expected_error); + if (pid_tests_supported) + TEST_COMPARE (expected_pid++, next_pid ()); +} + +/* Run all the tests. Invoked with different IFS values. */ +static void +run_tests (void) +{ + /* Integer overflow in division. */ + { + static const char *const numbers[] = { + "0", + "1", + "65536", + "2147483648", + "4294967296" + "9223372036854775808", + "18446744073709551616", + "170141183460469231731687303715884105728", + "340282366920938463463374607431768211456", + NULL + }; + + for (const char *const *num = numbers; *num != NULL; ++num) + { + wordexp_t w; + char pattern[256]; + snprintf (pattern, sizeof (pattern), "$[(-%s)/(-1)]", *num); + int ret = wordexp (pattern, &w, WRDE_NOCMD); + if (ret == 0) + { + /* If the call is successful, the result must match the + original number. */ + TEST_COMPARE (w.we_wordc, 1); + TEST_COMPARE_STRING (w.we_wordv[0], *num); + TEST_COMPARE_STRING (w.we_wordv[1], NULL); + wordfree (&w); + } + else + /* Otherwise, the test must fail with a syntax error. */ + TEST_COMPARE (ret, WRDE_SYNTAX); + + /* In both cases, command execution is not permitted. */ + if (pid_tests_supported) + TEST_COMPARE (expected_pid++, next_pid ()); + } + } + + /* (Lack of) command execution tests. */ + + expect_failure ("$(ls)", WRDE_CMDSUB); + + /* Test for CVE-2014-7817. We test 3 combinations of command + substitution inside an arithmetic expression to make sure that + no commands are executed and error is returned. */ + expect_failure ("$((`echo 1`))", WRDE_CMDSUB); + expect_failure ("$((1+`echo 1`))", WRDE_CMDSUB); + expect_failure ("$((1+$((`echo 1`))))", WRDE_CMDSUB); + + expect_failure ("$[1/0]", WRDE_SYNTAX); /* BZ 18100. */ +} + +static void +subprocess (void *closure) +{ + expected_pid = 2; + if (pid_tests_supported) + TEST_COMPARE (expected_pid++, next_pid ()); + + /* Check that triggering command execution via wordexp results in a + PID increase. */ + if (pid_tests_supported) + { + wordexp_t w; + TEST_COMPARE (wordexp ("$(echo Test)", &w, 0), 0); + TEST_COMPARE (w.we_wordc, 1); + TEST_COMPARE_STRING (w.we_wordv[0], "Test"); + TEST_COMPARE_STRING (w.we_wordv[1], NULL); + wordfree (&w); + + pid_t n = next_pid (); + printf ("info: self-test resulted in PID %d (processes created: %d)\n", + (int) n, (int) (n - expected_pid)); + TEST_VERIFY (n > expected_pid); + expected_pid = n + 1; + } + + puts ("info: testing without IFS"); + unsetenv ("IFS"); + run_tests (); + + puts ("info: testing with IFS"); + TEST_COMPARE (setenv ("IFS", " \t\n", 1), 0); + run_tests (); +} + +static int +do_test (void) +{ + support_become_root (); + +#ifdef CLONE_NEWPID + if (unshare (CLONE_NEWPID) != 0) + printf ("warning: unshare (CLONE_NEW_PID) failed: %m\n" + "warning: This leads to reduced test coverage.\n"); + else + pid_tests_supported = true; +#else + printf ("warning: CLONE_NEW_PID not available.\n" + "warning: This leads to reduced test coverage.\n"); +#endif + + /* CLONE_NEWPID only has an effect after fork. */ + support_isolate_in_subprocess (subprocess, NULL); + + return 0; +} + +#include diff --git a/posix/wordexp-test.c b/posix/wordexp-test.c index 957184cf47..a4d8bcf1da 100644 --- a/posix/wordexp-test.c +++ b/posix/wordexp-test.c @@ -31,23 +31,6 @@ #define IFS " \n\t" -extern int __register_atfork (void (*) (void), void (*) (void), void (*) (void), void *); - -static int __app_register_atfork (void (*prepare) (void), void (*parent) (void), void (*child) (void)) -{ - return __register_atfork (prepare, parent, child, __dso_handle); -} - -/* Number of forks seen. */ -static int registered_forks; - -/* For each fork increment the fork count. */ -static void -register_fork (void) -{ - registered_forks++; -} - struct test_case_struct { int retval; @@ -217,7 +200,6 @@ struct test_case_struct { WRDE_BADCHAR, NULL, "close-paren)", 0, 0, { NULL, }, IFS }, { WRDE_BADCHAR, NULL, "{open-brace", 0, 0, { NULL, }, IFS }, { WRDE_BADCHAR, NULL, "close-brace}", 0, 0, { NULL, }, IFS }, - { WRDE_CMDSUB, NULL, "$(ls)", WRDE_NOCMD, 0, { NULL, }, IFS }, { WRDE_BADVAL, NULL, "$var", WRDE_UNDEF, 0, { NULL, }, IFS }, { WRDE_BADVAL, NULL, "$9", WRDE_UNDEF, 0, { NULL, }, IFS }, { WRDE_SYNTAX, NULL, "$[50+20))", 0, 0, { NULL, }, IFS }, @@ -227,17 +209,10 @@ struct test_case_struct { WRDE_SYNTAX, NULL, "$((2+))", 0, 0, { NULL, }, IFS }, { WRDE_SYNTAX, NULL, "`", 0, 0, { NULL, }, IFS }, { WRDE_SYNTAX, NULL, "$((010+4+))", 0, 0, { NULL }, IFS }, - /* Test for CVE-2014-7817. We test 3 combinations of command - substitution inside an arithmetic expression to make sure that - no commands are executed and error is returned. */ - { WRDE_CMDSUB, NULL, "$((`echo 1`))", WRDE_NOCMD, 0, { NULL, }, IFS }, - { WRDE_CMDSUB, NULL, "$((1+`echo 1`))", WRDE_NOCMD, 0, { NULL, }, IFS }, - { WRDE_CMDSUB, NULL, "$((1+$((`echo 1`))))", WRDE_NOCMD, 0, { NULL, }, IFS }, { WRDE_SYNTAX, NULL, "`\\", 0, 0, { NULL, }, IFS }, /* BZ 18042 */ { WRDE_SYNTAX, NULL, "${", 0, 0, { NULL, }, IFS }, /* BZ 18043 */ { WRDE_SYNTAX, NULL, "L${a:", 0, 0, { NULL, }, IFS }, /* BZ 18043#c4 */ - { WRDE_SYNTAX, NULL, "$[1/0]", WRDE_NOCMD, 0, {NULL, }, IFS }, /* BZ 18100 */ { -1, NULL, NULL, 0, 0, { NULL, }, IFS }, }; @@ -290,15 +265,6 @@ main (int argc, char *argv[]) return -1; } - /* If we are not allowed to do command substitution, we install - fork handlers to verify that no forks happened. No forks should - happen at all if command substitution is disabled. */ - if (__app_register_atfork (register_fork, NULL, NULL) != 0) - { - printf ("Failed to register fork handler.\n"); - return -1; - } - for (test = 0; test_case[test].retval != -1; test++) if (testit (&test_case[test])) ++fail; @@ -363,45 +329,6 @@ main (int argc, char *argv[]) ++fail; } - /* Integer overflow in division. */ - { - static const char *const numbers[] = { - "0", - "1", - "65536", - "2147483648", - "4294967296" - "9223372036854775808", - "18446744073709551616", - "170141183460469231731687303715884105728", - "340282366920938463463374607431768211456", - NULL - }; - - for (const char *const *num = numbers; *num; ++num) - { - wordexp_t p; - char pattern[256]; - snprintf (pattern, sizeof (pattern), "$[(-%s)/(-1)]", *num); - int ret = wordexp (pattern, &p, WRDE_NOCMD); - if (ret == 0) - { - if (p.we_wordc != 1 || strcmp (p.we_wordv[0], *num) != 0) - { - printf ("Integer overflow for \"%s\" failed", pattern); - ++fail; - } - wordfree (&p); - } - else if (ret != WRDE_SYNTAX) - { - printf ("Integer overflow for \"%s\" failed with %d", - pattern, ret); - ++fail; - } - } - } - puts ("tests completed, now cleaning up"); /* Clean up */ @@ -472,9 +399,6 @@ testit (struct test_case_struct *tc) fflush (NULL); const char *words = at_page_end (tc->words); - if (tc->flags & WRDE_NOCMD) - registered_forks = 0; - if (tc->flags & WRDE_APPEND) { /* initial wordexp() call, to be appended to */ @@ -486,13 +410,6 @@ testit (struct test_case_struct *tc) } retval = wordexp (words, &we, tc->flags); - if ((tc->flags & WRDE_NOCMD) - && (registered_forks > 0)) - { - printf ("FAILED fork called for WRDE_NOCMD\n"); - return 1; - } - if (tc->flags & WRDE_DOOFFS) start_offs = sav_we.we_offs;