From patchwork Tue Jun 27 06:42:09 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Peter Korsgaard X-Patchwork-Id: 781011 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from silver.osuosl.org (smtp3.osuosl.org [140.211.166.136]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3wxbvS1Q8dz9s2G for ; Tue, 27 Jun 2017 16:42:28 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="s8Jk1utu"; dkim-atps=neutral Received: from localhost (localhost [127.0.0.1]) by silver.osuosl.org (Postfix) with ESMTP id 49A8125485; Tue, 27 Jun 2017 06:42:26 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from silver.osuosl.org ([127.0.0.1]) by localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id xxM8b+Kcy-lZ; Tue, 27 Jun 2017 06:42:23 +0000 (UTC) Received: from ash.osuosl.org (ash.osuosl.org [140.211.166.34]) by silver.osuosl.org (Postfix) with ESMTP id 916402EF38; Tue, 27 Jun 2017 06:42:23 +0000 (UTC) X-Original-To: buildroot@lists.busybox.net Delivered-To: buildroot@osuosl.org Received: from hemlock.osuosl.org (smtp2.osuosl.org [140.211.166.133]) by ash.osuosl.org (Postfix) with ESMTP id E43961C286E for ; Tue, 27 Jun 2017 06:42:21 +0000 (UTC) Received: from localhost (localhost [127.0.0.1]) by hemlock.osuosl.org (Postfix) with ESMTP id D14C2858F4 for ; Tue, 27 Jun 2017 06:42:21 +0000 (UTC) X-Virus-Scanned: amavisd-new at osuosl.org Received: from hemlock.osuosl.org ([127.0.0.1]) by localhost (.osuosl.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id qjpIXfqGKosF for ; Tue, 27 Jun 2017 06:42:20 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from mail-wm0-f66.google.com (mail-wm0-f66.google.com [74.125.82.66]) by hemlock.osuosl.org (Postfix) with ESMTPS id E4A1E858B4 for ; Tue, 27 Jun 2017 06:42:19 +0000 (UTC) Received: by mail-wm0-f66.google.com with SMTP id f134so3965866wme.1 for ; Mon, 26 Jun 2017 23:42:19 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=sender:from:to:cc:subject:date:message-id; bh=W/2KQmfKbVzdlpIRGFIYBTp6L4o3YZVg6Xe+aRxSmjk=; b=s8Jk1utuhYO/qYi7jkoRT8TQKA4NrBpuJS6srd9dl/naTkKDrdx/TNHUc0qdTydLxr zqmxPJa2nAOSKrHo87ekjxK8L5Rd9woPGupC8aZf2Tekmmoz9jeDi1aQcOsJ7HslYa8Q 31WbHcFH5rzpbGLBBIVk4ZvpAiCTPThydq/7PSYGfFq3reQjYVF4QxBpCRxYYVkzkpJI pOl3+o+22X47DqeAjFd85Kw5RQSiFXjl7xqxV0UZ6AG0Z6sI9vVhRtMVqnIJS7cagPbw o1GB9wI1wrngrsRA0ZfU1DJQsLt3hfXVVYQ8OPuR5j5ccwVzxBCK2PSwAeejVhmz8JPM UY7A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:from:to:cc:subject:date:message-id; bh=W/2KQmfKbVzdlpIRGFIYBTp6L4o3YZVg6Xe+aRxSmjk=; b=sdO5MpVyLJTOLciX2BRu00OmJklokP78X8mjEpqbCWuQRhuEhYD3Z6vwvTP2ORbi6q 92buKV6LkFfbeXGtoFd9V4Ye6KZly18NagAHSuoYA/kH98BUskXPeEcVtDxOVXY9aj8t xWKBAisDTUgzn6aYf3ixmcAQdnBLzijX4QfbUwwBXKfJB7RhU5QnjFxnpmzRJ6HsSune vq+yCQWUEFKf3CP93UF1U0Op7zO74q5YK3KQ+dA0773ttEjMGsrcfKFdGKmDgs+qU+hY 6M68JujM6WTvnSACo0veQUaqABgOwTTe1E6EAL//0Tvv1ID1S/4EEZCQOhJb9R+zPkcQ DhUw== X-Gm-Message-State: AKS2vOwFp8odIvXnOyK8rNQ+w6Gv/v+jyrA0woZVkSMjLZtAyHpAOCHj wzlM4onvox9JngYWlHw= X-Received: by 10.80.138.218 with SMTP id k26mr2680254edk.133.1498545737614; Mon, 26 Jun 2017 23:42:17 -0700 (PDT) Received: from dell.be.48ers.dk (d51a5bc31.access.telenet.be. [81.165.188.49]) by smtp.gmail.com with ESMTPSA id n26sm1419185edd.51.2017.06.26.23.42.16 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Mon, 26 Jun 2017 23:42:16 -0700 (PDT) Received: from peko by dell.be.48ers.dk with local (Exim 4.88) (envelope-from ) id 1dPkCV-0007gh-VB; Tue, 27 Jun 2017 08:42:15 +0200 From: Peter Korsgaard To: buildroot@buildroot.org Date: Tue, 27 Jun 2017 08:42:09 +0200 Message-Id: <20170627064209.29481-1-peter@korsgaard.com> X-Mailer: git-send-email 2.11.0 Subject: [Buildroot] [PATCH] glibc: add upstream security patches fixing CVE-2017-1000366 (stack clash) X-BeenThere: buildroot@busybox.net X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: Discussion and development of buildroot List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: buildroot-bounces@busybox.net Sender: "buildroot" glibc contains a vulnerability that allows specially crafted LD_LIBRARY_PATH values to manipulate the heap/stack, causing them to alias, potentially resulting in arbitrary code execution. Please note that additional hardening changes have been made to glibc to prevent manipulation of stack and heap memory but these issues are not directly exploitable, as such they have not been given a CVE. https://www.qualys.com/2017/06/19/stack-clash/stack-clash.txt Patches are identical to upstream, except that the ChangeLog modifications have been stripped. Signed-off-by: Peter Korsgaard --- ...00366-Ignore-LD_LIBRARY_PATH-for-AT_SECUR.patch | 35 ++++ ...ject-overly-long-LD_PRELOAD-path-elements.patch | 122 ++++++++++++ ...Reject-overly-long-LD_AUDIT-path-elements.patch | 204 +++++++++++++++++++++ 3 files changed, 361 insertions(+) create mode 100644 package/glibc/0002-CVE-2017-1000366-Ignore-LD_LIBRARY_PATH-for-AT_SECUR.patch create mode 100644 package/glibc/0003-ld.so-Reject-overly-long-LD_PRELOAD-path-elements.patch create mode 100644 package/glibc/0004-ld.so-Reject-overly-long-LD_AUDIT-path-elements.patch diff --git a/package/glibc/0002-CVE-2017-1000366-Ignore-LD_LIBRARY_PATH-for-AT_SECUR.patch b/package/glibc/0002-CVE-2017-1000366-Ignore-LD_LIBRARY_PATH-for-AT_SECUR.patch new file mode 100644 index 0000000000..d701294deb --- /dev/null +++ b/package/glibc/0002-CVE-2017-1000366-Ignore-LD_LIBRARY_PATH-for-AT_SECUR.patch @@ -0,0 +1,35 @@ +From f6110a8fee2ca36f8e2d2abecf3cba9fa7b8ea7d Mon Sep 17 00:00:00 2001 +From: Florian Weimer +Date: Mon, 19 Jun 2017 17:09:55 +0200 +Subject: [PATCH] CVE-2017-1000366: Ignore LD_LIBRARY_PATH for AT_SECURE=1 + programs [BZ #21624] + +LD_LIBRARY_PATH can only be used to reorder system search paths, which +is not useful functionality. + +This makes an exploitable unbounded alloca in _dl_init_paths unreachable +for AT_SECURE=1 programs. + +[Peter: Drop ChangeLog modification] +Signed-off-by: Peter Korsgaard +--- + elf/rtld.c | 3 ++- + 1 file changed, 8 insertions(+), 1 deletion(-) + +diff --git a/elf/rtld.c b/elf/rtld.c +index 2446a87680..2269dbec81 100644 +--- a/elf/rtld.c ++++ b/elf/rtld.c +@@ -2422,7 +2422,8 @@ process_envvars (enum mode *modep) + + case 12: + /* The library search path. */ +- if (memcmp (envline, "LIBRARY_PATH", 12) == 0) ++ if (!__libc_enable_secure ++ && memcmp (envline, "LIBRARY_PATH", 12) == 0) + { + library_path = &envline[13]; + break; +-- +2.11.0 + diff --git a/package/glibc/0003-ld.so-Reject-overly-long-LD_PRELOAD-path-elements.patch b/package/glibc/0003-ld.so-Reject-overly-long-LD_PRELOAD-path-elements.patch new file mode 100644 index 0000000000..df410931da --- /dev/null +++ b/package/glibc/0003-ld.so-Reject-overly-long-LD_PRELOAD-path-elements.patch @@ -0,0 +1,122 @@ +From 6d0ba622891bed9d8394eef1935add53003b12e8 Mon Sep 17 00:00:00 2001 +From: Florian Weimer +Date: Mon, 19 Jun 2017 22:31:04 +0200 +Subject: [PATCH] ld.so: Reject overly long LD_PRELOAD path elements + +[Peter: Drop ChangeLog modification] +Signed-off-by: Peter Korsgaard +--- + elf/rtld.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++------------ + 1 file changed, 72 insertions(+), 16 deletions(-) + +diff --git a/elf/rtld.c b/elf/rtld.c +index 2269dbec81..86ae20c83f 100644 +--- a/elf/rtld.c ++++ b/elf/rtld.c +@@ -99,6 +99,35 @@ uintptr_t __pointer_chk_guard_local + strong_alias (__pointer_chk_guard_local, __pointer_chk_guard) + #endif + ++/* Length limits for names and paths, to protect the dynamic linker, ++ particularly when __libc_enable_secure is active. */ ++#ifdef NAME_MAX ++# define SECURE_NAME_LIMIT NAME_MAX ++#else ++# define SECURE_NAME_LIMIT 255 ++#endif ++#ifdef PATH_MAX ++# define SECURE_PATH_LIMIT PATH_MAX ++#else ++# define SECURE_PATH_LIMIT 1024 ++#endif ++ ++/* Check that AT_SECURE=0, or that the passed name does not contain ++ directories and is not overly long. Reject empty names ++ unconditionally. */ ++static bool ++dso_name_valid_for_suid (const char *p) ++{ ++ if (__glibc_unlikely (__libc_enable_secure)) ++ { ++ /* Ignore pathnames with directories for AT_SECURE=1 ++ programs, and also skip overlong names. */ ++ size_t len = strlen (p); ++ if (len >= SECURE_NAME_LIMIT || memchr (p, '/', len) != NULL) ++ return false; ++ } ++ return *p != '\0'; ++} + + /* List of auditing DSOs. */ + static struct audit_list +@@ -718,6 +747,42 @@ static const char *preloadlist attribute_relro; + /* Nonzero if information about versions has to be printed. */ + static int version_info attribute_relro; + ++/* The LD_PRELOAD environment variable gives list of libraries ++ separated by white space or colons that are loaded before the ++ executable's dependencies and prepended to the global scope list. ++ (If the binary is running setuid all elements containing a '/' are ++ ignored since it is insecure.) Return the number of preloads ++ performed. */ ++unsigned int ++handle_ld_preload (const char *preloadlist, struct link_map *main_map) ++{ ++ unsigned int npreloads = 0; ++ const char *p = preloadlist; ++ char fname[SECURE_PATH_LIMIT]; ++ ++ while (*p != '\0') ++ { ++ /* Split preload list at space/colon. */ ++ size_t len = strcspn (p, " :"); ++ if (len > 0 && len < sizeof (fname)) ++ { ++ memcpy (fname, p, len); ++ fname[len] = '\0'; ++ } ++ else ++ fname[0] = '\0'; ++ ++ /* Skip over the substring and the following delimiter. */ ++ p += len; ++ if (*p != '\0') ++ ++p; ++ ++ if (dso_name_valid_for_suid (fname)) ++ npreloads += do_preload (fname, main_map, "LD_PRELOAD"); ++ } ++ return npreloads; ++} ++ + static void + dl_main (const ElfW(Phdr) *phdr, + ElfW(Word) phnum, +@@ -1464,23 +1529,8 @@ ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n", + + if (__glibc_unlikely (preloadlist != NULL)) + { +- /* The LD_PRELOAD environment variable gives list of libraries +- separated by white space or colons that are loaded before the +- executable's dependencies and prepended to the global scope +- list. If the binary is running setuid all elements +- containing a '/' are ignored since it is insecure. */ +- char *list = strdupa (preloadlist); +- char *p; +- + HP_TIMING_NOW (start); +- +- /* Prevent optimizing strsep. Speed is not important here. */ +- while ((p = (strsep) (&list, " :")) != NULL) +- if (p[0] != '\0' +- && (__builtin_expect (! __libc_enable_secure, 1) +- || strchr (p, '/') == NULL)) +- npreloads += do_preload (p, main_map, "LD_PRELOAD"); +- ++ npreloads += handle_ld_preload (preloadlist, main_map); + HP_TIMING_NOW (stop); + HP_TIMING_DIFF (diff, start, stop); + HP_TIMING_ACCUM_NT (load_time, diff); +-- +2.11.0 + diff --git a/package/glibc/0004-ld.so-Reject-overly-long-LD_AUDIT-path-elements.patch b/package/glibc/0004-ld.so-Reject-overly-long-LD_AUDIT-path-elements.patch new file mode 100644 index 0000000000..25e937bd10 --- /dev/null +++ b/package/glibc/0004-ld.so-Reject-overly-long-LD_AUDIT-path-elements.patch @@ -0,0 +1,204 @@ +From 81b82fb966ffbd94353f793ad17116c6088dedd9 Mon Sep 17 00:00:00 2001 +From: Florian Weimer +Date: Mon, 19 Jun 2017 22:32:12 +0200 +Subject: [PATCH] ld.so: Reject overly long LD_AUDIT path elements + +Also only process the last LD_AUDIT entry. + +[Peter: Drop ChangeLog modification] +Signed-off-by: Peter Korsgaard +--- + elf/rtld.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++++--------- + 1 file changed, 105 insertions(+), 15 deletions(-) + +diff --git a/elf/rtld.c b/elf/rtld.c +index 86ae20c83f..65647fb1c8 100644 +--- a/elf/rtld.c ++++ b/elf/rtld.c +@@ -129,13 +129,91 @@ dso_name_valid_for_suid (const char *p) + return *p != '\0'; + } + +-/* List of auditing DSOs. */ ++/* LD_AUDIT variable contents. Must be processed before the ++ audit_list below. */ ++const char *audit_list_string; ++ ++/* Cyclic list of auditing DSOs. audit_list->next is the first ++ element. */ + static struct audit_list + { + const char *name; + struct audit_list *next; + } *audit_list; + ++/* Iterator for audit_list_string followed by audit_list. */ ++struct audit_list_iter ++{ ++ /* Tail of audit_list_string still needing processing, or NULL. */ ++ const char *audit_list_tail; ++ ++ /* The list element returned in the previous iteration. NULL before ++ the first element. */ ++ struct audit_list *previous; ++ ++ /* Scratch buffer for returning a name which is part of ++ audit_list_string. */ ++ char fname[SECURE_NAME_LIMIT]; ++}; ++ ++/* Initialize an audit list iterator. */ ++static void ++audit_list_iter_init (struct audit_list_iter *iter) ++{ ++ iter->audit_list_tail = audit_list_string; ++ iter->previous = NULL; ++} ++ ++/* Iterate through both audit_list_string and audit_list. */ ++static const char * ++audit_list_iter_next (struct audit_list_iter *iter) ++{ ++ if (iter->audit_list_tail != NULL) ++ { ++ /* First iterate over audit_list_string. */ ++ while (*iter->audit_list_tail != '\0') ++ { ++ /* Split audit list at colon. */ ++ size_t len = strcspn (iter->audit_list_tail, ":"); ++ if (len > 0 && len < sizeof (iter->fname)) ++ { ++ memcpy (iter->fname, iter->audit_list_tail, len); ++ iter->fname[len] = '\0'; ++ } ++ else ++ /* Do not return this name to the caller. */ ++ iter->fname[0] = '\0'; ++ ++ /* Skip over the substring and the following delimiter. */ ++ iter->audit_list_tail += len; ++ if (*iter->audit_list_tail == ':') ++ ++iter->audit_list_tail; ++ ++ /* If the name is valid, return it. */ ++ if (dso_name_valid_for_suid (iter->fname)) ++ return iter->fname; ++ /* Otherwise, wrap around and try the next name. */ ++ } ++ /* Fall through to the procesing of audit_list. */ ++ } ++ ++ if (iter->previous == NULL) ++ { ++ if (audit_list == NULL) ++ /* No pre-parsed audit list. */ ++ return NULL; ++ /* Start of audit list. The first list element is at ++ audit_list->next (cyclic list). */ ++ iter->previous = audit_list->next; ++ return iter->previous->name; ++ } ++ if (iter->previous == audit_list) ++ /* Cyclic list wrap-around. */ ++ return NULL; ++ iter->previous = iter->previous->next; ++ return iter->previous->name; ++} ++ + #ifndef HAVE_INLINED_SYSCALLS + /* Set nonzero during loading and initialization of executable and + libraries, cleared before the executable's entry point runs. This +@@ -1305,11 +1383,13 @@ of this helper program; chances are you did not intend to run this program.\n\ + GL(dl_rtld_map).l_tls_modid = _dl_next_tls_modid (); + + /* If we have auditing DSOs to load, do it now. */ +- if (__glibc_unlikely (audit_list != NULL)) ++ bool need_security_init = true; ++ if (__glibc_unlikely (audit_list != NULL) ++ || __glibc_unlikely (audit_list_string != NULL)) + { +- /* Iterate over all entries in the list. The order is important. */ + struct audit_ifaces *last_audit = NULL; +- struct audit_list *al = audit_list->next; ++ struct audit_list_iter al_iter; ++ audit_list_iter_init (&al_iter); + + /* Since we start using the auditing DSOs right away we need to + initialize the data structures now. */ +@@ -1320,9 +1400,14 @@ of this helper program; chances are you did not intend to run this program.\n\ + use different values (especially the pointer guard) and will + fail later on. */ + security_init (); ++ need_security_init = false; + +- do ++ while (true) + { ++ const char *name = audit_list_iter_next (&al_iter); ++ if (name == NULL) ++ break; ++ + int tls_idx = GL(dl_tls_max_dtv_idx); + + /* Now it is time to determine the layout of the static TLS +@@ -1331,7 +1416,7 @@ of this helper program; chances are you did not intend to run this program.\n\ + no DF_STATIC_TLS bit is set. The reason is that we know + glibc will use the static model. */ + struct dlmopen_args dlmargs; +- dlmargs.fname = al->name; ++ dlmargs.fname = name; + dlmargs.map = NULL; + + const char *objname; +@@ -1344,7 +1429,7 @@ of this helper program; chances are you did not intend to run this program.\n\ + not_loaded: + _dl_error_printf ("\ + ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n", +- al->name, err_str); ++ name, err_str); + if (malloced) + free ((char *) err_str); + } +@@ -1448,10 +1533,7 @@ ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n", + goto not_loaded; + } + } +- +- al = al->next; + } +- while (al != audit_list->next); + + /* If we have any auditing modules, announce that we already + have two objects loaded. */ +@@ -1715,7 +1797,7 @@ ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n", + if (tcbp == NULL) + tcbp = init_tls (); + +- if (__glibc_likely (audit_list == NULL)) ++ if (__glibc_likely (need_security_init)) + /* Initialize security features. But only if we have not done it + earlier. */ + security_init (); +@@ -2346,9 +2428,7 @@ process_dl_audit (char *str) + char *p; + + while ((p = (strsep) (&str, ":")) != NULL) +- if (p[0] != '\0' +- && (__builtin_expect (! __libc_enable_secure, 1) +- || strchr (p, '/') == NULL)) ++ if (dso_name_valid_for_suid (p)) + { + /* This is using the local malloc, not the system malloc. The + memory can never be freed. */ +@@ -2412,7 +2492,7 @@ process_envvars (enum mode *modep) + break; + } + if (memcmp (envline, "AUDIT", 5) == 0) +- process_dl_audit (&envline[6]); ++ audit_list_string = &envline[6]; + break; + + case 7: +-- +2.11.0 +