From patchwork Wed Jun 21 05:58:35 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kees Cook X-Patchwork-Id: 9800771 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id A55F2601C0 for ; Wed, 21 Jun 2017 05:58:54 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 9127F28516 for ; Wed, 21 Jun 2017 05:58:54 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 84E4628528; Wed, 21 Jun 2017 05:58:54 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-4.1 required=2.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_MED,T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from mother.openwall.net (mother.openwall.net [195.42.179.200]) by mail.wl.linuxfoundation.org (Postfix) with SMTP id 695AA28516 for ; Wed, 21 Jun 2017 05:58:52 +0000 (UTC) Received: (qmail 14301 invoked by uid 550); 21 Jun 2017 05:58:50 -0000 Mailing-List: contact kernel-hardening-help@lists.openwall.com; run by ezmlm Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: List-ID: Delivered-To: mailing list kernel-hardening@lists.openwall.com Received: (qmail 14280 invoked from network); 21 Jun 2017 05:58:49 -0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=date:from:to:cc:subject:message-id:mime-version:content-disposition; bh=qoIus0BAPb13rtWmAiOkEAkpqZr187nOZFXGtS1vbFY=; b=crUlz4v2iX1IigJNQPGu8UFL0ujdszGdzNgNSWZX7C0CRF5rcAOq6bLHj0DO0ZZl9c /hMKel9S2Ye54QO25HCX0csM8yC73fYDB1bBBpgURoAV2zjCCJGZVyzXwjbqdDMDJNUR tuSyWoyAPMeLrsO/hy25UXwJU5GBY/V1+c4j0= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:from:to:cc:subject:message-id:mime-version :content-disposition; bh=qoIus0BAPb13rtWmAiOkEAkpqZr187nOZFXGtS1vbFY=; b=preb91UTgMLk7VDZmUKfiKloy8GivtYt/EHWX2LwdRCvkt1OqAyg4d1J7SyTE1+YdG gsHgW8hqcK4mUkd3Agc7TeNPxd7eLiiTUiO0p0keha9QPLXlAtfPfkV15g2MQ813PTwS 1sdF+vvFoBsmj8HRZ3zegC38nB7/RcxBy5NVTr5fYvUy4m0scGAn35kfViZvXxLj3pB6 0HtiKOx3IsZOqUbxQ5ruxMeGOQY/qEj/fhjjWwC80FvWgICayzurkgoFnE2tI2NThwRC On90QKNtjC9fxe86caleviE/kgKmIhI7mOriSEB6eeIaJ52lAtc4uM/P4Sx0tEJ9OZjj Rm3Q== X-Gm-Message-State: AKS2vOxP/ptioznzXgLyfHgzLxiACtJ7ZJgn8rgTIOkWVvYQ0DpM1cRB MEQyOEBMjATjZ+1A X-Received: by 10.99.43.5 with SMTP id r5mr34881843pgr.135.1498024717151; Tue, 20 Jun 2017 22:58:37 -0700 (PDT) Date: Tue, 20 Jun 2017 22:58:35 -0700 From: Kees Cook To: linux-kernel@vger.kernel.org Cc: Rik van Riel , Daniel Micay , Qualys Security Advisory , Thomas Gleixner , Ingo Molnar , "H. Peter Anvin" , x86@kernel.org, Alexander Viro , Dmitry Safonov , Masahiro Yamada , Grzegorz Andrejczuk , linux-fsdevel@vger.kernel.org, kernel-hardening@lists.openwall.com Message-ID: <20170621055835.GA27467@beast> MIME-Version: 1.0 Content-Disposition: inline Subject: [kernel-hardening] [PATCH] [RFC] binfmt_elf: Use ELF_ET_DYN_BASE only for PIE X-Virus-Scanned: ClamAV using ClamSMTP The ELF_ET_DYN_BASE position was originally intended to keep loaders away from ET_EXEC binaries. (For example, running "/lib/ld-linux.so.2 /bin/cat" might cause the subsequent load of /bin/cat into where the loader had been loaded.) With the advent of PIE (ET_DYN binaries with an INTERP Program Header), ELF_ET_DYN_BASE continued to be used since the kernel was only looking at ET_DYN. However, since ELF_ET_DYN_BASE is traditionally set at the top 1/3rd of the TASK_SIZE, a substantial portion of the address space is unused. For 32-bit tasks when RLIMIT_STACK is set to RLIM_INFINITY, programs are loaded below the mmap region. This means they can be made to collide (CVE-2017-1000370) or nearly collide (CVE-2017-1000371) with pathological stack regions. Lowering ELF_ET_DYN_BASE solves both by moving programs above the mmap region in all cases, and will now additionally avoid programs falling back to the mmap region by enforcing MAP_FIXED for program loads (i.e. if it would have collided with the stack, now it will fail to load instead of falling back to the mmap region). To allow for a lower ELF_ET_DYN_BASE, loaders (ET_DYN without INTERP) are loaded into the mmap region, leaving space available for either an ET_EXEC binary with a fixed location or PIE being loaded into mmap by the loader. Only PIE programs are loaded offset from ELF_ET_DYN_BASE, which means architectures can now safely lower their values without risk of loaders colliding with their subsequently loaded programs. Thanks go to PaX for inspiration on how to approach this solution. Fixes: d1fd836dcf00 ("mm: split ET_DYN ASLR from mmap ASLR") Signed-off-by: Kees Cook --- arch/x86/include/asm/elf.h | 8 ++------ fs/binfmt_elf.c | 38 +++++++++++++++++++++++++++++++------- 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/arch/x86/include/asm/elf.h b/arch/x86/include/asm/elf.h index e8ab9a46bc68..46549973ea98 100644 --- a/arch/x86/include/asm/elf.h +++ b/arch/x86/include/asm/elf.h @@ -245,12 +245,8 @@ extern int force_personality32; #define CORE_DUMP_USE_REGSET #define ELF_EXEC_PAGESIZE 4096 -/* This is the location that an ET_DYN program is loaded if exec'ed. Typical - use of this is to invoke "./ld.so someprog" to test out a new version of - the loader. We need to make sure that it is out of the way of the program - that it will "exec", and that there is sufficient room for the brk. */ - -#define ELF_ET_DYN_BASE (TASK_SIZE / 3 * 2) +/* This is the base location for PIE (ET_DYN with INTERP) loads. */ +#define ELF_ET_DYN_BASE 0x400000UL /* This yields a mask that user programs can use to figure out what instruction set this CPU supports. This could be done in user space, diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 5075fd5c62c8..a998c7251d1c 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -930,13 +930,37 @@ static int load_elf_binary(struct linux_binprm *bprm) if (loc->elf_ex.e_type == ET_EXEC || load_addr_set) { elf_flags |= MAP_FIXED; } else if (loc->elf_ex.e_type == ET_DYN) { - /* Try and get dynamic programs out of the way of the - * default mmap base, as well as whatever program they - * might try to exec. This is because the brk will - * follow the loader, and is not movable. */ - load_bias = ELF_ET_DYN_BASE - vaddr; - if (current->flags & PF_RANDOMIZE) - load_bias += arch_mmap_rnd(); + /* + * There are effectively two types of ET_DYN + * binaries: programs (i.e. PIE: ET_DYN with INTERP) + * and loaders (ET_DYN without INTERP, since they + * _are_ the ELF interpreter). The loaders must + * be loaded away from programs since the program + * may otherwise collide with the loader (especially + * for ET_EXEC which does not have a randomized + * position). For example to handle invocations of + * "./ld.so someprog" to test out a new version of + * the loader, the subsequent program that the + * loader loads must avoid the loader itself, so + * they cannot share the same load range. Sufficient + * room for the brk must be allocated with the + * loader as well, since brk must be available with + * the loader. + * + * Therefore, programs are loaded offset from + * ELF_ET_DYN_BASE and loaders are loaded into the + * independently randomized mmap region (0 load_bias + * without MAP_FIXED). + */ + if (elf_interpreter) { + load_bias = ELF_ET_DYN_BASE; + if (current->flags & PF_RANDOMIZE) + load_bias += arch_mmap_rnd(); + elf_flags |= MAP_FIXED; + } else + load_bias = 0; + + load_bias -= vaddr; load_bias = ELF_PAGESTART(load_bias); total_size = total_mapping_size(elf_phdata, loc->elf_ex.e_phnum);