From patchwork Fri Dec 21 17:20:39 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kevin 'ldir' Darbyshire-Bryant X-Patchwork-Id: 1017638 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=lists.openwrt.org (client-ip=2607:7c80:54:e::133; helo=bombadil.infradead.org; envelope-from=openwrt-devel-bounces+incoming=patchwork.ozlabs.org@lists.openwrt.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=darbyshire-bryant.me.uk Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.b="ttyPBe7z"; dkim=fail reason="signature verification failed" (1024-bit key; unprotected) header.d=darbyshire-bryant.me.uk header.i=@darbyshire-bryant.me.uk header.b="YSMYUd4Q"; dkim-atps=neutral Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:e::133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 43LwQS5hd9z9sML for ; Sat, 22 Dec 2018 04:21:20 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:Subject:MIME-Version:Message-ID:Date:To :From:Reply-To:Content-ID:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References: List-Owner; bh=5SMeve2l76F31wL3ypILQkJLM68KIEUEFDXYORsvp34=; b=ttyPBe7zl8fire o9/xIsWLKQzMgGTqLXv/FIMsXRy47xrmF4ycv6KV1nJwZ2l8QnaHOqH5p63TBDcrG1hxx2ZSHANda EiIHSyJK9B4goMpA1HW4mdKfDpYMT4fw5G0P3ZrnWrqESTBXLLJQH+r00N4/rVUYLoVXAu8waCq/n BdXKkmGjGIjI/S7vxH6r5SOxJ6eI1+loAf1e0jEYMApSPpz0Kmvpf2n0dh8ED+ao/4ahmne3r08hC XLZba5IUkCzLuwtwRz5STfTuf6ipKhFWnsLfTk+6EGYtwTDOy+UssuewNVfxtNL7/QGYVlySZXgmu PUDpXso7DqJqUdMXiEcA==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1gaOU2-0003Oa-QT; Fri, 21 Dec 2018 17:21:10 +0000 Received: from mail-he1eur01on0618.outbound.protection.outlook.com ([2a01:111:f400:fe1e::618] helo=EUR01-HE1-obe.outbound.protection.outlook.com) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1gaOTo-0003EA-JF for openwrt-devel@lists.openwrt.org; Fri, 21 Dec 2018 17:20:58 +0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=darbyshire-bryant.me.uk; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=iue+iryXC87kX0UaKgrgTxkoo2S6Fqx4+mIUSq0RiJQ=; b=YSMYUd4QQlf4QfZ+JxTj+C1XAlGfLDW8ecllZJnRLGAS3zx/3yGZ/R91gX52FErulXRLMZJ5ub5kZC58c4qF6mKet6ejkV6Ls3u/Pd12oaar7OMJuP8BAyDYe1uQq37nAKkKMWMmYVUuyG8r3Q2S/F/aPnq97V7muBpqvcIWROg= Received: from VI1PR0302MB2750.eurprd03.prod.outlook.com (10.171.105.143) by VI1PR0302MB2751.eurprd03.prod.outlook.com (10.171.105.144) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.1446.19; Fri, 21 Dec 2018 17:20:39 +0000 Received: from VI1PR0302MB2750.eurprd03.prod.outlook.com ([fe80::d77:d217:1660:c5d4]) by VI1PR0302MB2750.eurprd03.prod.outlook.com ([fe80::d77:d217:1660:c5d4%2]) with mapi id 15.20.1446.022; Fri, 21 Dec 2018 17:20:39 +0000 From: Kevin 'ldir' Darbyshire-Bryant To: "openwrt-devel@lists.openwrt.org" Thread-Topic: [PATCH] kernel: MIPS: math-emu Write-protect delay slot emulation pages Thread-Index: AQHUmVF+8EfvRYxeJU2fG2Umgo4zoQ== Date: Fri, 21 Dec 2018 17:20:39 +0000 Message-ID: <20181221172030.78770-1-ldir@darbyshire-bryant.me.uk> Accept-Language: en-GB, en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: x-clientproxiedby: LO2P265CA0374.GBRP265.PROD.OUTLOOK.COM (2603:10a6:600:a3::26) To VI1PR0302MB2750.eurprd03.prod.outlook.com (2603:10a6:800:e2::15) authentication-results: spf=none (sender IP is ) smtp.mailfrom=ldir@darbyshire-bryant.me.uk; x-ms-exchange-messagesentrepresentingtype: 1 x-originating-ip: [2a02:c7f:1240:ee00::dc83] x-ms-publictraffictype: Email x-microsoft-exchange-diagnostics: 1; VI1PR0302MB2751; 6:QKqNYhzf2ZNrOenpibvx4Ju/brZYJp4eFPPAlm1SEyNdXllfCa8atdoMQaiE27w3Eg16imKjOgcDzmtjk0zt1zNQs8ieeT36/9a4hALCzE0DB3VKNiodRyM1/ZdDKlh33voQVkS5vH9WgcviB+/WlgbcicswrOdTWoGhyKfhdmb7H11UOCgUrhwYvNAK2FrfTypOahlsgSEDQtDzKotMiLQ/UMYPLDdaj/oNaeIkb1CV6x95ZuxM7sO0UjrUvLKQNfvW4vSeOpMeXOt/NFyylzEIOEW1gMv2+dFiTgDilkV6Hq5aZPZyG6MNGVX7zjem9JVaZ1ftXGHnCqe0e7uhVuO78beHwzogvibRDcz7Nze0daH0MvPXaUiTXgygwI0BpnfdUbLK6ORCjcLmZ3F27uzTPe9NoYO26CVtYAf0/P22JtZrENcjTo9MEkYGIlCa/hjBNDPriC9o4BnM9D9IUw==; 5:U0NJUzdfHSoRCfPKszOEBVl5j4EFm2O7yl+5EY1Lkn0h5Tu1CGeA6GovjgCgGWF5eoojIoBkTjuJYqAkT7t7dQQ+jprWjDrNXXI15CX2R9tbwB4XoQwCmV+zdN0fJTkC9pHyHR11+HioxxOMJxmHNVyvyqzmmANYrQSBwQQByPw=; 7:b4fovc0vY9l7YQa7TrKZzDTd1lvZ+grMVQd+eMBK+BxIeDw7RypN1yKRRsrUk2N4eT7l/mZxpDDyEYZ1YopCrp1pPgW7Zw81fpSeRDMTEt9gTw80+fhSCoGUN+sBsKjLT/G5ippaa0amYc1tqC/5+g== x-ms-office365-filtering-correlation-id: 6c688217-c6c8-4a9c-97a5-08d66768a0ab x-microsoft-antispam: BCL:0; PCL:0; RULEID:(2390118)(7020095)(4652040)(7021145)(8989299)(4534185)(7022145)(4603075)(4627221)(201702281549075)(8990200)(7048125)(7024125)(7027125)(7023125)(5600074)(711020)(2017052603328)(7153060)(7193020); SRVR:VI1PR0302MB2751; x-ms-traffictypediagnostic: VI1PR0302MB2751: x-microsoft-antispam-prvs: x-ms-exchange-senderadcheck: 1 x-exchange-antispam-report-cfa-test: BCL:0; PCL:0; RULEID:(3230021)(999002)(5005026)(6040522)(2401047)(8121501046)(10201501046)(3231475)(944501520)(52105112)(3002001)(93006095)(93001095)(149066)(150057)(6041310)(2016111802025)(20161123564045)(20161123562045)(20161123558120)(20161123560045)(6043046)(201708071742011)(7699051)(76991095); SRVR:VI1PR0302MB2751; BCL:0; PCL:0; RULEID:; SRVR:VI1PR0302MB2751; x-forefront-prvs: 0893636978 x-forefront-antispam-report: SFV:NSPM; SFS:(10009020)(396003)(39830400003)(366004)(346002)(136003)(376002)(199004)(189003)(53946003)(105586002)(14444005)(256004)(575784001)(14454004)(86362001)(5640700003)(81166006)(81156014)(8936002)(6916009)(25786009)(6436002)(54906003)(508600001)(1076003)(966005)(316002)(4744004)(36756003)(71190400001)(71200400001)(2501003)(6512007)(6306002)(97736004)(68736007)(6116002)(6486002)(305945005)(486006)(7736002)(476003)(2616005)(74482002)(46003)(2906002)(8676002)(386003)(2351001)(6506007)(52116002)(4326008)(186003)(5660300001)(53936002)(102836004)(106356001)(99286004); DIR:OUT; SFP:1101; SCL:1; SRVR:VI1PR0302MB2751; H:VI1PR0302MB2750.eurprd03.prod.outlook.com; FPR:; SPF:None; LANG:en; PTR:InfoNoRecords; A:1; MX:1; received-spf: None (protection.outlook.com: darbyshire-bryant.me.uk does not designate permitted sender hosts) x-microsoft-antispam-message-info: MhocrGV4dqnaCpmN9W0RlcDMaOdhkOawCpQYl8pktsaQZnFOiHfqN/cqjmIK7yancQovn8lPZLG2Hx/HyBHEE7w/QHEd1U0uLBjYd+7yJS9HdULI7QuoNV2Z7yJ1nu1xd55mIE5Gdd2DsX4nbBrf0XIjQCS+yjcj/yoBL++NzhuhazdGO29Q96Upe9rTgBdvJODCxK3JVNq0zICzNTk3fBnX1glDwOsxOfwribfRrKRZaiVI1J6cYIgA4PtJgTlFekb1FFSrNkyx46gXSti8/o3mkrrvSlf4F8XCUFGPtjSOEykmzPxCNMVyFhIUOOON spamdiagnosticoutput: 1:99 spamdiagnosticmetadata: NSPM MIME-Version: 1.0 X-OriginatorOrg: darbyshire-bryant.me.uk X-MS-Exchange-CrossTenant-Network-Message-Id: 6c688217-c6c8-4a9c-97a5-08d66768a0ab X-MS-Exchange-CrossTenant-originalarrivaltime: 21 Dec 2018 17:20:39.4417 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 9151708b-c553-406f-8e56-694f435154a4 X-MS-Exchange-Transport-CrossTenantHeadersStamped: VI1PR0302MB2751 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20181221_092056_997728_6ACFCAE1 X-CRM114-Status: GOOD ( 15.48 ) X-Spam-Score: -0.2 (/) X-Spam-Report: SpamAssassin version 3.4.2 on bombadil.infradead.org summary: Content analysis details: (-0.2 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.0 RCVD_IN_DNSWL_NONE RBL: Sender listed at http://www.dnswl.org/, no trust [2a01:111:f400:fe1e:0:0:0:618 listed in] [list.dnswl.org] -0.0 SPF_HELO_PASS SPF: HELO matches SPF record -0.0 SPF_PASS SPF: sender matches SPF record -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID_EF Message has a valid DKIM or DK signature from envelope-from domain Subject: [OpenWrt-Devel] [PATCH] kernel: MIPS: math-emu Write-protect delay slot emulation pages X-BeenThere: openwrt-devel@lists.openwrt.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Paul Burton , Kevin 'ldir' Darbyshire-Bryant Sender: "openwrt-devel" Errors-To: openwrt-devel-bounces+incoming=patchwork.ozlabs.org@lists.openwrt.org Backport https://git.kernel.org/pub/scm/linux/kernel/git/mips/linux.git/commit/?id=adcc81f148d733b7e8e641300c5590a2cdc13bf3 "Mapping the delay slot emulation page as both writeable & executable presents a security risk, in that if an exploit can write to & jump into the page then it can be used as an easy way to execute arbitrary code. Prevent this by mapping the page read-only for userland, and using access_process_vm() with the FOLL_FORCE flag to write to it from mips_dsemul(). This will likely be less efficient due to copy_to_user_page() performing cache maintenance on a whole page, rather than a single line as in the previous use of flush_cache_sigtramp(). However this delay slot emulation code ought not to be running in any performance critical paths anyway so this isn't really a problem, and we can probably do better in copy_to_user_page() anyway in future. A major advantage of this approach is that the fix is small & simple to backport to stable kernels. Reported-by: Andy Lutomirski Signed-off-by: Paul Burton Fixes: 432c6bacbd0c ("MIPS: Use per-mm page to execute branch delay slot instructions")" Without patch: cat /proc/self/maps 00400000-0047a000 r-xp 00000000 1f:03 1823 /bin/busybox 00489000-0048a000 r-xp 00079000 1f:03 1823 /bin/busybox 0048a000-0048b000 rwxp 0007a000 1f:03 1823 /bin/busybox 77ec8000-77eed000 r-xp 00000000 1f:03 2296 /lib/libgcc_s.so.1 77eed000-77eee000 rwxp 00015000 1f:03 2296 /lib/libgcc_s.so.1 77eee000-77f81000 r-xp 00000000 1f:03 2470 /lib/libc.so 77f90000-77f92000 rwxp 00092000 1f:03 2470 /lib/libc.so 77f92000-77f94000 rwxp 00000000 00:00 0 7f946000-7f967000 rw-p 00000000 00:00 0 [stack] 7fefb000-7fefc000 rwxp 00000000 00:00 0 7ffac000-7ffad000 r--p 00000000 00:00 0 [vvar] 7ffad000-7ffae000 r-xp 00000000 00:00 0 [vdso] Patch applied: cat /proc/self/maps 00400000-0047a000 r-xp 00000000 1f:03 1825 /bin/busybox 00489000-0048a000 r-xp 00079000 1f:03 1825 /bin/busybox 0048a000-0048b000 rwxp 0007a000 1f:03 1825 /bin/busybox 77ed0000-77ef5000 r-xp 00000000 1f:03 2298 /lib/libgcc_s.so.1 77ef5000-77ef6000 rwxp 00015000 1f:03 2298 /lib/libgcc_s.so.1 77ef6000-77f89000 r-xp 00000000 1f:03 2474 /lib/libc.so 77f98000-77f9a000 rwxp 00092000 1f:03 2474 /lib/libc.so 77f9a000-77f9c000 rwxp 00000000 00:00 0 7fbed000-7fc0e000 rw-p 00000000 00:00 0 [stack] 7fefb000-7fefc000 r-xp 00000000 00:00 0 7fff6000-7fff7000 r--p 00000000 00:00 0 [vvar] 7fff7000-7fff8000 r-xp 00000000 00:00 0 [vdso] Note lack of write permission to 7fefb000-7fefc000 This has received minimal testing on ath79 4.14 Archer C7 v2 only Signed-off-by: Kevin Darbyshire-Bryant --- ...e-protect-delay-slot-emulation-pages.patch | 119 ++++++++++++++++++ ...e-protect-delay-slot-emulation-pages.patch | 119 ++++++++++++++++++ ...e-protect-delay-slot-emulation-pages.patch | 119 ++++++++++++++++++ 3 files changed, 357 insertions(+) create mode 100644 target/linux/generic/backport-4.14/096-mips-math-emu-Write-protect-delay-slot-emulation-pages.patch create mode 100644 target/linux/generic/backport-4.19/096-mips-math-emu-Write-protect-delay-slot-emulation-pages.patch create mode 100644 target/linux/generic/backport-4.9/096-mips-math-emu-Write-protect-delay-slot-emulation-pages.patch diff --git a/target/linux/generic/backport-4.14/096-mips-math-emu-Write-protect-delay-slot-emulation-pages.patch b/target/linux/generic/backport-4.14/096-mips-math-emu-Write-protect-delay-slot-emulation-pages.patch new file mode 100644 index 0000000000..f428285a64 --- /dev/null +++ b/target/linux/generic/backport-4.14/096-mips-math-emu-Write-protect-delay-slot-emulation-pages.patch @@ -0,0 +1,119 @@ +From adcc81f148d733b7e8e641300c5590a2cdc13bf3 Mon Sep 17 00:00:00 2001 +From: Paul Burton +Date: Thu, 20 Dec 2018 17:45:43 +0000 +Subject: MIPS: math-emu: Write-protect delay slot emulation pages + +Mapping the delay slot emulation page as both writeable & executable +presents a security risk, in that if an exploit can write to & jump into +the page then it can be used as an easy way to execute arbitrary code. + +Prevent this by mapping the page read-only for userland, and using +access_process_vm() with the FOLL_FORCE flag to write to it from +mips_dsemul(). + +This will likely be less efficient due to copy_to_user_page() performing +cache maintenance on a whole page, rather than a single line as in the +previous use of flush_cache_sigtramp(). However this delay slot +emulation code ought not to be running in any performance critical paths +anyway so this isn't really a problem, and we can probably do better in +copy_to_user_page() anyway in future. + +A major advantage of this approach is that the fix is small & simple to +backport to stable kernels. + +Reported-by: Andy Lutomirski +Signed-off-by: Paul Burton +Fixes: 432c6bacbd0c ("MIPS: Use per-mm page to execute branch delay slot instructions") +Cc: stable@vger.kernel.org # v4.8+ +Cc: linux-mips@vger.kernel.org +Cc: linux-kernel@vger.kernel.org +Cc: Rich Felker +Cc: David Daney +--- + arch/mips/kernel/vdso.c | 4 ++-- + arch/mips/math-emu/dsemul.c | 38 ++++++++++++++++++++------------------ + 2 files changed, 22 insertions(+), 20 deletions(-) + +--- a/arch/mips/kernel/vdso.c ++++ b/arch/mips/kernel/vdso.c +@@ -126,8 +126,8 @@ int arch_setup_additional_pages(struct l + + /* Map delay slot emulation page */ + base = mmap_region(NULL, STACK_TOP, PAGE_SIZE, +- VM_READ|VM_WRITE|VM_EXEC| +- VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC, ++ VM_READ | VM_EXEC | ++ VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC, + 0, NULL); + if (IS_ERR_VALUE(base)) { + ret = base; +--- a/arch/mips/math-emu/dsemul.c ++++ b/arch/mips/math-emu/dsemul.c +@@ -214,8 +214,9 @@ int mips_dsemul(struct pt_regs *regs, mi + { + int isa16 = get_isa16_mode(regs->cp0_epc); + mips_instruction break_math; +- struct emuframe __user *fr; +- int err, fr_idx; ++ unsigned long fr_uaddr; ++ struct emuframe fr; ++ int fr_idx, ret; + + /* NOP is easy */ + if (ir == 0) +@@ -250,27 +251,31 @@ int mips_dsemul(struct pt_regs *regs, mi + fr_idx = alloc_emuframe(); + if (fr_idx == BD_EMUFRAME_NONE) + return SIGBUS; +- fr = &dsemul_page()[fr_idx]; + + /* Retrieve the appropriately encoded break instruction */ + break_math = BREAK_MATH(isa16); + + /* Write the instructions to the frame */ + if (isa16) { +- err = __put_user(ir >> 16, +- (u16 __user *)(&fr->emul)); +- err |= __put_user(ir & 0xffff, +- (u16 __user *)((long)(&fr->emul) + 2)); +- err |= __put_user(break_math >> 16, +- (u16 __user *)(&fr->badinst)); +- err |= __put_user(break_math & 0xffff, +- (u16 __user *)((long)(&fr->badinst) + 2)); ++ union mips_instruction _emul = { ++ .halfword = { ir >> 16, ir } ++ }; ++ union mips_instruction _badinst = { ++ .halfword = { break_math >> 16, break_math } ++ }; ++ ++ fr.emul = _emul.word; ++ fr.badinst = _badinst.word; + } else { +- err = __put_user(ir, &fr->emul); +- err |= __put_user(break_math, &fr->badinst); ++ fr.emul = ir; ++ fr.badinst = break_math; + } + +- if (unlikely(err)) { ++ /* Write the frame to user memory */ ++ fr_uaddr = (unsigned long)&dsemul_page()[fr_idx]; ++ ret = access_process_vm(current, fr_uaddr, &fr, sizeof(fr), ++ FOLL_FORCE | FOLL_WRITE); ++ if (unlikely(ret != sizeof(fr))) { + MIPS_FPU_EMU_INC_STATS(errors); + free_emuframe(fr_idx, current->mm); + return SIGBUS; +@@ -282,10 +287,7 @@ int mips_dsemul(struct pt_regs *regs, mi + atomic_set(¤t->thread.bd_emu_frame, fr_idx); + + /* Change user register context to execute the frame */ +- regs->cp0_epc = (unsigned long)&fr->emul | isa16; +- +- /* Ensure the icache observes our newly written frame */ +- flush_cache_sigtramp((unsigned long)&fr->emul); ++ regs->cp0_epc = fr_uaddr | isa16; + + return 0; + } diff --git a/target/linux/generic/backport-4.19/096-mips-math-emu-Write-protect-delay-slot-emulation-pages.patch b/target/linux/generic/backport-4.19/096-mips-math-emu-Write-protect-delay-slot-emulation-pages.patch new file mode 100644 index 0000000000..f428285a64 --- /dev/null +++ b/target/linux/generic/backport-4.19/096-mips-math-emu-Write-protect-delay-slot-emulation-pages.patch @@ -0,0 +1,119 @@ +From adcc81f148d733b7e8e641300c5590a2cdc13bf3 Mon Sep 17 00:00:00 2001 +From: Paul Burton +Date: Thu, 20 Dec 2018 17:45:43 +0000 +Subject: MIPS: math-emu: Write-protect delay slot emulation pages + +Mapping the delay slot emulation page as both writeable & executable +presents a security risk, in that if an exploit can write to & jump into +the page then it can be used as an easy way to execute arbitrary code. + +Prevent this by mapping the page read-only for userland, and using +access_process_vm() with the FOLL_FORCE flag to write to it from +mips_dsemul(). + +This will likely be less efficient due to copy_to_user_page() performing +cache maintenance on a whole page, rather than a single line as in the +previous use of flush_cache_sigtramp(). However this delay slot +emulation code ought not to be running in any performance critical paths +anyway so this isn't really a problem, and we can probably do better in +copy_to_user_page() anyway in future. + +A major advantage of this approach is that the fix is small & simple to +backport to stable kernels. + +Reported-by: Andy Lutomirski +Signed-off-by: Paul Burton +Fixes: 432c6bacbd0c ("MIPS: Use per-mm page to execute branch delay slot instructions") +Cc: stable@vger.kernel.org # v4.8+ +Cc: linux-mips@vger.kernel.org +Cc: linux-kernel@vger.kernel.org +Cc: Rich Felker +Cc: David Daney +--- + arch/mips/kernel/vdso.c | 4 ++-- + arch/mips/math-emu/dsemul.c | 38 ++++++++++++++++++++------------------ + 2 files changed, 22 insertions(+), 20 deletions(-) + +--- a/arch/mips/kernel/vdso.c ++++ b/arch/mips/kernel/vdso.c +@@ -126,8 +126,8 @@ int arch_setup_additional_pages(struct l + + /* Map delay slot emulation page */ + base = mmap_region(NULL, STACK_TOP, PAGE_SIZE, +- VM_READ|VM_WRITE|VM_EXEC| +- VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC, ++ VM_READ | VM_EXEC | ++ VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC, + 0, NULL); + if (IS_ERR_VALUE(base)) { + ret = base; +--- a/arch/mips/math-emu/dsemul.c ++++ b/arch/mips/math-emu/dsemul.c +@@ -214,8 +214,9 @@ int mips_dsemul(struct pt_regs *regs, mi + { + int isa16 = get_isa16_mode(regs->cp0_epc); + mips_instruction break_math; +- struct emuframe __user *fr; +- int err, fr_idx; ++ unsigned long fr_uaddr; ++ struct emuframe fr; ++ int fr_idx, ret; + + /* NOP is easy */ + if (ir == 0) +@@ -250,27 +251,31 @@ int mips_dsemul(struct pt_regs *regs, mi + fr_idx = alloc_emuframe(); + if (fr_idx == BD_EMUFRAME_NONE) + return SIGBUS; +- fr = &dsemul_page()[fr_idx]; + + /* Retrieve the appropriately encoded break instruction */ + break_math = BREAK_MATH(isa16); + + /* Write the instructions to the frame */ + if (isa16) { +- err = __put_user(ir >> 16, +- (u16 __user *)(&fr->emul)); +- err |= __put_user(ir & 0xffff, +- (u16 __user *)((long)(&fr->emul) + 2)); +- err |= __put_user(break_math >> 16, +- (u16 __user *)(&fr->badinst)); +- err |= __put_user(break_math & 0xffff, +- (u16 __user *)((long)(&fr->badinst) + 2)); ++ union mips_instruction _emul = { ++ .halfword = { ir >> 16, ir } ++ }; ++ union mips_instruction _badinst = { ++ .halfword = { break_math >> 16, break_math } ++ }; ++ ++ fr.emul = _emul.word; ++ fr.badinst = _badinst.word; + } else { +- err = __put_user(ir, &fr->emul); +- err |= __put_user(break_math, &fr->badinst); ++ fr.emul = ir; ++ fr.badinst = break_math; + } + +- if (unlikely(err)) { ++ /* Write the frame to user memory */ ++ fr_uaddr = (unsigned long)&dsemul_page()[fr_idx]; ++ ret = access_process_vm(current, fr_uaddr, &fr, sizeof(fr), ++ FOLL_FORCE | FOLL_WRITE); ++ if (unlikely(ret != sizeof(fr))) { + MIPS_FPU_EMU_INC_STATS(errors); + free_emuframe(fr_idx, current->mm); + return SIGBUS; +@@ -282,10 +287,7 @@ int mips_dsemul(struct pt_regs *regs, mi + atomic_set(¤t->thread.bd_emu_frame, fr_idx); + + /* Change user register context to execute the frame */ +- regs->cp0_epc = (unsigned long)&fr->emul | isa16; +- +- /* Ensure the icache observes our newly written frame */ +- flush_cache_sigtramp((unsigned long)&fr->emul); ++ regs->cp0_epc = fr_uaddr | isa16; + + return 0; + } diff --git a/target/linux/generic/backport-4.9/096-mips-math-emu-Write-protect-delay-slot-emulation-pages.patch b/target/linux/generic/backport-4.9/096-mips-math-emu-Write-protect-delay-slot-emulation-pages.patch new file mode 100644 index 0000000000..69cc493bba --- /dev/null +++ b/target/linux/generic/backport-4.9/096-mips-math-emu-Write-protect-delay-slot-emulation-pages.patch @@ -0,0 +1,119 @@ +From adcc81f148d733b7e8e641300c5590a2cdc13bf3 Mon Sep 17 00:00:00 2001 +From: Paul Burton +Date: Thu, 20 Dec 2018 17:45:43 +0000 +Subject: MIPS: math-emu: Write-protect delay slot emulation pages + +Mapping the delay slot emulation page as both writeable & executable +presents a security risk, in that if an exploit can write to & jump into +the page then it can be used as an easy way to execute arbitrary code. + +Prevent this by mapping the page read-only for userland, and using +access_process_vm() with the FOLL_FORCE flag to write to it from +mips_dsemul(). + +This will likely be less efficient due to copy_to_user_page() performing +cache maintenance on a whole page, rather than a single line as in the +previous use of flush_cache_sigtramp(). However this delay slot +emulation code ought not to be running in any performance critical paths +anyway so this isn't really a problem, and we can probably do better in +copy_to_user_page() anyway in future. + +A major advantage of this approach is that the fix is small & simple to +backport to stable kernels. + +Reported-by: Andy Lutomirski +Signed-off-by: Paul Burton +Fixes: 432c6bacbd0c ("MIPS: Use per-mm page to execute branch delay slot instructions") +Cc: stable@vger.kernel.org # v4.8+ +Cc: linux-mips@vger.kernel.org +Cc: linux-kernel@vger.kernel.org +Cc: Rich Felker +Cc: David Daney +--- + arch/mips/kernel/vdso.c | 4 ++-- + arch/mips/math-emu/dsemul.c | 38 ++++++++++++++++++++------------------ + 2 files changed, 22 insertions(+), 20 deletions(-) + +--- a/arch/mips/kernel/vdso.c ++++ b/arch/mips/kernel/vdso.c +@@ -111,8 +111,8 @@ int arch_setup_additional_pages(struct l + + /* Map delay slot emulation page */ + base = mmap_region(NULL, STACK_TOP, PAGE_SIZE, +- VM_READ|VM_WRITE|VM_EXEC| +- VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC, ++ VM_READ | VM_EXEC | ++ VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC, + 0); + if (IS_ERR_VALUE(base)) { + ret = base; +--- a/arch/mips/math-emu/dsemul.c ++++ b/arch/mips/math-emu/dsemul.c +@@ -211,8 +211,9 @@ int mips_dsemul(struct pt_regs *regs, mi + { + int isa16 = get_isa16_mode(regs->cp0_epc); + mips_instruction break_math; +- struct emuframe __user *fr; +- int err, fr_idx; ++ unsigned long fr_uaddr; ++ struct emuframe fr; ++ int fr_idx, ret; + + /* NOP is easy */ + if (ir == 0) +@@ -247,27 +248,31 @@ int mips_dsemul(struct pt_regs *regs, mi + fr_idx = alloc_emuframe(); + if (fr_idx == BD_EMUFRAME_NONE) + return SIGBUS; +- fr = &dsemul_page()[fr_idx]; + + /* Retrieve the appropriately encoded break instruction */ + break_math = BREAK_MATH(isa16); + + /* Write the instructions to the frame */ + if (isa16) { +- err = __put_user(ir >> 16, +- (u16 __user *)(&fr->emul)); +- err |= __put_user(ir & 0xffff, +- (u16 __user *)((long)(&fr->emul) + 2)); +- err |= __put_user(break_math >> 16, +- (u16 __user *)(&fr->badinst)); +- err |= __put_user(break_math & 0xffff, +- (u16 __user *)((long)(&fr->badinst) + 2)); ++ union mips_instruction _emul = { ++ .halfword = { ir >> 16, ir } ++ }; ++ union mips_instruction _badinst = { ++ .halfword = { break_math >> 16, break_math } ++ }; ++ ++ fr.emul = _emul.word; ++ fr.badinst = _badinst.word; + } else { +- err = __put_user(ir, &fr->emul); +- err |= __put_user(break_math, &fr->badinst); ++ fr.emul = ir; ++ fr.badinst = break_math; + } + +- if (unlikely(err)) { ++ /* Write the frame to user memory */ ++ fr_uaddr = (unsigned long)&dsemul_page()[fr_idx]; ++ ret = access_process_vm(current, fr_uaddr, &fr, sizeof(fr), ++ FOLL_FORCE | FOLL_WRITE); ++ if (unlikely(ret != sizeof(fr))) { + MIPS_FPU_EMU_INC_STATS(errors); + free_emuframe(fr_idx, current->mm); + return SIGBUS; +@@ -279,10 +284,7 @@ int mips_dsemul(struct pt_regs *regs, mi + atomic_set(¤t->thread.bd_emu_frame, fr_idx); + + /* Change user register context to execute the frame */ +- regs->cp0_epc = (unsigned long)&fr->emul | isa16; +- +- /* Ensure the icache observes our newly written frame */ +- flush_cache_sigtramp((unsigned long)&fr->emul); ++ regs->cp0_epc = fr_uaddr | isa16; + + return 0; + }