commit 58b930ae216bfa98cd60212b954b07b9963d6d04 Author: Siddhesh Poyarekar Date: Wed Sep 10 21:51:50 2014 +0530 Return failure in getnetgrent only when all netgroups have been searched (#17363) The netgroups lookup code fails when one of the groups in the search tree is empty. In such a case it only returns the leaves of the tree after the blank netgroup. This is because the line parser returns a NOTFOUND status when the netgroup exists but is empty. The __getnetgrent_internal implementation needs to be fixed to try remaining groups if the current group is entry. This patch implements this fix. Tested on x86_64. [BZ #17363] * inet/getnetgrent_r.c (__internal_getnetgrent_r): Try next group if the current group is empty. diff --git a/inet/getnetgrent_r.c b/inet/getnetgrent_r.c index f6d064d..e101537 100644 --- a/inet/getnetgrent_r.c +++ b/inet/getnetgrent_r.c @@ -297,7 +297,10 @@ __internal_getnetgrent_r (char **hostp, char **userp, char **domainp, { status = DL_CALL_FCT (*fct, (datap, buffer, buflen, &errno)); - if (status == NSS_STATUS_RETURN) + if (status == NSS_STATUS_RETURN + /* The service returned a NOTFOUND, but there are more groups that we + need to resolve before we give up. */ + || (status == NSS_STATUS_NOTFOUND && datap->needed_groups != NULL)) { /* This was the last one for this group. Look at next group if available. */ commit 984c0ea97f649c869130a1ff099098e2b6f70aad Author: Tim Lammens Date: Thu Sep 11 10:35:54 2014 +0530 Fix memory leak in libio/wfileops.c do_ftell_wide [BZ #17370] diff --git a/libio/wfileops.c b/libio/wfileops.c index f123add..ebc06e8 100644 --- a/libio/wfileops.c +++ b/libio/wfileops.c @@ -711,6 +711,7 @@ do_ftell_wide (_IO_FILE *fp) return WEOF; offset += outstop - out; + free (out); } /* We don't trust _IO_read_end to represent the current file offset commit 52ffbdf25a1100986f4ae27bb0febbe5a722ab25 Author: Florian Weimer Date: Wed Sep 10 20:29:15 2014 +0200 malloc: additional unlink hardening for non-small bins [BZ #17344] Turn two asserts into a conditional call to malloc_printerr. The memory locations are accessed later anyway, so the performance impact is minor. diff --git a/malloc/malloc.c b/malloc/malloc.c index 6ee3840..6cbe9f3 100644 --- a/malloc/malloc.c +++ b/malloc/malloc.c @@ -1418,8 +1418,10 @@ typedef struct malloc_chunk *mbinptr; BK->fd = FD; \ if (!in_smallbin_range (P->size) \ && __builtin_expect (P->fd_nextsize != NULL, 0)) { \ - assert (P->fd_nextsize->bk_nextsize == P); \ - assert (P->bk_nextsize->fd_nextsize == P); \ + if (__builtin_expect (P->fd_nextsize->bk_nextsize != P, 0) \ + || __builtin_expect (P->bk_nextsize->fd_nextsize != P, 0)) \ + malloc_printerr (check_action, \ + "corrupted double-linked list (not small)", P);\ if (FD->fd_nextsize == NULL) { \ if (P->fd_nextsize == P) \ FD->fd_nextsize = FD->bk_nextsize = FD; \ commit a7b872687073decdcc7effc2289877d69058aca9 Author: Andreas Schwab Date: Sat Sep 13 10:10:29 2014 +0200 Handle zero prefix length in getifaddrs (BZ #17371) diff --git a/sysdeps/unix/sysv/linux/ifaddrs.c b/sysdeps/unix/sysv/linux/ifaddrs.c index 2c04e17..a47b2ed 100644 --- a/sysdeps/unix/sysv/linux/ifaddrs.c +++ b/sysdeps/unix/sysv/linux/ifaddrs.c @@ -770,20 +770,17 @@ getifaddrs_internal (struct ifaddrs **ifap) if (cp != NULL) { - char c; unsigned int preflen; - if ((max_prefixlen > 0) && - (ifam->ifa_prefixlen > max_prefixlen)) + if (ifam->ifa_prefixlen > max_prefixlen) preflen = max_prefixlen; else preflen = ifam->ifa_prefixlen; - for (i = 0; i < ((preflen - 1) / 8); i++) + for (i = 0; i < preflen / 8; i++) *cp++ = 0xff; - c = 0xff; - c <<= ((128 - preflen) % 8); - *cp = c; + if (preflen % 8) + *cp = 0xff << (8 - preflen % 8); } } } commit 545583d664b64ff234b99aca0d85e99c8a55808f Author: Siddhesh Poyarekar Date: Tue Sep 16 14:20:45 2014 +0530 Fix memory leak in error path of do_ftell_wide (BZ #17370) diff --git a/libio/wfileops.c b/libio/wfileops.c index ebc06e8..c5ec5f7 100644 --- a/libio/wfileops.c +++ b/libio/wfileops.c @@ -708,7 +708,10 @@ do_ftell_wide (_IO_FILE *fp) sequences must be complete since they are accepted as wchar_t; if not, then that is an error. */ if (__glibc_unlikely (status != __codecvt_ok)) - return WEOF; + { + free (out); + return WEOF; + } offset += outstop - out; free (out); commit 04b76b5aa8b2d1d19066e42dd1a56a38f34e274c Author: Andreas Schwab Date: Thu Oct 30 12:18:48 2014 +0100 Don't error out writing a multibyte character to an unbuffered stream (bug 17522) diff --git a/libio/Makefile b/libio/Makefile index 56952ce..2742128 100644 --- a/libio/Makefile +++ b/libio/Makefile @@ -61,7 +61,7 @@ tests = tst_swprintf tst_wprintf tst_swscanf tst_wscanf tst_getwc tst_putwc \ bug-memstream1 bug-wmemstream1 \ tst-setvbuf1 tst-popen1 tst-fgetwc bug-wsetpos tst-fseek \ tst-fwrite-error tst-ftell-partial-wide tst-ftell-active-handler \ - tst-ftell-append + tst-ftell-append tst-fputws ifeq (yes,$(build-shared)) # Add test-fopenloc only if shared library is enabled since it depends on # shared localedata objects. diff --git a/libio/tst-fputws.c b/libio/tst-fputws.c new file mode 100644 index 0000000..09f53df --- /dev/null +++ b/libio/tst-fputws.c @@ -0,0 +1,39 @@ +/* Test that we can write a multibyte character to an unbuffered stream. + Copyright (C) 2014 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 + . */ + +#include +#include +#include + +static int +do_test (void) +{ + const wchar_t str[] = L"\xbe\n"; + + setlocale (LC_ALL, "en_US.UTF-8"); + setvbuf (stdout, NULL, _IONBF, 0); + + if (fputws (str, stdout) < 0) + return 1; + + return 0; +} + +#define TEST_FUNCTION do_test () + +#include diff --git a/libio/wfileops.c b/libio/wfileops.c index c5ec5f7..6a088b1 100644 --- a/libio/wfileops.c +++ b/libio/wfileops.c @@ -75,17 +75,32 @@ _IO_wdo_write (fp, data, to_do) { enum __codecvt_result result; const wchar_t *new_data; + char mb_buf[MB_LEN_MAX]; + char *write_base, *write_ptr, *buf_end; + + if (fp->_IO_write_ptr - fp->_IO_write_base < sizeof (mb_buf)) + { + /* Make sure we have room for at least one multibyte + character. */ + write_ptr = write_base = mb_buf; + buf_end = mb_buf + sizeof (mb_buf); + } + else + { + write_ptr = fp->_IO_write_ptr; + write_base = fp->_IO_write_base; + buf_end = fp->_IO_buf_end; + } /* Now convert from the internal format into the external buffer. */ result = (*cc->__codecvt_do_out) (cc, &fp->_wide_data->_IO_state, data, data + to_do, &new_data, - fp->_IO_write_ptr, - fp->_IO_buf_end, - &fp->_IO_write_ptr); + write_ptr, + buf_end, + &write_ptr); /* Write out what we produced so far. */ - if (_IO_new_do_write (fp, fp->_IO_write_base, - fp->_IO_write_ptr - fp->_IO_write_base) == EOF) + if (_IO_new_do_write (fp, write_base, write_ptr - write_base) == EOF) /* Something went wrong. */ return WEOF; commit a39208bd7fb76c1b01c127b4c61f9bfd915bfe7c Author: Carlos O'Donell Date: Wed Nov 19 11:44:12 2014 -0500 CVE-2014-7817: wordexp fails to honour WRDE_NOCMD. The function wordexp() fails to properly handle the WRDE_NOCMD flag when processing arithmetic inputs in the form of "$((... ``))" where "..." can be anything valid. The backticks in the arithmetic epxression are evaluated by in a shell even if WRDE_NOCMD forbade command substitution. This allows an attacker to attempt to pass dangerous commands via constructs of the above form, and bypass the WRDE_NOCMD flag. This patch fixes this by checking for WRDE_NOCMD in exec_comm(), the only place that can execute a shell. All other checks for WRDE_NOCMD are superfluous and removed. We expand the testsuite and add 3 new regression tests of roughly the same form but with a couple of nested levels. On top of the 3 new tests we add fork validation to the WRDE_NOCMD testing. If any forks are detected during the execution of a wordexp() call with WRDE_NOCMD, the test is marked as failed. This is slightly heuristic since vfork might be used in the future, but it provides a higher level of assurance that no shells were executed as part of command substitution with WRDE_NOCMD in effect. In addition it doesn't require libpthread or libdl, instead we use the public implementation namespace function __register_atfork (already part of the public ABI for libpthread). Tested on x86_64 with no regressions. diff --git a/posix/wordexp-test.c b/posix/wordexp-test.c index 4957006..bdd65e4 100644 --- a/posix/wordexp-test.c +++ b/posix/wordexp-test.c @@ -27,6 +27,25 @@ #define IFS " \n\t" +extern void *__dso_handle __attribute__ ((__weak__, __visibility__ ("hidden"))); +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 == NULL ? NULL : __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; @@ -206,6 +225,12 @@ 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 }, { -1, NULL, NULL, 0, 0, { NULL, }, IFS }, }; @@ -258,6 +283,15 @@ 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; @@ -367,6 +401,9 @@ testit (struct test_case_struct *tc) printf ("Test %d (%s): ", ++tests, tc->words); + if (tc->flags & WRDE_NOCMD) + registered_forks = 0; + if (tc->flags & WRDE_APPEND) { /* initial wordexp() call, to be appended to */ @@ -378,6 +415,13 @@ testit (struct test_case_struct *tc) } retval = wordexp (tc->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; diff --git a/posix/wordexp.c b/posix/wordexp.c index b6b65dd..26f3a26 100644 --- a/posix/wordexp.c +++ b/posix/wordexp.c @@ -893,6 +893,10 @@ exec_comm (char *comm, char **word, size_t *word_length, size_t *max_length, pid_t pid; int noexec = 0; + /* Do nothing if command substitution should not succeed. */ + if (flags & WRDE_NOCMD) + return WRDE_CMDSUB; + /* Don't fork() unless necessary */ if (!comm || !*comm) return 0; @@ -2082,9 +2086,6 @@ parse_dollars (char **word, size_t *word_length, size_t *max_length, } } - if (flags & WRDE_NOCMD) - return WRDE_CMDSUB; - (*offset) += 2; return parse_comm (word, word_length, max_length, words, offset, flags, quoted? NULL : pwordexp, ifs, ifs_white); @@ -2196,9 +2197,6 @@ parse_dquote (char **word, size_t *word_length, size_t *max_length, break; case '`': - if (flags & WRDE_NOCMD) - return WRDE_CMDSUB; - ++(*offset); error = parse_backtick (word, word_length, max_length, words, offset, flags, NULL, NULL, NULL); @@ -2357,12 +2355,6 @@ wordexp (const char *words, wordexp_t *pwordexp, int flags) break; case '`': - if (flags & WRDE_NOCMD) - { - error = WRDE_CMDSUB; - goto do_error; - } - ++words_offset; error = parse_backtick (&word, &word_length, &max_length, words, &words_offset, flags, pwordexp, ifs,