From patchwork Fri May 19 01:19:09 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jeff Xu X-Patchwork-Id: 96155 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:b0ea:0:b0:3b6:4342:cba0 with SMTP id b10csp910843vqo; Thu, 18 May 2023 18:21:33 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ6KYyXgq0WZEXHyJKUa4IxQ7ufZ8i0GKp9ezr/i2tXYeTSSDswWIs5W7XEcIYYzeHadl4mP X-Received: by 2002:a05:6a00:23d5:b0:643:59cd:6caa with SMTP id g21-20020a056a0023d500b0064359cd6caamr1090062pfc.7.1684459292681; Thu, 18 May 2023 18:21:32 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1684459292; cv=none; d=google.com; s=arc-20160816; b=kfRKbjiMB40My/uwVO6ScX7rIheGheKnyHlB1maEo0pOynr7jF+x1olWYHzs2o07lg z7U5uhTTqYkA0XrB4lGrRoMBNnSV+CmvKaGpLwNBeeSnUPetls5ZqU1bBOgHbFc+e0IE CDNtDbBPiNqhfMuwiRas4onLJp+SHUTaHyQErIFX5kIv2mf7b5zTQWIM1cr7rBEcCo41 GJQZuj5FzSZdKDyzCNVwgyLlD0f8Q1bb8NVejx5b2MQ3sAmsYh9Wyxm408FItl4v+EFt P1pZ4uFK8v/Ycw9VQJJqQxJcQuiOp6ryPldev+HTB9vfmU/sBKSQe/YL4G4wBGCIuHcn 07jQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=amUmjeQdjogZim3KCvDHtpibL1Mlx7r/unbXJ7VLzz4=; b=omIQDUVm1wQYPHsOOIDbW+TOfAf3FOjLqL1caoynIQNQvteMypsRtgSnYTgLPVXtC9 FTFKjewIwR4YDfYp4ErlA681Fu9ngaxUo6qDw0KhcXbJok87mX/FQ/ic2UxigrZKrNTs vHWkzLdHK5OojWVewszwkeiAoMcxw2Ml2J1UtCGRGPGBDq0c4xeXjzEGYodigeUzAw7I gMjbqoKRtPM+29RSBekxEDoH2a4mEq0Qt/ksH35R+enxwKmQEEeJYvZZkd+161+h14Z9 gk3MLSeujuQJh0rkga9b9o//TwbM+ZiLV3ciw4UnSWySB7QO4KhvcKp4fYVuQGnSWApt VVrg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@chromium.org header.s=google header.b=GibbkWqf; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=chromium.org Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id r16-20020aa79890000000b0064cf28ac395si2992367pfl.172.2023.05.18.18.21.19; Thu, 18 May 2023 18:21:32 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@chromium.org header.s=google header.b=GibbkWqf; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=chromium.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230510AbjESBTd (ORCPT + 99 others); Thu, 18 May 2023 21:19:33 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53570 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230290AbjESBTb (ORCPT ); Thu, 18 May 2023 21:19:31 -0400 Received: from mail-pf1-x432.google.com (mail-pf1-x432.google.com [IPv6:2607:f8b0:4864:20::432]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 3CE0310C6 for ; Thu, 18 May 2023 18:19:28 -0700 (PDT) Received: by mail-pf1-x432.google.com with SMTP id d2e1a72fcca58-64d247a023aso831739b3a.2 for ; Thu, 18 May 2023 18:19:28 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; t=1684459168; x=1687051168; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=amUmjeQdjogZim3KCvDHtpibL1Mlx7r/unbXJ7VLzz4=; b=GibbkWqfiwuo0GDKTjKpCXZxRAC34F6flBcYc63YtvRDvtZ56HZtHKuhrJYSdwe3/+ 6kPjFdVzSbPq+cF3lXOpkjZLCMYa7ei/z4zXxpzej0So1g37PcWmqewUZUgxYJCMM+RN jmAWbhNUMedM1L5m+UzN6elDn6LRlSsQzlPeM= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1684459168; x=1687051168; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=amUmjeQdjogZim3KCvDHtpibL1Mlx7r/unbXJ7VLzz4=; b=KuepeyyUkwwuRN241y5dV0iCqgr486MPzhd9obae1z4/M3EfnroD9+iPsv6c3S2XVe Px77I2rk2E4x7zaCQ4N+9OG5GWRRGnmL9FuJzrU9kuYFib1X1mj0AmCE10af5d4eA6FG l/pzmWCEThv6CIX7CWeZbYEcmaP3JKPT3htefHM/O/dWJOxV/KNIkLygFmTv1zN16sPy 5OoE5uvz43iujkUG6rRKEJaHo0IBYTtFvHQXY/oE3L6XV8mtRZbzb7XsCpkT7ROftmOV 8fxJunhkSCMTCDyDeCCfKY8x6qvhUij7sbdXYWysjK2I0o2OnNAT3xg9LODckf8ePig8 U8Kw== X-Gm-Message-State: AC+VfDzrmtBWeKMlADPKNq9UeZQ+DHraB+xR8f250iAaJospTZvRwZVC bGqgSfX4I699+aHX9kNKd6RlIQ== X-Received: by 2002:a05:6a00:134c:b0:648:64fe:b14b with SMTP id k12-20020a056a00134c00b0064864feb14bmr881854pfu.32.1684459167669; Thu, 18 May 2023 18:19:27 -0700 (PDT) Received: from localhost (183.43.230.35.bc.googleusercontent.com. [35.230.43.183]) by smtp.gmail.com with UTF8SMTPSA id b6-20020a630c06000000b00502ecb91940sm1824283pgl.55.2023.05.18.18.19.26 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Thu, 18 May 2023 18:19:27 -0700 (PDT) From: jeffxu@chromium.org To: dave.hansen@intel.com, luto@kernel.org, jorgelo@chromium.org, keescook@chromium.org, groeck@chromium.org, jannh@google.com, sroettger@google.com Cc: akpm@linux-foundation.org, jeffxu@google.com, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-mm@kvack.org, linux-hardening@vger.kernel.org Subject: [PATCH v1 1/6] PKEY: Introduce PKEY_ENFORCE_API flag Date: Fri, 19 May 2023 01:19:09 +0000 Message-ID: <20230519011915.846407-2-jeffxu@chromium.org> X-Mailer: git-send-email 2.40.1.698.g37aff9b760-goog In-Reply-To: <20230519011915.846407-1-jeffxu@chromium.org> References: <20230519011915.846407-1-jeffxu@chromium.org> MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_NONE, SPF_HELO_NONE,SPF_PASS,T_SCC_BODY_TEXT_LINE,URIBL_BLOCKED autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1766283586940422678?= X-GMAIL-MSGID: =?utf-8?q?1766283586940422678?= From: Jeff Xu This patch introduces a new flag, PKEY_ENFORCE_API, to the pkey_alloc() function. When a PKEY is created with this flag, it is enforced that any thread that wants to make changes to the memory mapping (such as mprotect/munmap) of the memory must have write access to the PKEY. This is to prevent unauthorized access to protected memory. PKEYs created without this flag will continue to work as they do now, for backwards compatibility. Signed-off-by: Jeff Xu --- arch/powerpc/include/asm/pkeys.h | 11 ++++++++- arch/x86/include/asm/mmu.h | 7 ++++++ arch/x86/include/asm/pkeys.h | 42 ++++++++++++++++++++++++++++++-- arch/x86/mm/pkeys.c | 2 +- include/linux/pkeys.h | 9 ++++++- include/uapi/linux/mman.h | 5 ++++ mm/mprotect.c | 6 ++--- 7 files changed, 74 insertions(+), 8 deletions(-) diff --git a/arch/powerpc/include/asm/pkeys.h b/arch/powerpc/include/asm/pkeys.h index 59a2c7dbc78f..943333ac0fee 100644 --- a/arch/powerpc/include/asm/pkeys.h +++ b/arch/powerpc/include/asm/pkeys.h @@ -82,7 +82,7 @@ static inline bool mm_pkey_is_allocated(struct mm_struct *mm, int pkey) * Relies on the mmap_lock to protect against concurrency in mm_pkey_alloc() and * mm_pkey_free(). */ -static inline int mm_pkey_alloc(struct mm_struct *mm) +static inline int mm_pkey_alloc(struct mm_struct *mm, unsigned long flags) { /* * Note: this is the one and only place we make sure that the pkey is @@ -168,5 +168,14 @@ static inline bool arch_pkeys_enabled(void) return mmu_has_feature(MMU_FTR_PKEY); } +static inline bool arch_check_pkey_alloc_flags(unsigned long flags) +{ + /* No flags supported yet. */ + if (flags) + return false; + + return true; +} + extern void pkey_mm_init(struct mm_struct *mm); #endif /*_ASM_POWERPC_KEYS_H */ diff --git a/arch/x86/include/asm/mmu.h b/arch/x86/include/asm/mmu.h index 0da5c227f490..d97594b44d9a 100644 --- a/arch/x86/include/asm/mmu.h +++ b/arch/x86/include/asm/mmu.h @@ -66,6 +66,13 @@ typedef struct { */ u16 pkey_allocation_map; s16 execute_only_pkey; + /* + * One bit per protection key. + * When set, thread must have write permission on corresponding + * PKRU in order to call memory mapping API, such as mprotect, + * munmap, etc. + */ + u16 pkey_enforce_api_map; #endif } mm_context_t; diff --git a/arch/x86/include/asm/pkeys.h b/arch/x86/include/asm/pkeys.h index 2e6c04d8a45b..ecadf04a8251 100644 --- a/arch/x86/include/asm/pkeys.h +++ b/arch/x86/include/asm/pkeys.h @@ -51,6 +51,17 @@ static inline int arch_override_mprotect_pkey(struct vm_area_struct *vma, mm_pkey_allocation_map(mm) &= ~(1U << pkey); \ } while (0) +#define mm_pkey_enforce_api_map(mm) (mm->context.pkey_enforce_api_map) +#define mm_set_pkey_enforce_api(mm, pkey) \ + { \ + mm_pkey_enforce_api_map(mm) |= (1U << pkey); \ + } + +#define mm_clear_pkey_enforce_api(mm, pkey) \ + { \ + mm_pkey_enforce_api_map(mm) &= ~(1U << pkey); \ + } + static inline bool mm_pkey_is_allocated(struct mm_struct *mm, int pkey) { @@ -74,11 +85,25 @@ bool mm_pkey_is_allocated(struct mm_struct *mm, int pkey) return mm_pkey_allocation_map(mm) & (1U << pkey); } +/* + * Return true if the pkey has ENFORCE_API flag during allocation. + */ +static inline bool mm_pkey_enforce_api(struct mm_struct *mm, int pkey) +{ + /* + * Only pkey created by user space has the flag. + * execute_only_pkey check is in mm_pkey_is_allocated(). + */ + if (pkey != ARCH_DEFAULT_PKEY && mm_pkey_is_allocated(mm, pkey)) + return mm_pkey_enforce_api_map(mm) & (1U << pkey); + + return false; +} + /* * Returns a positive, 4-bit key on success, or -1 on failure. */ -static inline -int mm_pkey_alloc(struct mm_struct *mm) +static inline int mm_pkey_alloc(struct mm_struct *mm, unsigned long flags) { /* * Note: this is the one and only place we make sure @@ -101,6 +126,9 @@ int mm_pkey_alloc(struct mm_struct *mm) mm_set_pkey_allocated(mm, ret); + if (flags & PKEY_ENFORCE_API) + mm_set_pkey_enforce_api(mm, ret); + return ret; } @@ -110,6 +138,7 @@ int mm_pkey_free(struct mm_struct *mm, int pkey) if (!mm_pkey_is_allocated(mm, pkey)) return -EINVAL; + mm_clear_pkey_enforce_api(mm, pkey); mm_set_pkey_free(mm, pkey); return 0; @@ -123,4 +152,13 @@ static inline int vma_pkey(struct vm_area_struct *vma) return (vma->vm_flags & vma_pkey_mask) >> VM_PKEY_SHIFT; } +static inline bool arch_check_pkey_alloc_flags(unsigned long flags) +{ + unsigned long valid_flags = PKEY_ENFORCE_API; + + if (flags & ~valid_flags) + return false; + + return true; +} #endif /*_ASM_X86_PKEYS_H */ diff --git a/arch/x86/mm/pkeys.c b/arch/x86/mm/pkeys.c index 7418c367e328..a76981f44acf 100644 --- a/arch/x86/mm/pkeys.c +++ b/arch/x86/mm/pkeys.c @@ -20,7 +20,7 @@ int __execute_only_pkey(struct mm_struct *mm) /* Do we need to assign a pkey for mm's execute-only maps? */ if (execute_only_pkey == -1) { /* Go allocate one to use, which might fail */ - execute_only_pkey = mm_pkey_alloc(mm); + execute_only_pkey = mm_pkey_alloc(mm, 0); if (execute_only_pkey < 0) return -1; need_to_set_mm_pkey = true; diff --git a/include/linux/pkeys.h b/include/linux/pkeys.h index 86be8bf27b41..81a482c3e051 100644 --- a/include/linux/pkeys.h +++ b/include/linux/pkeys.h @@ -3,6 +3,7 @@ #define _LINUX_PKEYS_H #include +#include #define ARCH_DEFAULT_PKEY 0 @@ -25,7 +26,7 @@ static inline bool mm_pkey_is_allocated(struct mm_struct *mm, int pkey) return (pkey == 0); } -static inline int mm_pkey_alloc(struct mm_struct *mm) +static inline int mm_pkey_alloc(struct mm_struct *mm, unsigned long flags) { return -1; } @@ -46,6 +47,12 @@ static inline bool arch_pkeys_enabled(void) return false; } +static inline bool arch_check_pkey_alloc_flags(unsigned long flags) +{ + if (flags) + return false; + return true; +} #endif /* ! CONFIG_ARCH_HAS_PKEYS */ #endif /* _LINUX_PKEYS_H */ diff --git a/include/uapi/linux/mman.h b/include/uapi/linux/mman.h index f55bc680b5b0..8c69b9a7ff5b 100644 --- a/include/uapi/linux/mman.h +++ b/include/uapi/linux/mman.h @@ -41,4 +41,9 @@ #define MAP_HUGE_2GB HUGETLB_FLAG_ENCODE_2GB #define MAP_HUGE_16GB HUGETLB_FLAG_ENCODE_16GB +/* + * Flags for pkey_alloc + */ +#define PKEY_ENFORCE_API (1 << 0) + #endif /* _UAPI_LINUX_MMAN_H */ diff --git a/mm/mprotect.c b/mm/mprotect.c index 92d3d3ca390a..8a68fdca8487 100644 --- a/mm/mprotect.c +++ b/mm/mprotect.c @@ -894,15 +894,15 @@ SYSCALL_DEFINE2(pkey_alloc, unsigned long, flags, unsigned long, init_val) int pkey; int ret; - /* No flags supported yet. */ - if (flags) + if (!arch_check_pkey_alloc_flags(flags)) return -EINVAL; + /* check for unsupported init values */ if (init_val & ~PKEY_ACCESS_MASK) return -EINVAL; mmap_write_lock(current->mm); - pkey = mm_pkey_alloc(current->mm); + pkey = mm_pkey_alloc(current->mm, flags); ret = -ENOSPC; if (pkey == -1) From patchwork Fri May 19 01:19:10 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jeff Xu X-Patchwork-Id: 96156 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:b0ea:0:b0:3b6:4342:cba0 with SMTP id b10csp910886vqo; Thu, 18 May 2023 18:21:38 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ5lllPMSYx5/i6NbV2eGYcr1mTclP8Hza0+neGSibSVm7oSKqmDzCZB6+9gkQ/JJM+DNsWC X-Received: by 2002:a05:6a00:2183:b0:647:1cb7:b714 with SMTP id h3-20020a056a00218300b006471cb7b714mr983040pfi.3.1684459298378; Thu, 18 May 2023 18:21:38 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1684459298; cv=none; d=google.com; s=arc-20160816; b=HTzJbW/fadUP9A0hqAwL47lzFU0SG6UZI5bYdB5yTpQw3/nKON6Yiz8nTvh7XEzFCv YteDkaOsjk0gCIddnX161mf94wp3RVj5VnzSxSYpAr0ljfVYAaiNNQlPTUqzyGLw2KNU Uo32wY52W9301DRcrpc12t1L16EqfqXValF0PjaMBuM4vqi/TSnGPA2KBYWRPPyyp4h8 k86Z/cuPaq4vX5wxoWS4+0AHYLG0VQhAlUpqn+2fulsIdg09bwY8ZzG0gYBYZBYvCka1 HOE2sMhIwdguV9oDPAxUXW+mbIaLRlGQys1lSzi9UcOOaqZs29WoMU5Qk1GQiRbG/qSV x0VA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=TQS98FLuUxt8kfip9Nk8O1hVfeuWAt0sl7+06c/6tso=; b=xaVIxjmJ9YRzG+yBrHORwWJCQJiiZK2P0XtNJmpDiHR+jgIJ529UVleBxTEYWOjpR3 BC4gCaAT9zpqzh/PUonj2e8nYuxaV8yPC+fnRnYx3ZmL3UzgYt/yJrTpgZbaXW/yDvg7 3wHcDrC9uRfsKbOiQBou5rSIz6a32cNavkW3zYIug1Gt+TDRWcbGW9Pk70btsycSRp71 PfSmWfVu6Lr8ScBgY/kKuELuhXen9/xEgNFuqA3YRdoQIKZEkbgov7RemCF1gXQUHczJ URAtnQ9V58+VSwUxUc2WEVEMN69M+drmzVzWBriKfPUNfpQujPtLwuHrNrk+kg/UFhCH 98sg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@chromium.org header.s=google header.b=OGPd3mn4; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=chromium.org Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id x24-20020a656ab8000000b0053076900facsi2831010pgu.864.2023.05.18.18.21.26; Thu, 18 May 2023 18:21:38 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@chromium.org header.s=google header.b=OGPd3mn4; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=chromium.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230376AbjESBTf (ORCPT + 99 others); Thu, 18 May 2023 21:19:35 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53578 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230312AbjESBTb (ORCPT ); Thu, 18 May 2023 21:19:31 -0400 Received: from mail-pj1-x1032.google.com (mail-pj1-x1032.google.com [IPv6:2607:f8b0:4864:20::1032]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7DCC910D2 for ; Thu, 18 May 2023 18:19:29 -0700 (PDT) Received: by mail-pj1-x1032.google.com with SMTP id 98e67ed59e1d1-24e3b69bc99so2141066a91.2 for ; Thu, 18 May 2023 18:19:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; t=1684459169; x=1687051169; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=TQS98FLuUxt8kfip9Nk8O1hVfeuWAt0sl7+06c/6tso=; b=OGPd3mn4AXIrc1RRZIu250DTtUsK1KYvi8EfuJPLZrZrm+XTM1dOslqaOYntJ4QlnF P1XUdOXQFW2q48KVb2YvQI1UOuJ6zvUuQNOwxGx4Ltcqz+zGeskAKaAjKOirPNTB39HS jBq/1Yq0YsewgO7yu5hZy+p7qvRI04QhhT2rY= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1684459169; x=1687051169; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=TQS98FLuUxt8kfip9Nk8O1hVfeuWAt0sl7+06c/6tso=; b=jm0WZkGR4kCIDVqtYYcRvjZNzFymvygfrZkA1gZJB+f4L/31iVTggM73ISSAlC5WXe UyAI2Vf/NA9GCS6242ewS+2O+RF1OovwD8eOrtGViy7Oj+TLgJKeiuAWcZUx36t8YmmM p2Q/WyezVmGHyGHi+2zy8Ra7NKYMz/Ol3V70E189+54CpZ/aWD3hQzo/d2ebo+h34SYo CGmDPmSJBkJO0ectkqvSQsoOmTVOIvg0Oh18PhYW9wV86hIsJnrgCtnFmoWr1lScjfFq S907P3vSYGlJ21+hIZSkqWpsgjy2xRRWpfSGNtQBnu6nL3c43HFQjUj3rH+5YciMzzej Q3yA== X-Gm-Message-State: AC+VfDzc8lGExZj3dX0PC8ji41GVSOzyICHUGVean5glYzlrfnzSonby 928LhotuuHDb8JjHrBrhsV817A== X-Received: by 2002:a17:90b:3ecc:b0:246:85ec:d816 with SMTP id rm12-20020a17090b3ecc00b0024685ecd816mr538354pjb.3.1684459168766; Thu, 18 May 2023 18:19:28 -0700 (PDT) Received: from localhost (183.43.230.35.bc.googleusercontent.com. [35.230.43.183]) by smtp.gmail.com with UTF8SMTPSA id a19-20020a17090acb9300b0025356cce0e4sm279592pju.24.2023.05.18.18.19.27 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Thu, 18 May 2023 18:19:28 -0700 (PDT) From: jeffxu@chromium.org To: dave.hansen@intel.com, luto@kernel.org, jorgelo@chromium.org, keescook@chromium.org, groeck@chromium.org, jannh@google.com, sroettger@google.com Cc: akpm@linux-foundation.org, jeffxu@google.com, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-mm@kvack.org, linux-hardening@vger.kernel.org Subject: [PATCH v1 2/6] PKEY: Add arch_check_pkey_enforce_api() Date: Fri, 19 May 2023 01:19:10 +0000 Message-ID: <20230519011915.846407-3-jeffxu@chromium.org> X-Mailer: git-send-email 2.40.1.698.g37aff9b760-goog In-Reply-To: <20230519011915.846407-1-jeffxu@chromium.org> References: <20230519011915.846407-1-jeffxu@chromium.org> MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_NONE, SPF_HELO_NONE,SPF_PASS,T_SCC_BODY_TEXT_LINE,URIBL_BLOCKED autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1766283593500960357?= X-GMAIL-MSGID: =?utf-8?q?1766283593500960357?= From: Jeff Xu This patch adds an architecture-independent function, arch_check_pkey_enforce_api(), that checks whether the calling thread has write access to the PKRU for a given range of memory. If the memory range is protected by PKEY_ENFORCE_API, then the thread must have write access to the PKRU in order to make changes to the memory mapping (such as mprotect/munmap). This function is used by the kernel to enforce the PKEY_ENFORCE_API flag. Signed-off-by: Jeff Xu --- arch/powerpc/include/asm/pkeys.h | 8 +++++ arch/x86/include/asm/pkeys.h | 50 ++++++++++++++++++++++++++++++++ include/linux/pkeys.h | 9 ++++++ 3 files changed, 67 insertions(+) diff --git a/arch/powerpc/include/asm/pkeys.h b/arch/powerpc/include/asm/pkeys.h index 943333ac0fee..24c481e5e95b 100644 --- a/arch/powerpc/include/asm/pkeys.h +++ b/arch/powerpc/include/asm/pkeys.h @@ -177,5 +177,13 @@ static inline bool arch_check_pkey_alloc_flags(unsigned long flags) return true; } +static inline int arch_check_pkey_enforce_api(struct mm_struct *mm, + unsigned long start, + unsigned long end) +{ + /* Allow by default */ + return 0; +} + extern void pkey_mm_init(struct mm_struct *mm); #endif /*_ASM_POWERPC_KEYS_H */ diff --git a/arch/x86/include/asm/pkeys.h b/arch/x86/include/asm/pkeys.h index ecadf04a8251..8b94ffc4ca32 100644 --- a/arch/x86/include/asm/pkeys.h +++ b/arch/x86/include/asm/pkeys.h @@ -161,4 +161,54 @@ static inline bool arch_check_pkey_alloc_flags(unsigned long flags) return true; } + +static inline int __arch_check_vma_pkey_for_write(struct vm_area_struct *vma) +{ + int pkey = vma_pkey(vma); + + if (mm_pkey_enforce_api(vma->vm_mm, pkey)) { + if (!__pkru_allows_write(read_pkru(), pkey)) + return -EACCES; + } + + return 0; +} + +/* + * arch_check_pkey_enforce_api is used by the kernel to enforce + * PKEY_ENFORCE_API flag. + * It checks whether the calling thread has write access to the PKRU + * for a given range of memory. If the memory range is protected by + * PKEY_ENFORCE_API, then the thread must have write access to the + * PKRU in order to make changes to the memory mapping, such as + * mprotect/munmap. + */ +static inline int arch_check_pkey_enforce_api(struct mm_struct *mm, + unsigned long start, + unsigned long end) +{ + int error; + struct vm_area_struct *vma; + + if (!arch_pkeys_enabled()) + return 0; + + while (true) { + vma = find_vma_intersection(mm, start, end); + if (!vma) + break; + + error = __arch_check_vma_pkey_for_write(vma); + if (error) + return error; + + if (vma->vm_end >= end) + break; + + start = vma->vm_end; + } + + return 0; +} + #endif /*_ASM_X86_PKEYS_H */ diff --git a/include/linux/pkeys.h b/include/linux/pkeys.h index 81a482c3e051..7b00689e1c24 100644 --- a/include/linux/pkeys.h +++ b/include/linux/pkeys.h @@ -53,6 +53,15 @@ static inline bool arch_check_pkey_alloc_flags(unsigned long flags) return false; return true; } + +static inline int arch_check_pkey_enforce_api(struct mm_struct *mm, + unsigned long start, + unsigned long end) +{ + // Allow by default. + return 0; +} + #endif /* ! CONFIG_ARCH_HAS_PKEYS */ #endif /* _LINUX_PKEYS_H */ From patchwork Fri May 19 01:19:11 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jeff Xu X-Patchwork-Id: 96158 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:b0ea:0:b0:3b6:4342:cba0 with SMTP id b10csp913018vqo; Thu, 18 May 2023 18:27:46 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ7nIB/G5J+zA0vZBD65a6F2yidSO2Upg1jxPtO3WPs1OXCLOwqYjmQqVl8ZoU/M5Xfj2a9T X-Received: by 2002:a17:902:d2c2:b0:1ac:b4db:6a62 with SMTP id n2-20020a170902d2c200b001acb4db6a62mr867982plc.65.1684459666197; Thu, 18 May 2023 18:27:46 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1684459666; cv=none; d=google.com; s=arc-20160816; b=mPPxQ1pZOurjOSGjVnpFNsPf0BRU0PTVNVoYr3cxuC5kqtgJiJfOw1q8KHOSPqEj/k imarxhTOA8syvdd8NtQcJru0vRfwrsSRxy/fzX4eGTXum1v2xY2ENtbWuzM0amjyMoHQ BQ7Qv4gdimg2TfQshkSMBGP71Ndy2ShKf1Mr7KS877ZBGdMlJkRCy8hDuyTJapWbg4zi qCS4meec7Cjnnrle4s1ikBnGw2/ETesEhP3JeOn8eHDcaQtWYo7wL3itzJklJDJYAgWZ oD2J5MxU+TNifAu+Li8g9o3cz2/SfTUWKNplNBDXhhORKWoevDV2VxY2eTatWaD7gPQu 1W1A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=DlYfhs3BTQ2EJ5c00OoF7xAAokRQfPc4lQTpfzHm9OE=; b=YcZSMmxk7tQdjI88rywHGM5hbrCnq9rk3RwctwepSOyaRxRf1qkOcegoJjohM4RPZk PbRBiQp25XUPSLCkQ4F3Qa19/zxlIiNg5ZbFocA0vH2Qvrm6aQ/FUIpH0ah66HAaxMFd 4svtmQ/G1TTYMZL44EgPvFySoStZAgo9v2uIO/2GIynthcxd9epTavcV+HyrQG66P8Ay dETpWw7S7ZGuoJFHymMJ4VfnWbOa5p2XmtJJ5iKmMEYWJeTqBygZurcNS7Zj1eSKcNB6 RMPl2+HcbNlhp6YxD77wwvOQ0MTZUpSDj/UDpGLmai5exQ9Uyp1HWBSqGZ7om4fxgY6m FRyA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@chromium.org header.s=google header.b=JWIfslA4; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=chromium.org Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id ja2-20020a170902efc200b0019e9c8e51f4si2490169plb.165.2023.05.18.18.27.33; Thu, 18 May 2023 18:27:46 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@chromium.org header.s=google header.b=JWIfslA4; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=chromium.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230338AbjESBTj (ORCPT + 99 others); Thu, 18 May 2023 21:19:39 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53578 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230469AbjESBTc (ORCPT ); Thu, 18 May 2023 21:19:32 -0400 Received: from mail-pf1-x435.google.com (mail-pf1-x435.google.com [IPv6:2607:f8b0:4864:20::435]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 448B9E72 for ; Thu, 18 May 2023 18:19:30 -0700 (PDT) Received: by mail-pf1-x435.google.com with SMTP id d2e1a72fcca58-64384c6797eso2123267b3a.2 for ; Thu, 18 May 2023 18:19:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; t=1684459169; x=1687051169; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=DlYfhs3BTQ2EJ5c00OoF7xAAokRQfPc4lQTpfzHm9OE=; b=JWIfslA4UKDBw1Mp583btzlGGK8G9E6xpS7M8yt3iOyE0agyVlu+KO5Lxq4vpucXSU HH1YA4BQeb7OL6OLVkoix4UxlcxXNVOrDy2c5gWBw82vyYqfMb8tY9sDkBb9j+Na0b8j 0kO3Cd+a0jrUOyQcvN7J2vDvvbkTYN/CiGCqY= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1684459169; x=1687051169; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=DlYfhs3BTQ2EJ5c00OoF7xAAokRQfPc4lQTpfzHm9OE=; b=V//B6ophZ5yr9JHjcjv3IKtm7+vuUba87wbJjHW+fePx7g/Yq+OhYDXpZXsguEduWp YbOJpBp16/5SgO23NsBy55WPLtsNXGTjdiAQ0WIy9PqIUEMtPsHmMeXbnj8V9uv9/7Fz vewyOptKPqTIZyJOSQzoLiWtde9MNjaHl4esXbpM6RMkcMKhTbKh4Mok84gcxZc4kPli R+UfOAA6E8sfdIDYqnM5bgKyqUToITMAztq7eon067hxQiiX9n9JEjtRD7QHWsBMFe2d eW+GTYwhejM+0CXSAP0k/zBBQdphURQpx3BVyTy/sk5GUowKCn46IOICadbrwY3k8AjU 8JaA== X-Gm-Message-State: AC+VfDzpR/CkQ581DYwNYnHoLo1uIUvKF2M/cl88Z64AsVdjGHIFvZqY WYhgScwPFGsI+xQF45eaVU0wGw== X-Received: by 2002:a05:6a20:8422:b0:ef:205f:8184 with SMTP id c34-20020a056a20842200b000ef205f8184mr425525pzd.13.1684459169739; Thu, 18 May 2023 18:19:29 -0700 (PDT) Received: from localhost (183.43.230.35.bc.googleusercontent.com. [35.230.43.183]) by smtp.gmail.com with UTF8SMTPSA id c24-20020a62e818000000b00643864d03dfsm1960424pfi.171.2023.05.18.18.19.29 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Thu, 18 May 2023 18:19:29 -0700 (PDT) From: jeffxu@chromium.org To: dave.hansen@intel.com, luto@kernel.org, jorgelo@chromium.org, keescook@chromium.org, groeck@chromium.org, jannh@google.com, sroettger@google.com Cc: akpm@linux-foundation.org, jeffxu@google.com, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-mm@kvack.org, linux-hardening@vger.kernel.org Subject: [PATCH v1 3/6] PKEY: Apply PKEY_ENFORCE_API to mprotect Date: Fri, 19 May 2023 01:19:11 +0000 Message-ID: <20230519011915.846407-4-jeffxu@chromium.org> X-Mailer: git-send-email 2.40.1.698.g37aff9b760-goog In-Reply-To: <20230519011915.846407-1-jeffxu@chromium.org> References: <20230519011915.846407-1-jeffxu@chromium.org> MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_NONE, SPF_HELO_NONE,SPF_PASS,T_SCC_BODY_TEXT_LINE,URIBL_BLOCKED autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1766283979177006738?= X-GMAIL-MSGID: =?utf-8?q?1766283979177006738?= From: Jeff Xu This patch enables PKEY_ENFORCE_API for the mprotect and mprotect_pkey syscalls. Signed-off-by: Jeff Xu --- mm/mprotect.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/mm/mprotect.c b/mm/mprotect.c index 8a68fdca8487..1db30b8baac3 100644 --- a/mm/mprotect.c +++ b/mm/mprotect.c @@ -794,6 +794,17 @@ static int do_mprotect_pkey(unsigned long start, size_t len, } } + /* + * arch_check_pkey_enforce_api checks if current thread + * has the PKEY permission to modify the memory mapping. + * Note: this should only apply to the cases that do_mprotect_pkey + * is called from syscall entry. Ref. to munmap for other cases. + */ + if (arch_check_pkey_enforce_api(current->mm, start, end) < 0) { + error = -EACCES; + goto out; + } + prev = vma_prev(&vmi); if (start > vma->vm_start) prev = vma; From patchwork Fri May 19 01:19:12 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jeff Xu X-Patchwork-Id: 96160 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:b0ea:0:b0:3b6:4342:cba0 with SMTP id b10csp916938vqo; Thu, 18 May 2023 18:39:16 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ7f6JC0mOcQl8t3Nc/44GJ3B8YaO++VPloGCrPvxhLcEbUYjhCAs29962hfhBL3G6ovMmE+ X-Received: by 2002:a17:90a:4298:b0:253:629e:dc3b with SMTP id p24-20020a17090a429800b00253629edc3bmr437660pjg.44.1684460356596; Thu, 18 May 2023 18:39:16 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1684460356; cv=none; d=google.com; s=arc-20160816; b=sZ/NR1Xm1Jb01UXvTtZd3o9QTZaATfrum/gjcXBnDq6dxdQ3ek7VMbWova05VOQ2f5 8l+/OFm3laUm/OU8972+jgfnDfobooODe6pIZLztAEPmdo6rqrOOIlNfgOndgIA4wo9E k4SpPo9tO/OZg768BXgLe4f5XisrJ1aYwYW1WS3O3ePm2buRwlTMAq4Y7kWVxAvRj+zq MwxP2n6w2X6RLSPteiEdvbXX/hxngPIha3BpJ0OOIQC6R1qmy71Ux5HnRHZSAGTfmXhz tV4spu9158XeD/RFayecX+keGn4xj5cKY+5LY6q4Ca/t7e9Hx0cKxXpOsVduD32freWy sPVQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=qzO3EawE44DmDL2RDFGxHbI+J47AsuGzE5x/fc5YRSs=; b=JlhIPV6A6j/YusnG43DrLPO7P84/0dHM1niYJdSenT0UDr2RUJiHstJlUn7ElQ/sva 5aBlq553GW+7tGDtvfTjfpsrrolXRzMrt0qkuAMdRfNu8xYAQwwM6QJBghu8wguFRkVg ul89p2O94SPY//JkO7BS2ZEALls+x1NPIbuj/5a1wohxcTRZIVLbQUmPvTAZpxhzdl0z UwXPZEtlXCNGPpeOvFlvz6fEGBm4UMAiHT919sqTBP09LqzGTBeXYGUipjPAeDlWsV+S gcJVqktqVEn/pwt2qDiV3uM2RWq6yHLIHbzcy+fEUBOOP3A0jzovi80bLAZAZAKmeiuf YjCg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@chromium.org header.s=google header.b=Xz+5GZCb; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=chromium.org Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id a1-20020a17090a854100b00253482cbb31si781750pjw.56.2023.05.18.18.39.02; Thu, 18 May 2023 18:39:16 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@chromium.org header.s=google header.b=Xz+5GZCb; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=chromium.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S230469AbjESBTm (ORCPT + 99 others); Thu, 18 May 2023 21:19:42 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53596 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231132AbjESBTe (ORCPT ); Thu, 18 May 2023 21:19:34 -0400 Received: from mail-pg1-x52f.google.com (mail-pg1-x52f.google.com [IPv6:2607:f8b0:4864:20::52f]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5F9B0E75 for ; Thu, 18 May 2023 18:19:31 -0700 (PDT) Received: by mail-pg1-x52f.google.com with SMTP id 41be03b00d2f7-5304913530fso2482356a12.0 for ; Thu, 18 May 2023 18:19:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; t=1684459171; x=1687051171; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=qzO3EawE44DmDL2RDFGxHbI+J47AsuGzE5x/fc5YRSs=; b=Xz+5GZCb3fba+tXdlILZoYQkV4SVPHj5Vwq3DIRmzGvX64eYAzu0JkwUukNwnffs54 M9v00G5vG3JMYqhRAZSNj3NjdZf+5DSxJMHNq9AQDEWFtawgnIFbemZ5wunbbCOLlxtw ghWDWiFb7TtOseQ9yPeyC/EQ4o67LIc0xOhfo= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1684459171; x=1687051171; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=qzO3EawE44DmDL2RDFGxHbI+J47AsuGzE5x/fc5YRSs=; b=mA/cUqmWL8zJLzSBboKBIEpr3hPDuKTFhSoi1UXATOdO7Z1RuHphnSVse2XHlGp+Xp ilEKP6JLs1G3J3uaY7Xp/r7Rtfkv5PxmLRhYv20sLPWi35JuzAMfkG4knea9041TD4pA PxY9NYuqEY/5Lm7IB3iOmq9b9LBMwUqrSMh52iU6U7Yj4aPkS84WXXGfGKRGQFj1AUA+ WNBErddMahYy3XWHssTYeDZ5k25T6RNnf/Q2pqp02RZ9J2o2goA5TAgYn/GMk3TXm7B3 xbIH9794qCnpjzIi3+EbE5lhjsHxu0hAjmNBCm4Smh8PdZr9RoJdgmV63FBLt0lVEoRZ T6OQ== X-Gm-Message-State: AC+VfDxUWqVv8P7R+oHoxSlwHNCIS1dRDDIAc6BhcoSMPnfPMB7QqjWl M90WYnmdLpYNs86GvYxlmoEOlA== X-Received: by 2002:a05:6a20:c18d:b0:102:7aec:6dcc with SMTP id bg13-20020a056a20c18d00b001027aec6dccmr299922pzb.27.1684459170711; Thu, 18 May 2023 18:19:30 -0700 (PDT) Received: from localhost (183.43.230.35.bc.googleusercontent.com. [35.230.43.183]) by smtp.gmail.com with UTF8SMTPSA id f62-20020a17090a704400b0023d386e4806sm294372pjk.57.2023.05.18.18.19.30 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Thu, 18 May 2023 18:19:30 -0700 (PDT) From: jeffxu@chromium.org To: dave.hansen@intel.com, luto@kernel.org, jorgelo@chromium.org, keescook@chromium.org, groeck@chromium.org, jannh@google.com, sroettger@google.com Cc: akpm@linux-foundation.org, jeffxu@google.com, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-mm@kvack.org, linux-hardening@vger.kernel.org Subject: [PATCH v1 4/6] PKEY:selftest pkey_enforce_api for mprotect Date: Fri, 19 May 2023 01:19:12 +0000 Message-ID: <20230519011915.846407-5-jeffxu@chromium.org> X-Mailer: git-send-email 2.40.1.698.g37aff9b760-goog In-Reply-To: <20230519011915.846407-1-jeffxu@chromium.org> References: <20230519011915.846407-1-jeffxu@chromium.org> MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_NONE, SPF_HELO_NONE,SPF_PASS,T_SCC_BODY_TEXT_LINE,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1766284702833821707?= X-GMAIL-MSGID: =?utf-8?q?1766284702833821707?= From: Jeff Xu Add selftest for pkey_enforce_api for mprotect. Signed-off-by: Jeff Xu --- tools/testing/selftests/mm/Makefile | 1 + tools/testing/selftests/mm/pkey_enforce_api.c | 875 ++++++++++++++++++ 2 files changed, 876 insertions(+) create mode 100644 tools/testing/selftests/mm/pkey_enforce_api.c diff --git a/tools/testing/selftests/mm/Makefile b/tools/testing/selftests/mm/Makefile index 23af4633f0f4..93437a394128 100644 --- a/tools/testing/selftests/mm/Makefile +++ b/tools/testing/selftests/mm/Makefile @@ -71,6 +71,7 @@ CAN_BUILD_X86_64 := $(shell ./../x86/check_cc.sh "$(CC)" ../x86/trivial_64bit_pr CAN_BUILD_WITH_NOPIE := $(shell ./../x86/check_cc.sh "$(CC)" ../x86/trivial_program.c -no-pie) VMTARGETS := protection_keys +VMTARGETS += pkey_enforce_api BINARIES_32 := $(VMTARGETS:%=%_32) BINARIES_64 := $(VMTARGETS:%=%_64) diff --git a/tools/testing/selftests/mm/pkey_enforce_api.c b/tools/testing/selftests/mm/pkey_enforce_api.c new file mode 100644 index 000000000000..23663c89bc9c --- /dev/null +++ b/tools/testing/selftests/mm/pkey_enforce_api.c @@ -0,0 +1,875 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Tests pkey_enforce_api + * + * Compile like this: + * gcc -mxsave -o pkey_enforce_api -O2 -g -std=gnu99 -pthread -Wall pkey_enforce_api.c \ + * -lrt -ldl -lm + * gcc -mxsave -m32 -o pkey_enforce_api_32 -O2 -g -std=gnu99 -pthread -Wall pkey_enforce_api.c \ + * -lrt -ldl -lm + */ +#define _GNU_SOURCE +#define __SANE_USERSPACE_TYPES__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../kselftest.h" +#include + +#if defined(__i386__) || defined(__x86_64__) /* arch */ + +#define dprintf0(args...) +#define dprintf1(args...) +#define dprintf2(args...) +#define dprintf3(args...) +#define dprintf4(args...) + +#ifndef u16 +#define u16 __u16 +#endif + +#ifndef u32 +#define u32 __u32 +#endif + +#ifndef u64 +#define u64 __u64 +#endif + +#ifndef PTR_ERR_ENOTSUP +#define PTR_ERR_ENOTSUP ((void *)-ENOTSUP) +#endif + +int read_ptr(int *ptr) +{ + return *ptr; +} + +void expected_pkey_fault(int pkey) +{ +} + +#include "pkey-x86.h" + +#ifndef PKEY_ENFORCE_API +#define PKEY_ENFORCE_API 1 +#endif + +#define PKEY_MASK (PKEY_DISABLE_ACCESS | PKEY_DISABLE_WRITE) + +#define LOG_TEST_ENTER(x) \ + { \ + printf("%s, enforce=%d\n", __func__, x); \ + } +static inline u64 set_pkey_bits(u64 reg, int pkey, u64 flags) +{ + u32 shift = pkey_bit_position(pkey); + /* mask out bits from pkey in old value */ + reg &= ~((u64)PKEY_MASK << shift); + /* OR in new bits for pkey */ + reg |= (flags & PKEY_MASK) << shift; + return reg; +} + +static inline u64 get_pkey_bits(u64 reg, int pkey) +{ + u32 shift = pkey_bit_position(pkey); + /* + * shift down the relevant bits to the lowest two, then + * mask off all the other higher bits + */ + return ((reg >> shift) & PKEY_MASK); +} + +static u32 get_pkey(int pkey) +{ + return (u32)get_pkey_bits(__read_pkey_reg(), pkey); +} + +static void set_pkey(int pkey, unsigned long pkey_value) +{ + u32 mask = (PKEY_DISABLE_ACCESS | PKEY_DISABLE_WRITE); + u64 new_pkey_reg; + + assert(!(pkey_value & ~mask)); + new_pkey_reg = set_pkey_bits(__read_pkey_reg(), pkey, pkey_value); + __write_pkey_reg(new_pkey_reg); +} + +void pkey_disable_set(int pkey, int value) +{ + int pkey_new; + + assert(value & (PKEY_DISABLE_ACCESS | PKEY_DISABLE_WRITE)); + + pkey_new = get_pkey(pkey); + pkey_new |= value; + set_pkey(pkey, pkey_new); +} + +void pkey_disable_clear(int pkey, int value) +{ + int pkey_new; + + assert(value & (PKEY_DISABLE_ACCESS | PKEY_DISABLE_WRITE)); + + pkey_new = get_pkey(pkey); + pkey_new &= ~value; + + set_pkey(pkey, pkey_new); +} + +void pkey_write_allow(int pkey) +{ + pkey_disable_clear(pkey, PKEY_DISABLE_WRITE); +} +void pkey_write_deny(int pkey) +{ + pkey_disable_set(pkey, PKEY_DISABLE_WRITE); +} +void pkey_access_allow(int pkey) +{ + pkey_disable_clear(pkey, PKEY_DISABLE_ACCESS); +} +void pkey_access_deny(int pkey) +{ + pkey_disable_set(pkey, PKEY_DISABLE_ACCESS); +} + +int sys_mprotect(void *ptr, size_t size, unsigned long prot) +{ + int sret; + + errno = 0; + sret = syscall(SYS_mprotect, ptr, size, prot); + return sret; +} + +int sys_mprotect_pkey(void *ptr, size_t size, unsigned long orig_prot, + unsigned long pkey) +{ + int sret; + + errno = 0; + sret = syscall(SYS_mprotect_key, ptr, size, orig_prot, pkey); + return sret; +} + +int sys_pkey_alloc(unsigned long flags, unsigned long init_val) +{ + int ret = syscall(SYS_pkey_alloc, flags, init_val); + return ret; +} + +int sys_pkey_free(unsigned long pkey) +{ + int ret = syscall(SYS_pkey_free, pkey); + return ret; +} + +bool can_create_pkey(void) +{ + int pkey; + + pkey = sys_pkey_alloc(0, 0); + if (pkey <= 0) + return false; + + sys_pkey_free(pkey); + return true; +} + +static inline int is_pkeys_supported(void) +{ + /* check if the cpu supports pkeys */ + if (!cpu_has_pkeys() || !can_create_pkey()) + return 0; + return 1; +} + +int pkey_alloc_with_check(bool enforce) +{ + int pkey; + + if (enforce) + pkey = sys_pkey_alloc(PKEY_ENFORCE_API, 0); + else + pkey = sys_pkey_alloc(0, 0); + + assert(pkey > 0); + return pkey; +} + +void *addr1 = (void *)0x5000000; +void *addr2 = (void *)0x5001000; +void *addr3 = (void *)0x5002000; +void *addr4 = (void *)0x5003000; + +void setup_single_address_with_pkey(bool enforce, int size, int *pkeyOut, + void **ptrOut) +{ + int pkey; + void *ptr; + int ret; + + pkey = pkey_alloc_with_check(enforce); + + ptr = mmap(NULL, size, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + assert(ptr != (void *)-1); + + // assign pkey to the memory. + ret = sys_mprotect_pkey((void *)ptr, size, PROT_READ, pkey); + assert(!ret); + + *pkeyOut = pkey; + *ptrOut = ptr; +} + +void setup_single_fixed_address_with_pkey(bool enforce, int size, int *pkeyOut, + void **ptrOut) +{ + int pkey; + void *ptr; + int ret; + + pkey = pkey_alloc_with_check(enforce); + + ptr = mmap(addr1, size, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + assert(ptr == (void *)addr1); + + // assign pkey to the memory. + ret = sys_mprotect_pkey((void *)ptr, size, PROT_READ, pkey); + assert(!ret); + + *pkeyOut = pkey; + *ptrOut = ptr; +} + +void clean_single_address_with_pkey(int pkey, void *ptr, int size) +{ + int ret; + + ret = munmap(ptr, size); + assert(!ret); + + ret = sys_pkey_free(pkey); + assert(ret == 0); +} + +void setup_two_continues_fixed_address_with_pkey(bool enforce, int size, + int *pkeyOut, void **ptrOut, + void **ptr2Out) +{ + void *ptr; + void *ptr2; + int pkey; + int ret; + + pkey = pkey_alloc_with_check(enforce); + + ptr = mmap(addr1, size, PROT_READ, + MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0); + assert(ptr == addr1); + + ptr2 = mmap(addr2, size, PROT_READ, + MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0); + assert(ptr2 == addr2); + + // assign pkey to both addresses in the same call (merged) + ret = sys_mprotect_pkey(ptr, size * 2, + PROT_READ | PROT_WRITE | PROT_EXEC, pkey); + assert(!ret); + *pkeyOut = pkey; + *ptrOut = ptr; + *ptr2Out = ptr2; +} + +void clean_two_address_with_pkey(int size, int pkey, void *ptr, void *ptr2) +{ + int ret; + + ret = munmap(ptr, size); + assert(!ret); + + ret = munmap(ptr2, size); + assert(!ret); + + ret = sys_pkey_free(pkey); + assert(ret == 0); +} + +// pkey_alloc with flags. +void test_pkey_alloc(bool enforce) +{ + int ret; + + LOG_TEST_ENTER(enforce); + + ret = sys_pkey_alloc(0, 0); + assert(ret > 0); + ret = sys_pkey_free(ret); + assert(ret == 0); + + if (enforce) { + ret = sys_pkey_alloc(PKEY_ENFORCE_API, 0); + assert(ret > 0); + ret = sys_pkey_free(ret); + assert(ret == 0); + + // invalid flag. + ret = sys_pkey_alloc(0x4, 0); + assert(ret != 0); + } +} + +// mmap one address. +// assign pkey on the address. +// mprotect is denied when no-writeable PKRU in enforce mode. +void test_mprotect_single_address(bool enforce) +{ + int pkey; + int ret; + void *ptr; + int size = PAGE_SIZE; + + LOG_TEST_ENTER(enforce); + + setup_single_fixed_address_with_pkey(enforce, size, &pkey, &ptr); + + // disable write access. + pkey_write_deny(pkey); + + ret = sys_mprotect_pkey(ptr, size, PROT_READ | PROT_WRITE, pkey); + if (enforce) + assert(ret < 0); + else + assert(!ret); + + ret = sys_mprotect(ptr, size, PROT_READ | PROT_WRITE); + if (enforce) + assert(ret < 0); + else + assert(ret == 0); + + pkey_write_allow(pkey); + + ret = sys_mprotect_pkey(ptr, size, PROT_READ, pkey); + assert(!ret); + + ret = sys_mprotect(ptr, size, PROT_READ); + assert(ret == 0); + + clean_single_address_with_pkey(pkey, ptr, size); +} + +// mmap two address (continuous two pages). +// assign PKEY to them with one mprotect_pkey call (merged address). +// mprotect is denied when non-writeable PKRU in enforce mode. +void test_mprotect_two_address_merge(bool enforce) +{ + int pkey; + int ret; + void *ptr; + void *ptr2; + int size = PAGE_SIZE; + + LOG_TEST_ENTER(enforce); + + setup_two_continues_fixed_address_with_pkey(enforce, size, &pkey, &ptr, + &ptr2); + + // disable write. + pkey_write_deny(pkey); + + // modify the protection on both addresses (merged). + ret = sys_mprotect(ptr, size * 2, PROT_READ | PROT_WRITE | PROT_EXEC); + if (enforce) + assert(ret < 0); + else + assert(!ret); + + ret = sys_mprotect_pkey(ptr, size * 2, + PROT_READ | PROT_WRITE | PROT_EXEC, pkey); + if (enforce) + assert(ret < 0); + else + assert(!ret); + + pkey_write_allow(pkey); + + // modify the protection on both addresses (merged). + ret = sys_mprotect(ptr, size * 2, PROT_READ | PROT_WRITE | PROT_EXEC); + assert(!ret); + + ret = sys_mprotect_pkey(ptr, size * 2, + PROT_READ | PROT_WRITE | PROT_EXEC, pkey); + assert(!ret); + + clean_two_address_with_pkey(size, pkey, ptr, ptr2); +} + +void setup_two_continues_fixed_address_protect_second_with_pkey( + bool enforce, int size, int *pkeyOut, void **ptrOut, void **ptr2Out) +{ + void *ptr; + void *ptr2; + int pkey; + int ret; + + LOG_TEST_ENTER(enforce); + + pkey = pkey_alloc_with_check(enforce); + + // mmap two addresses (continuous two pages). + ptr = mmap(addr1, size, PROT_READ, + MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0); + assert(ptr == addr1); + + ptr2 = mmap(addr2, size, PROT_READ, + MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0); + assert(ptr2 == addr2); + + // assign pkey to the second page. + ret = sys_mprotect_pkey(addr2, size, PROT_READ | PROT_WRITE | PROT_EXEC, + pkey); + assert(!ret); + + *pkeyOut = pkey; + *ptrOut = ptr; + *ptr2Out = ptr2; +} + +// mmap two address (continuous two pages). +// assign PKEY to the second address. +// mprotect on the second address is denied properly. +// mprotect on both addresses (merged) is denied properly. +void test_mprotect_two_address_deny_second(bool enforce) +{ + int pkey; + int ret; + void *ptr; + void *ptr2; + int size = PAGE_SIZE; + + LOG_TEST_ENTER(enforce); + + setup_two_continues_fixed_address_protect_second_with_pkey( + enforce, size, &pkey, &ptr, &ptr2); + + // disable write through pkey. + pkey_write_deny(pkey); + + // modify the first addr is allowed. + ret = sys_mprotect(ptr, size, PROT_READ | PROT_WRITE | PROT_EXEC); + assert(!ret); + + // modify the second mmap is protected by pkey. + ret = sys_mprotect(ptr2, size, PROT_READ | PROT_WRITE | PROT_EXEC); + if (enforce) + assert(ret < 0); + else + assert(!ret); + + // mprotect both addresses (merged). + ret = sys_mprotect_pkey(ptr, size * 2, + PROT_READ | PROT_WRITE | PROT_EXEC, pkey); + if (enforce) + assert(ret < 0); + else + assert(!ret); + + ret = sys_mprotect(ptr, size * 2, PROT_READ | PROT_WRITE | PROT_EXEC); + if (enforce) + assert(ret < 0); + else + assert(!ret); + + pkey_write_allow(pkey); + + ret = sys_mprotect_pkey(ptr, size * 2, PROT_READ, pkey); + assert(!ret); + + ret = sys_mprotect(ptr, size * 2, PROT_READ); + assert(!ret); + + clean_two_address_with_pkey(size, pkey, ptr, ptr2); +} + +void setup_4pages_fixed_protect_second_page(bool enforce, int size, + int *pkeyOut, void **ptrOut, + void **ptr2Out, void **ptr3Out) +{ + int pkey; + int ret; + void *ptr; + + pkey = pkey_alloc_with_check(enforce); + + // allocate 4 pages. + ptr = mmap(addr1, size * 4, PROT_READ, + MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0); + assert(ptr == addr1); + + // assign pkey to the second address. + ret = sys_mprotect_pkey(addr2, size, PROT_READ | PROT_WRITE | PROT_EXEC, + pkey); + assert(!ret); + + *pkeyOut = pkey; + *ptrOut = ptr; + *ptr2Out = addr2; + *ptr3Out = addr3; +} + +// mmap one address with 4 pages. +// assign PKEY to the second page only. +// mprotect on the first page is allowed. +// mprotect on the second page is protected in enforce mode. +// mprotect on memory range that includes the second pages is protected. +void test_mprotect_vma_middle_addr(bool enforce) +{ + int pkey; + int ret; + void *ptr, *ptr2, *ptr3; + int size = PAGE_SIZE; + + LOG_TEST_ENTER(enforce); + + setup_4pages_fixed_protect_second_page(enforce, size, &pkey, &ptr, + &ptr2, &ptr3); + + // disable write through pkey. + pkey_write_deny(pkey); + + // modify to the first page is allowed. + ret = sys_mprotect(ptr, size, PROT_READ | PROT_WRITE | PROT_EXEC); + assert(!ret); + + // modify to the third page is allowed. + ret = sys_mprotect(ptr3, size, PROT_READ | PROT_WRITE | PROT_EXEC); + assert(!ret); + + // modify to the second page is protected by pkey. + ret = sys_mprotect(ptr2, size, PROT_READ | PROT_WRITE | PROT_EXEC); + if (enforce) + assert(ret < 0); + else + assert(!ret); + + // modify to memory range that includes the second page is protected. + ret = sys_mprotect_pkey(ptr, size * 4, + PROT_READ | PROT_WRITE | PROT_EXEC, pkey); + if (enforce) + assert(ret < 0); + else + assert(!ret); + + ret = sys_mprotect(ptr, size * 4, PROT_READ | PROT_WRITE | PROT_EXEC); + if (enforce) + assert(ret < 0); + else + assert(!ret); + + pkey_write_allow(pkey); + + ret = sys_mprotect(addr2, size, PROT_READ | PROT_WRITE | PROT_EXEC); + assert(!ret); + + ret = sys_mprotect_pkey(ptr, size * 4, + PROT_READ | PROT_WRITE | PROT_EXEC, pkey); + assert(!ret); + + clean_single_address_with_pkey(pkey, ptr, size * 4); +} + +// mmap one address with 4 pages. +// assign PKEY to the second page only. +// mprotect on the second page, but size is unaligned. +void test_mprotect_unaligned(bool enforce) +{ + int pkey; + int ret; + void *ptr, *ptr2, *ptr3; + int size = PAGE_SIZE; + + LOG_TEST_ENTER(enforce); + + setup_4pages_fixed_protect_second_page(enforce, size, &pkey, &ptr, + &ptr2, &ptr3); + + // disable write through pkey. + pkey_write_deny(pkey); + + // modify to the first page is allowed. + ret = sys_mprotect(ptr, size, PROT_READ | PROT_WRITE | PROT_EXEC); + assert(!ret); + + // modify to the second page is protected by pkey. + ret = sys_mprotect(ptr2, size - 1, PROT_READ | PROT_WRITE | PROT_EXEC); + if (enforce) + assert(ret < 0); + else + assert(!ret); + + pkey_write_allow(pkey); + + ret = sys_mprotect(addr2, size - 1, PROT_READ | PROT_WRITE | PROT_EXEC); + assert(!ret); + + clean_single_address_with_pkey(pkey, ptr, size * 4); +} + +// mmap one address with 4 pages. +// assign PKEY to the second page only. +// mprotect on the second page, but size is unaligned. +void test_mprotect_unaligned2(bool enforce) +{ + int pkey; + int ret; + void *ptr, *ptr2, *ptr3; + int size = PAGE_SIZE; + + LOG_TEST_ENTER(enforce); + + setup_4pages_fixed_protect_second_page(enforce, size, &pkey, &ptr, + &ptr2, &ptr3); + + // disable write through pkey. + pkey_write_deny(pkey); + + // modify to the first page is allowed. + ret = sys_mprotect(ptr, size, PROT_READ | PROT_WRITE | PROT_EXEC); + assert(!ret); + + // modify to the second page is protected by pkey. + ret = sys_mprotect(ptr2, size + 1, PROT_READ | PROT_WRITE | PROT_EXEC); + if (enforce) + assert(ret < 0); + else + assert(!ret); + + pkey_write_allow(pkey); + + ret = sys_mprotect(addr2, size + 1, PROT_READ | PROT_WRITE | PROT_EXEC); + assert(!ret); + + clean_single_address_with_pkey(pkey, ptr, size * 4); +} + +void setup_address_with_gap_two_pkeys(bool enforce, int size, int *pkeyOut, + int *pkey2Out, void **ptrOut, + void **ptr2Out) +{ + int pkey, pkey2; + void *ptr, *ptr2; + int ret; + + pkey = pkey_alloc_with_check(enforce); + pkey2 = pkey_alloc_with_check(enforce); + + ptr = mmap(addr1, size, PROT_READ, + MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0); + assert(ptr == (void *)addr1); + + ptr2 = mmap(addr3, size, PROT_READ, + MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0); + assert(ptr2 == (void *)addr3); + + // assign pkey to the memory. + ret = sys_mprotect_pkey((void *)ptr, size, PROT_READ, pkey); + assert(!ret); + + // assign pkey to the memory. + ret = sys_mprotect_pkey((void *)ptr2, size, PROT_READ, pkey2); + assert(!ret); + + *pkeyOut = pkey; + *ptrOut = ptr; + + *pkey2Out = pkey2; + *ptr2Out = ptr2; +} + +void clean_address_with_pag_two_pkeys(int pkey, void *ptr, int pkey2, + void *ptr2, int size) +{ + int ret; + + ret = munmap(ptr, size); + assert(!ret); + + ret = sys_pkey_free(pkey); + assert(ret == 0); + + ret = munmap(ptr2, size); + assert(!ret); + + ret = sys_pkey_free(pkey2); + assert(ret == 0); +} + +// mmap two addresses, with a page gap between two. +// assign pkeys on both address. +// disable access to the second address. +// mprotect from start of address1 to the end of address 2, +// because there is a gap in the memory range, mprotect will fail. +void test_mprotect_gapped_address_with_two_pkeys(bool enforce) +{ + int pkey, pkey2; + int ret; + void *ptr, *ptr2; + int size = PAGE_SIZE; + + LOG_TEST_ENTER(enforce); + + setup_address_with_gap_two_pkeys(enforce, size, &pkey, &pkey2, &ptr, + &ptr2); + + // disable write access. + pkey_write_deny(pkey2); + + ret = sys_mprotect_pkey(ptr, size * 3, PROT_READ | PROT_WRITE, pkey); + assert(ret < 0); + + ret = sys_mprotect(ptr, size * 3, PROT_READ | PROT_WRITE); + assert(ret < 0); + + pkey_write_allow(pkey2); + + ret = sys_mprotect_pkey(ptr, size * 3, PROT_READ, pkey); + assert(ret < 0); + + ret = sys_mprotect(ptr, size * 3, PROT_READ); + assert(ret < 0); + + clean_address_with_pag_two_pkeys(pkey, ptr, pkey2, ptr2, size); +} + +struct thread_info { + int pkey; + void *addr; + int size; + bool enforce; +}; + +void *thread_mprotect(void *arg) +{ + struct thread_info *tinfo = arg; + void *ptr = tinfo->addr; + int size = tinfo->size; + bool enforce = tinfo->enforce; + int pkey = tinfo->pkey; + int ret; + + // disable write access. + pkey_write_deny(pkey); + ret = sys_mprotect_pkey(ptr, size, PROT_READ | PROT_WRITE, pkey); + + if (enforce) + assert(ret < 0); + else + assert(!ret); + + ret = sys_mprotect(ptr, size, PROT_READ | PROT_WRITE); + if (enforce) + assert(ret < 0); + else + assert(ret == 0); + + pkey_write_allow(pkey); + + ret = sys_mprotect_pkey(ptr, size, PROT_READ, pkey); + assert(!ret); + + ret = sys_mprotect(ptr, size, PROT_READ); + assert(ret == 0); + return NULL; +} + +// mmap one address. +// assign pkey on the address. +// in child thread, mprotect is denied when no-writeable PKRU in enforce mode. +void test_mprotect_child_thread(bool enforce) +{ + int pkey; + int ret; + void *ptr; + int size = PAGE_SIZE; + pthread_t thread; + struct thread_info tinfo; + + LOG_TEST_ENTER(enforce); + + setup_single_fixed_address_with_pkey(enforce, size, &pkey, &ptr); + tinfo.size = size; + tinfo.addr = ptr; + tinfo.enforce = enforce; + tinfo.pkey = pkey; + + ret = pthread_create(&thread, NULL, thread_mprotect, (void *)&tinfo); + assert(ret == 0); + pthread_join(thread, NULL); + + clean_single_address_with_pkey(pkey, ptr, size); +} + +void test_enforce_api(void) +{ + for (int i = 0; i < 2; i++) { + bool enforce = (i == 1); + + test_pkey_alloc(enforce); + + test_mprotect_single_address(enforce); + test_mprotect_two_address_merge(enforce); + test_mprotect_two_address_deny_second(enforce); + test_mprotect_vma_middle_addr(enforce); + test_mprotect_unaligned(enforce); + test_mprotect_unaligned2(enforce); + test_mprotect_child_thread(enforce); + test_mprotect_gapped_address_with_two_pkeys(enforce); + } +} + +int main(void) +{ + int pkeys_supported = is_pkeys_supported(); + + printf("pid: %d\n", getpid()); + printf("has pkeys: %d\n", pkeys_supported); + if (!pkeys_supported) { + printf("PKEY not supported, skip the test.\n"); + exit(0); + } + + test_enforce_api(); + printf("done (all tests OK)\n"); + return 0; +} +#else /* arch */ +int main(void) +{ + printf("SKIP: not supported arch\n"); + return 0; +} +#endif /* arch */ From patchwork Fri May 19 01:19:13 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jeff Xu X-Patchwork-Id: 96159 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:b0ea:0:b0:3b6:4342:cba0 with SMTP id b10csp916498vqo; Thu, 18 May 2023 18:37:55 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ4B+Ohh0mMKWy6yU29IHyOp1Cs5fv97QZkQFD0mImN0EGuBTjxQCyZsTsOGkinrAUTSvQBo X-Received: by 2002:a05:6a20:ce43:b0:102:5f75:91ea with SMTP id id3-20020a056a20ce4300b001025f7591eamr279827pzb.47.1684460274961; Thu, 18 May 2023 18:37:54 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1684460274; cv=none; d=google.com; s=arc-20160816; b=I7VsCw5Jw6wjx7EC29fGgAVeKNCMLdcuv8nyoZyZD+MfRFfoorjvFLEoZvnWOhRfAO 9G0hkwHFu6IoQW/XFi0zsEh3/M1N9myBLJJLnnrfEYQHaI9DvpwBc+yckwmwfWb0wTko nUsGYngnHawx4Op8H9UvzX+aHb45m8ouw4xRyJYiLrPVRZInomEx+s8IKxwQs9awvnut lyGDUr2Ak2dsCD3DBix0KqrR7p3RV/9S45V3MfzEQOYe26W9jriWA6UcNDE+0bUXsrlQ l28YojpheXQ5nTR2KhL81wl47v7qJsKfJTNknPRGbjvl87cLKVS28mYYZO8yiArUNGaN nbsQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=UP7PrspRcAj60L7H0nNNXk2sIZvcVn8Hzlm0pHNc5DI=; b=Ie/ZXRkU47Iq0t9XJysVRXPSeDMbEsAyxgUkcXz2CBmEq6+vbZVU7rmFZ2CeQZVqtD CN58WR2BNaVb7GNRStR4+h11B1R04ZA771BM0ovOss+nGVdLDxPK53sUBo5d+2SFaMyL kYkucZOSYH9HvrM7l0yFjnuXhxMdxB8WKjeEeV0AOeV6efsiS4mtd3R00gC3PPkjErOp xgYXAVO7Dp0XmBD0UWMDuk8djDoNszKCuScqFm470jM0Jti0h1HQqGtP45fp2zOTPxj3 7rLZaeEDysoREvlQchNpcZRsf5+gXy8ola3DL32XSh2qge9v2M9/z+XFfoltvJo1+qBG CFoA== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@chromium.org header.s=google header.b=KyUoiw4E; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=chromium.org Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id j7-20020a633c07000000b00530e32c93besi2619121pga.881.2023.05.18.18.37.41; Thu, 18 May 2023 18:37:54 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@chromium.org header.s=google header.b=KyUoiw4E; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=chromium.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231223AbjESBTq (ORCPT + 99 others); Thu, 18 May 2023 21:19:46 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53596 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231173AbjESBTe (ORCPT ); Thu, 18 May 2023 21:19:34 -0400 Received: from mail-pg1-x532.google.com (mail-pg1-x532.google.com [IPv6:2607:f8b0:4864:20::532]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id C824310DF for ; Thu, 18 May 2023 18:19:32 -0700 (PDT) Received: by mail-pg1-x532.google.com with SMTP id 41be03b00d2f7-53482b44007so566180a12.2 for ; Thu, 18 May 2023 18:19:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; t=1684459171; x=1687051171; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=UP7PrspRcAj60L7H0nNNXk2sIZvcVn8Hzlm0pHNc5DI=; b=KyUoiw4ElMe/LNfLauu3mRsWtl6tLzsjFFWTg2362wmgmg2Ddqu/eccJT5ajd/WVCK u1l0ug+0cIrf8bFw6zRHAri7y+MBhpSJTKovSJLlNyH0CjoNHHPa7W+0dgGBS04gOyOU DCYEDZN/Ib9Kcmnqo4b0yFdJ8ouzcRriQUlWU= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1684459171; x=1687051171; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=UP7PrspRcAj60L7H0nNNXk2sIZvcVn8Hzlm0pHNc5DI=; b=cF/2HDYikh0JktDk8KpO2Brm1W4/oBHWcitztnfxi3hRu5pn4YmC0VgTMJEqk+5Bxt VmT8+vrvQFSXJ4DgbNZTrko6CCBgXUg8o8MaUP05F7/hEpzgxwFj80IFyqLOS7jy3dGn 4YMhH7jczw+u+eF38cJTlK69v97DME0fQwujch3gBqFl3s1ujdoCYN3WhkgM6uN2WRDz jW6c+ag4qEy87vA4bB+9S+Z8TptJzypUeLnwYISzVtQPO5F1v9tKZrxNxx1y0ki7V1z8 UhJdj4ejFafvNtnvihYjc5jMnLZPTJuVePyTYYv3flaS5eC8MNBlGsdcD/7mowfd1gHr YSig== X-Gm-Message-State: AC+VfDyE+cwKhhgy+abqSc/pIEZd+9aavFTn/3al7upyFHzteKx+QiQH uBP5GgMp+ivbeGjqYBhjxTeaXkdNUH4wpiF2LoI= X-Received: by 2002:a17:902:dac7:b0:1ac:7e95:74be with SMTP id q7-20020a170902dac700b001ac7e9574bemr1183574plx.28.1684459171540; Thu, 18 May 2023 18:19:31 -0700 (PDT) Received: from localhost (183.43.230.35.bc.googleusercontent.com. [35.230.43.183]) by smtp.gmail.com with UTF8SMTPSA id ji17-20020a170903325100b001960706141fsm2102839plb.149.2023.05.18.18.19.31 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Thu, 18 May 2023 18:19:31 -0700 (PDT) From: jeffxu@chromium.org To: dave.hansen@intel.com, luto@kernel.org, jorgelo@chromium.org, keescook@chromium.org, groeck@chromium.org, jannh@google.com, sroettger@google.com Cc: akpm@linux-foundation.org, jeffxu@google.com, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-mm@kvack.org, linux-hardening@vger.kernel.org Subject: [PATCH v1 5/6] PKEY: Apply PKEY_ENFORCE_API to munmap Date: Fri, 19 May 2023 01:19:13 +0000 Message-ID: <20230519011915.846407-6-jeffxu@chromium.org> X-Mailer: git-send-email 2.40.1.698.g37aff9b760-goog In-Reply-To: <20230519011915.846407-1-jeffxu@chromium.org> References: <20230519011915.846407-1-jeffxu@chromium.org> MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_NONE, SPF_HELO_NONE,SPF_PASS,T_SCC_BODY_TEXT_LINE,URIBL_BLOCKED autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1766284617017624858?= X-GMAIL-MSGID: =?utf-8?q?1766284617017624858?= From: Jeff Xu This patch enables PKEY_ENFORCE_API for the munmap syscall. Signed-off-by: Jeff Xu --- include/linux/mm.h | 8 +++++++- mm/mmap.c | 31 +++++++++++++++++++++++-------- mm/mremap.c | 6 ++++-- 3 files changed, 34 insertions(+), 11 deletions(-) diff --git a/include/linux/mm.h b/include/linux/mm.h index 27ce77080c79..795cdeee2ea7 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -429,6 +429,12 @@ extern unsigned int kobjsize(const void *objp); #endif #define VM_FLAGS_CLEAR (ARCH_VM_PKEY_FLAGS | VM_ARCH_CLEAR) +/* The current call stack is originated from user space or kernel */ +enum caller_origin { + ON_BEHALF_OF_KERNEL = 0, + ON_BEHALF_OF_USERSPACE, +}; + /* * mapping from the currently active vm_flags protection bits (the * low four bits) to a page protection mask.. @@ -3136,7 +3142,7 @@ extern unsigned long do_mmap(struct file *file, unsigned long addr, unsigned long pgoff, unsigned long *populate, struct list_head *uf); extern int do_vmi_munmap(struct vma_iterator *vmi, struct mm_struct *mm, unsigned long start, size_t len, struct list_head *uf, - bool downgrade); + bool downgrade, enum caller_origin called); extern int do_munmap(struct mm_struct *, unsigned long, size_t, struct list_head *uf); extern int do_madvise(struct mm_struct *mm, unsigned long start, size_t len_in, int behavior); diff --git a/mm/mmap.c b/mm/mmap.c index 13678edaa22c..3de95a6a4397 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -2498,6 +2498,7 @@ do_vmi_align_munmap(struct vma_iterator *vmi, struct vm_area_struct *vma, * @uf: The userfaultfd list_head * @downgrade: set to true if the user wants to attempt to write_downgrade the * mmap_lock + * @called: originated from user space or kernel * * This function takes a @mas that is either pointing to the previous VMA or set * to MA_START and sets it up to remove the mapping(s). The @len will be @@ -2507,7 +2508,7 @@ do_vmi_align_munmap(struct vma_iterator *vmi, struct vm_area_struct *vma, */ int do_vmi_munmap(struct vma_iterator *vmi, struct mm_struct *mm, unsigned long start, size_t len, struct list_head *uf, - bool downgrade) + bool downgrade, enum caller_origin called) { unsigned long end; struct vm_area_struct *vma; @@ -2519,6 +2520,15 @@ int do_vmi_munmap(struct vma_iterator *vmi, struct mm_struct *mm, if (end == start) return -EINVAL; + /* + * When called by syscall from userspace, check if the calling + * thread has the PKEY permission to modify the memory mapping. + */ + if (called == ON_BEHALF_OF_USERSPACE && + arch_check_pkey_enforce_api(mm, start, end) < 0) { + return -EACCES; + } + /* arch_unmap() might do unmaps itself. */ arch_unmap(mm, start, end); @@ -2541,7 +2551,7 @@ int do_munmap(struct mm_struct *mm, unsigned long start, size_t len, { VMA_ITERATOR(vmi, mm, start); - return do_vmi_munmap(&vmi, mm, start, len, uf, false); + return do_vmi_munmap(&vmi, mm, start, len, uf, false, ON_BEHALF_OF_KERNEL); } unsigned long mmap_region(struct file *file, unsigned long addr, @@ -2575,7 +2585,8 @@ unsigned long mmap_region(struct file *file, unsigned long addr, } /* Unmap any existing mapping in the area */ - if (do_vmi_munmap(&vmi, mm, addr, len, uf, false)) + if (do_vmi_munmap(&vmi, mm, addr, len, uf, false, + ON_BEHALF_OF_KERNEL)) return -ENOMEM; /* @@ -2792,7 +2803,11 @@ unsigned long mmap_region(struct file *file, unsigned long addr, return error; } -static int __vm_munmap(unsigned long start, size_t len, bool downgrade) +/* + * @called: originated from user space or kernel + */ +static int __vm_munmap(unsigned long start, size_t len, bool downgrade, + enum caller_origin called) { int ret; struct mm_struct *mm = current->mm; @@ -2802,7 +2817,7 @@ static int __vm_munmap(unsigned long start, size_t len, bool downgrade) if (mmap_write_lock_killable(mm)) return -EINTR; - ret = do_vmi_munmap(&vmi, mm, start, len, &uf, downgrade); + ret = do_vmi_munmap(&vmi, mm, start, len, &uf, downgrade, called); /* * Returning 1 indicates mmap_lock is downgraded. * But 1 is not legal return value of vm_munmap() and munmap(), reset @@ -2820,14 +2835,14 @@ static int __vm_munmap(unsigned long start, size_t len, bool downgrade) int vm_munmap(unsigned long start, size_t len) { - return __vm_munmap(start, len, false); + return __vm_munmap(start, len, false, ON_BEHALF_OF_KERNEL); } EXPORT_SYMBOL(vm_munmap); SYSCALL_DEFINE2(munmap, unsigned long, addr, size_t, len) { addr = untagged_addr(addr); - return __vm_munmap(addr, len, true); + return __vm_munmap(addr, len, true, ON_BEHALF_OF_USERSPACE); } @@ -3055,7 +3070,7 @@ int vm_brk_flags(unsigned long addr, unsigned long request, unsigned long flags) if (ret) goto limits_failed; - ret = do_vmi_munmap(&vmi, mm, addr, len, &uf, 0); + ret = do_vmi_munmap(&vmi, mm, addr, len, &uf, 0, ON_BEHALF_OF_KERNEL); if (ret) goto munmap_failed; diff --git a/mm/mremap.c b/mm/mremap.c index b11ce6c92099..a3f576effbb1 100644 --- a/mm/mremap.c +++ b/mm/mremap.c @@ -703,7 +703,8 @@ static unsigned long move_vma(struct vm_area_struct *vma, } vma_iter_init(&vmi, mm, old_addr); - if (do_vmi_munmap(&vmi, mm, old_addr, old_len, uf_unmap, false) < 0) { + if (do_vmi_munmap(&vmi, mm, old_addr, old_len, uf_unmap, false, + ON_BEHALF_OF_KERNEL) < 0) { /* OOM: unable to split vma, just get accounts right */ if (vm_flags & VM_ACCOUNT && !(flags & MREMAP_DONTUNMAP)) vm_acct_memory(old_len >> PAGE_SHIFT); @@ -993,7 +994,8 @@ SYSCALL_DEFINE5(mremap, unsigned long, addr, unsigned long, old_len, VMA_ITERATOR(vmi, mm, addr + new_len); retval = do_vmi_munmap(&vmi, mm, addr + new_len, - old_len - new_len, &uf_unmap, true); + old_len - new_len, &uf_unmap, true, + ON_BEHALF_OF_KERNEL); /* Returning 1 indicates mmap_lock is downgraded to read. */ if (retval == 1) { downgraded = true; From patchwork Fri May 19 01:19:14 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jeff Xu X-Patchwork-Id: 96157 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:b0ea:0:b0:3b6:4342:cba0 with SMTP id b10csp911138vqo; Thu, 18 May 2023 18:22:22 -0700 (PDT) X-Google-Smtp-Source: ACHHUZ7Drs/YPJX1k40VxDS099UY+SWNiih/1GO8wByzGljVlvzJ35yoN29benYllfiwB4NPLGBm X-Received: by 2002:a17:903:1cd:b0:1ad:bccc:af78 with SMTP id e13-20020a17090301cd00b001adbcccaf78mr1134555plh.58.1684459341874; Thu, 18 May 2023 18:22:21 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1684459341; cv=none; d=google.com; s=arc-20160816; b=c3GFNlqd193Cynou4bWlVOKlwvq1XiVowH+0qpjMhhS9AqhiayV7eXVMUPB79RuCgp NK0hx9YRSK/c3jA6KbGisPSN1gtyO/9YlPwzaBWcaQMeuiTPydxxiDVWMd/rBZGtXwr8 vX1eHrRIrajYml4PsJ/yd8ohGE2VJ8DAwKlsFryUp+ONygKxfy+JbqR1bdfz5Bp6izUj 1Xpbr3MBVUIzn6bwfz0EuTleYp1t/qBO2FIUnTwuyRu0ww7aulev27ajzjCiAiMrOmv5 4IvaI1h7nTX0wWWthM3zB0Y87vF+rbHiyJT3YVJ5OFbW35SIchRiTxyn1mg0teERehk8 zgmg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=oD0d2al7XFldRkjYP37ZWtTdhmPaRGV8lU2SKIN3qFc=; b=zEHVxWn541zeey9RMSjKAuSFpD+m0Hyk9SjGGI5dbO0PlmR6MHhNmNyZgZJCf8IRqK 2hqFFjIsTm3dzU9PmwoUnTzsksJ3Mx3Lf29+RXQpGzcklDRZbq5OMSnRXU7s+MS8Rvoi jHeGTe+XOJquaJ6adWAysPGi/HUR+BiRdDHOSutzYdEOYj9PBEe0mMRUW19MW+MdRvw7 Tre6knCGA7c4K6BYvG3Mbi9bwbHskKFJ9yzJjPsha+r4FJCswQ0TA68tqrupUWaidMtH GLG7VLF8iK0Xe0R+YhLH2KAfw52VruvpZY1FEMbW40FTQIjhq/K5XmMHFj+NsdofgPW6 PPkQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@chromium.org header.s=google header.b=jyj0rZJ2; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=chromium.org Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id jj9-20020a170903048900b001a63a7b7025si2540347plb.30.2023.05.18.18.22.09; Thu, 18 May 2023 18:22:21 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@chromium.org header.s=google header.b=jyj0rZJ2; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=chromium.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229733AbjESBTu (ORCPT + 99 others); Thu, 18 May 2023 21:19:50 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:53578 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S230486AbjESBTf (ORCPT ); Thu, 18 May 2023 21:19:35 -0400 Received: from mail-pf1-x432.google.com (mail-pf1-x432.google.com [IPv6:2607:f8b0:4864:20::432]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id F060310E6 for ; Thu, 18 May 2023 18:19:32 -0700 (PDT) Received: by mail-pf1-x432.google.com with SMTP id d2e1a72fcca58-64d1e96c082so939575b3a.1 for ; Thu, 18 May 2023 18:19:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; t=1684459172; x=1687051172; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=oD0d2al7XFldRkjYP37ZWtTdhmPaRGV8lU2SKIN3qFc=; b=jyj0rZJ2+kbh8T1zO6wM8DlV8sGh/w75MLMPMO9zUvD75OwqU1+pvdIyqLBxBdrY3a rXB8rBUenQPxYI61rjx4jyVBrs9t9RT0RsQbQNuzyJdowe7fQEFAAgCJnY1Fu+bAdVH6 13412jqIvJM2IWcUlxkkmtmlW5xdC2AjmAUT8= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1684459172; x=1687051172; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=oD0d2al7XFldRkjYP37ZWtTdhmPaRGV8lU2SKIN3qFc=; b=lZQ8LnlORTWI/d7wx+551Ue/U/dsFFQ+VDVHE9jDINeFY5KOcUpIZfzc6226B/oWx2 3G0Gq/XA0pe2D6C4/uWlFDo/zdlJ6uCux56U4Ep7YynGOac/7TsMh3RBlDCl5K0bfPvi /lS1+MJaD48ezhY3Z5hpDWBtQGIFSYpfgc4VhpcyGNhoB12g7znrsM76KeJzgAXpRz1h K9vBiMGQ7ewO0QziOyVne3ijHtCZXizM5sh80CuLQhnfsKHZFzbpGN8+Sbboxypd1Rf1 eScGks8p9S03llvHmH8qjQS1M87/ptM1xK3abVEPq02DdhKrS7ZfDRsj1Y/EtXJ4KwKW Bf+Q== X-Gm-Message-State: AC+VfDwftgZ85ao5OVA/v7AA4n0btYjTo0pdEIj7AQ6DNj/OFxvlGm7l I2eEQDtcr3mRC7lDow4wzYz8hg== X-Received: by 2002:a05:6a00:a91:b0:64a:fa71:a98f with SMTP id b17-20020a056a000a9100b0064afa71a98fmr926851pfl.13.1684459172511; Thu, 18 May 2023 18:19:32 -0700 (PDT) Received: from localhost (183.43.230.35.bc.googleusercontent.com. [35.230.43.183]) by smtp.gmail.com with UTF8SMTPSA id p15-20020a63c14f000000b00528db73ed70sm1870456pgi.3.2023.05.18.18.19.31 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Thu, 18 May 2023 18:19:32 -0700 (PDT) From: jeffxu@chromium.org To: dave.hansen@intel.com, luto@kernel.org, jorgelo@chromium.org, keescook@chromium.org, groeck@chromium.org, jannh@google.com, sroettger@google.com Cc: akpm@linux-foundation.org, jeffxu@google.com, linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org, linux-mm@kvack.org, linux-hardening@vger.kernel.org Subject: [PATCH v1 6/6] PKEY:selftest pkey_enforce_api for munmap Date: Fri, 19 May 2023 01:19:14 +0000 Message-ID: <20230519011915.846407-7-jeffxu@chromium.org> X-Mailer: git-send-email 2.40.1.698.g37aff9b760-goog In-Reply-To: <20230519011915.846407-1-jeffxu@chromium.org> References: <20230519011915.846407-1-jeffxu@chromium.org> MIME-Version: 1.0 X-Spam-Status: No, score=-2.1 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_NONE, SPF_HELO_NONE,SPF_PASS,T_SCC_BODY_TEXT_LINE,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1766283639141438416?= X-GMAIL-MSGID: =?utf-8?q?1766283639141438416?= From: Jeff Xu Add selftest for pkey_enforce_api for mprotect Signed-off-by: Jeff Xu --- tools/testing/selftests/mm/pkey_enforce_api.c | 437 ++++++++++++++++++ 1 file changed, 437 insertions(+) diff --git a/tools/testing/selftests/mm/pkey_enforce_api.c b/tools/testing/selftests/mm/pkey_enforce_api.c index 23663c89bc9c..92aa29248e1f 100644 --- a/tools/testing/selftests/mm/pkey_enforce_api.c +++ b/tools/testing/selftests/mm/pkey_enforce_api.c @@ -833,6 +833,429 @@ void test_mprotect_child_thread(bool enforce) clean_single_address_with_pkey(pkey, ptr, size); } +// mmap one address with one page. +// assign PKEY to the address. +// munmap on the address is protected. +void test_munmap_single_address(bool enforce) +{ + int pkey; + int ret; + void *ptr; + int size = PAGE_SIZE; + + LOG_TEST_ENTER(enforce); + + setup_single_address_with_pkey(enforce, size, &pkey, &ptr); + + // disable write access. + pkey_write_deny(pkey); + + ret = munmap(ptr, size); + if (enforce) + assert(ret < 0); + else + assert(!ret); + + pkey_write_allow(pkey); + + if (enforce) { + ret = munmap(ptr, size); + assert(!ret); + } + + clean_single_address_with_pkey(pkey, ptr, size); +} + +// mmap two address (continuous two pages). +// assign PKEY to them with one mprotect_pkey call (merged address). +// munmap two address in one call (merged address). +void test_munmap_two_address_merge(bool enforce) +{ + int pkey; + int ret; + void *ptr; + void *ptr2; + int size = PAGE_SIZE; + + LOG_TEST_ENTER(enforce); + + setup_two_continues_fixed_address_with_pkey(enforce, size, &pkey, &ptr, + &ptr2); + + // disable write. + pkey_write_deny(pkey); + + // munmap on both addresses with one call (merged). + ret = munmap(ptr, size * 2); + if (enforce) + assert(ret < 0); + else + assert(!ret); + + pkey_write_allow(pkey); + + if (enforce) { + ret = munmap(ptr, size * 2); + assert(!ret); + } + + ret = sys_pkey_free(pkey); + assert(ret == 0); +} + +// mmap two address (continuous two pages). +// assign PKEY to the second address. +// munmap on the second address is protected. +void test_munmap_two_address_deny_second(bool enforce) +{ + int pkey; + int ret; + void *ptr; + void *ptr2; + int size = PAGE_SIZE; + + LOG_TEST_ENTER(enforce); + + setup_two_continues_fixed_address_protect_second_with_pkey( + enforce, size, &pkey, &ptr, &ptr2); + + // disable write through pkey. + pkey_write_deny(pkey); + + ret = munmap(ptr2, size); + if (enforce) + assert(ret < 0); + else + assert(!ret); + + ret = munmap(ptr, size); + assert(!ret); + + pkey_write_allow(pkey); + + if (enforce) { + ret = munmap(ptr2, size); + assert(!ret); + } + + ret = sys_pkey_free(pkey); + assert(ret == 0); +} + +// mmap two address (continuous two pages). +// assign PKEY to the second address. +// munmap on the range that includes the second address. +void test_munmap_two_address_deny_range(bool enforce) +{ + int pkey; + int ret; + void *ptr; + void *ptr2; + int size = PAGE_SIZE; + + LOG_TEST_ENTER(enforce); + + setup_two_continues_fixed_address_protect_second_with_pkey( + enforce, size, &pkey, &ptr, &ptr2); + + // disable write through pkey. + pkey_write_deny(pkey); + + ret = munmap(ptr, size * 2); + if (enforce) + assert(ret < 0); + else + assert(!ret); + + pkey_write_allow(pkey); + + if (enforce) { + ret = munmap(ptr, size * 2); + assert(!ret); + } + + ret = sys_pkey_free(pkey); + assert(ret == 0); +} + +// mmap one address with 4 pages. +// assign PKEY to the second page only. +// munmap on memory range that includes the second pages is protected. +void test_munmap_vma_middle_addr(bool enforce) +{ + int pkey; + int ret; + void *ptr, *ptr2, *ptr3; + int size = PAGE_SIZE; + + LOG_TEST_ENTER(enforce); + + setup_4pages_fixed_protect_second_page(enforce, size, &pkey, &ptr, + &ptr2, &ptr3); + + // disable write through pkey. + pkey_write_deny(pkey); + + // munmap support merge, we are going to make sure we don't regress. + ret = munmap(addr1, size * 4); + if (enforce) + assert(ret < 0); + else + assert(!ret); + + pkey_write_allow(pkey); + + if (enforce) { + ret = munmap(ptr, size * 4); + assert(!ret); + } + + ret = sys_pkey_free(pkey); + assert(ret == 0); +} + +// mmap one address with 4 pages. +// assign PKEY to the second page only. +// munmap from 2nd page. +void test_munmap_shrink(bool enforce) +{ + int pkey; + int ret; + void *ptr, *ptr2, *ptr3; + int size = PAGE_SIZE; + + LOG_TEST_ENTER(enforce); + + setup_4pages_fixed_protect_second_page(enforce, size, &pkey, &ptr, + &ptr2, &ptr3); + + // disable write through pkey. + pkey_write_deny(pkey); + + // munmap support merge, we are going to make sure we don't regress. + ret = munmap(ptr2, size * 3); + if (enforce) + assert(ret < 0); + else + assert(!ret); + + pkey_write_allow(pkey); + + if (enforce) { + ret = munmap(ptr2, size * 3); + assert(!ret); + } + + ret = munmap(ptr, size); + assert(!ret); + + ret = sys_pkey_free(pkey); + assert(ret == 0); +} + +// mmap one address with 4 pages. +// assign PKEY to the second page only. +// munmap from 2nd page but size is less than one page +void test_munmap_unaligned(bool enforce) +{ + int pkey; + int ret; + void *ptr, *ptr2, *ptr3; + int size = PAGE_SIZE; + + LOG_TEST_ENTER(enforce); + + setup_4pages_fixed_protect_second_page(enforce, size, &pkey, &ptr, + &ptr2, &ptr3); + + // disable write through pkey. + pkey_write_deny(pkey); + + // munmap support merge, we are going to make sure we don't regress. + ret = munmap(ptr2, size - 1); + if (enforce) + assert(ret < 0); + else + assert(!ret); + + pkey_write_allow(pkey); + + if (enforce) { + ret = munmap(ptr2, size - 1); + assert(!ret); + } + + ret = munmap(ptr, size * 4); + assert(!ret); + + ret = sys_pkey_free(pkey); + assert(ret == 0); +} + +// mmap one address with 4 pages. +// assign PKEY to the second page only. +// munmap from 2nd page but size is less than one page +void test_munmap_unaligned2(bool enforce) +{ + int pkey; + int ret; + void *ptr, *ptr2, *ptr3; + int size = PAGE_SIZE; + + LOG_TEST_ENTER(enforce); + + setup_4pages_fixed_protect_second_page(enforce, size, &pkey, &ptr, + &ptr2, &ptr3); + + // disable write through pkey. + pkey_write_deny(pkey); + + // munmap support merge, we are going to make sure we don't regress. + ret = munmap(ptr2, size + 1); + if (enforce) + assert(ret < 0); + else + assert(!ret); + + pkey_write_allow(pkey); + + if (enforce) { + ret = munmap(ptr2, size + 1); + assert(!ret); + } + + ret = munmap(ptr, size * 4); + assert(!ret); + + ret = sys_pkey_free(pkey); + assert(ret == 0); +} + +// mmap one address with one page. +// assign PKEY to the address. +// munmap on the address but with size of 4 pages(should OK). +void test_munmap_outbound_addr(bool enforce) +{ + int pkey; + int ret; + void *ptr; + int size = PAGE_SIZE; + + LOG_TEST_ENTER(enforce); + + setup_single_fixed_address_with_pkey(enforce, size, &pkey, &ptr); + + // disable write through pkey. + pkey_write_deny(pkey); + + // Interesting enough, this is allowed, even the other 3 pages are not allocated. + ret = munmap(addr1, size * 4); + if (enforce) + assert(ret < 0); + else + assert(!ret); + + pkey_write_allow(pkey); + + if (enforce) { + ret = munmap(addr1, size * 4); + assert(!ret); + } + + clean_single_address_with_pkey(pkey, ptr, size); +} +// mmap two addresses, with a page gap between two. +// assign pkeys on both address. +// disable access to the second address. +// munmap from start of address1 to the end of address 2, +// because there is a gap in the memory range, mprotect will fail. +void test_munmap_gapped_address_with_two_pkeys(bool enforce) +{ + int pkey, pkey2; + int ret; + void *ptr, *ptr2; + int size = PAGE_SIZE; + + LOG_TEST_ENTER(enforce); + + setup_address_with_gap_two_pkeys(enforce, size, &pkey, &pkey2, &ptr, + &ptr2); + + // disable write access. + pkey_write_deny(pkey2); + + // Interesting enough, this is allowed, even there is a gap beween address 1 and 2. + ret = munmap(addr1, size * 3); + if (enforce) + assert(ret < 0); + else + assert(!ret); + + pkey_write_allow(pkey2); + if (enforce) { + ret = munmap(addr1, size * 3); + assert(!ret); + } +} + +// use write-deny pkey and see if program can exit properly. +// This is manual test, run it at end if needed. +void test_exit_munmap_disable_write(void) +{ + int pkey; + int ret; + void *ptr; + int size = PAGE_SIZE; + + pkey = sys_pkey_alloc(PKEY_ENFORCE_API, 0); + assert(pkey > 0); + + // allocate 1 page. + ptr = mmap(addr1, size, PROT_READ, + MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0); + assert(ptr == addr1); + + // assign pkey to the first address. + ret = sys_mprotect_pkey(ptr, size, PROT_READ | PROT_WRITE | PROT_EXEC, + pkey); + assert(!ret); + + // disable write through pkey. + pkey_write_deny(pkey); + + ret = munmap(ptr, size); + assert(ret < 0); +} + +// use disable-all pkey and see if program can exit properly. +// This is manual test, run it at end if needed. +void test_exit_munmap_disable_all(void) +{ + int pkey; + int ret; + void *ptr; + int size = PAGE_SIZE; + + pkey = sys_pkey_alloc(PKEY_ENFORCE_API, 0); + assert(pkey > 0); + + // allocate 1 page. + ptr = mmap(addr2, size, PROT_READ, + MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0); + assert(ptr == addr2); + + // assign pkey to the first address. + ret = sys_mprotect_pkey(ptr, size, PROT_READ | PROT_WRITE | PROT_EXEC, + pkey); + assert(!ret); + + // disable write through pkey. + pkey_access_deny(pkey); + + ret = munmap(addr1, size); + assert(ret < 0); +} + void test_enforce_api(void) { for (int i = 0; i < 2; i++) { @@ -848,7 +1271,21 @@ void test_enforce_api(void) test_mprotect_unaligned2(enforce); test_mprotect_child_thread(enforce); test_mprotect_gapped_address_with_two_pkeys(enforce); + + test_munmap_single_address(enforce); + test_munmap_two_address_merge(enforce); + test_munmap_two_address_deny_second(enforce); + test_munmap_two_address_deny_range(enforce); + test_munmap_vma_middle_addr(enforce); + test_munmap_outbound_addr(enforce); + test_munmap_shrink(enforce); + test_munmap_unaligned(enforce); + test_munmap_unaligned2(enforce); + test_munmap_gapped_address_with_two_pkeys(enforce); } + + test_exit_munmap_disable_write(); + test_exit_munmap_disable_all(); } int main(void)