From patchwork Sat Sep 28 18:00:44 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Shu Han X-Patchwork-Id: 13814739 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from kanga.kvack.org (kanga.kvack.org [205.233.56.17]) by smtp.lore.kernel.org (Postfix) with ESMTP id 767CCCF6493 for ; Sat, 28 Sep 2024 18:01:02 +0000 (UTC) Received: by kanga.kvack.org (Postfix) id B77176B0250; Sat, 28 Sep 2024 14:01:01 -0400 (EDT) Received: by kanga.kvack.org (Postfix, from userid 40) id AFFE16B0251; Sat, 28 Sep 2024 14:01:01 -0400 (EDT) X-Delivered-To: int-list-linux-mm@kvack.org Received: by kanga.kvack.org (Postfix, from userid 63042) id 9794B6B0252; Sat, 28 Sep 2024 14:01:01 -0400 (EDT) X-Delivered-To: linux-mm@kvack.org Received: from relay.hostedemail.com (smtprelay0010.hostedemail.com [216.40.44.10]) by kanga.kvack.org (Postfix) with ESMTP id 6FD2E6B0250 for ; Sat, 28 Sep 2024 14:01:01 -0400 (EDT) Received: from smtpin11.hostedemail.com (a10.router.float.18 [10.200.18.1]) by unirelay09.hostedemail.com (Postfix) with ESMTP id E409C813FD for ; Sat, 28 Sep 2024 18:01:00 +0000 (UTC) X-FDA: 82614913080.11.5B846A3 Received: from mail-pg1-f176.google.com (mail-pg1-f176.google.com [209.85.215.176]) by imf03.hostedemail.com (Postfix) with ESMTP id 12E0A2001A for ; Sat, 28 Sep 2024 18:00:58 +0000 (UTC) Authentication-Results: imf03.hostedemail.com; dkim=pass header.d=gmail.com header.s=20230601 header.b=ikQDVGXK; spf=pass (imf03.hostedemail.com: domain of ebpqwerty472123@gmail.com designates 209.85.215.176 as permitted sender) smtp.mailfrom=ebpqwerty472123@gmail.com; dmarc=pass (policy=none) header.from=gmail.com ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=hostedemail.com; s=arc-20220608; t=1727546294; h=from:from:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-transfer-encoding:content-transfer-encoding: in-reply-to:references:dkim-signature; bh=U2+GsiuIKpICD0jp4J/MwRwjOL4N458aVUdezvXmH88=; b=CmP7eM6nfu/sd9jHIaTcwHUg9xwrgBGe+TrHfLHKI3Bzvl/cJ7X5wzMwVyhSUJdkBaJZWn +i8iE45ACok6nErig8kds79CqwvaWcvMe+pm1628hdn3BH0TWQLb58XM95TRkwKaC8zwhE CMP+poQoKmfcewiPjm9L7ta1jgrXQSU= ARC-Authentication-Results: i=1; imf03.hostedemail.com; dkim=pass header.d=gmail.com header.s=20230601 header.b=ikQDVGXK; spf=pass (imf03.hostedemail.com: domain of ebpqwerty472123@gmail.com designates 209.85.215.176 as permitted sender) smtp.mailfrom=ebpqwerty472123@gmail.com; dmarc=pass (policy=none) header.from=gmail.com ARC-Seal: i=1; s=arc-20220608; d=hostedemail.com; t=1727546294; a=rsa-sha256; cv=none; b=nPPOJGWHZfuQrEwmEVFGsivRJECzwlC6gGFC8nmQYgdFvm+lGEUc0kgDPbSvL+1DMjJJHQ EKVBt7aVvtlPH+mlogdtZHQiDM4wcB7rHV/iiQzPoOb255v7juWZIwYgIO52jmer3tJzfx 4YfDLPdhYcdzziiipIohT6r1IJpn9ic= Received: by mail-pg1-f176.google.com with SMTP id 41be03b00d2f7-7db908c9c83so1949065a12.2 for ; Sat, 28 Sep 2024 11:00:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1727546458; x=1728151258; darn=kvack.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=U2+GsiuIKpICD0jp4J/MwRwjOL4N458aVUdezvXmH88=; b=ikQDVGXKd/T2sV1y2nL+exyc4NpryxU3rns1NjPLi5fi0i6r/Kn1311T6fv2aYJO5d mNVTrrOdW+WEsE/beDichUIl8SEltOwNKSiLgvWjef0x348Wj8jrZ4ox9A/pqTMybqp7 7ESR6jswG5CLcOI5/I7T/ecM+4/O4MsXRfspVdLdvCwJ4gWPmLgiJ36j3qYYPuDoavLu B6mN1mL/ZBF5qAiBl26NGs2Up+lPQUG6X/dyMqhRWjH0pUkWQv2su+3+S8ubKqz87IU2 VK61Oh24uHFck60rtlZ+WznJKzzpPU16RhF9UTW2o1Zyu0Wb6tO/uVirNyk2cUCJ8j+h zZag== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1727546458; x=1728151258; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=U2+GsiuIKpICD0jp4J/MwRwjOL4N458aVUdezvXmH88=; b=pScWEe9d0Bdchz6JGLYzx6TD/oZagBYeE2XBu8EWx95P9JtQ2oiRzbzlbpsXAK5Epg 53z5q9aMU1ysRho+yS2VHdnShiuAO2LN/Nc+pfELMWw0a5ZcbFcVFSZQLnrjy9KNdDIw 3HE/S03CvYHi3PdpzioW4mQ2XBAMsTJd2bWg80lc0tfT6ty2pDcfPz7rfr9IA8xg7c9e sn6q2oSoTPBwxDOsdpjFTmMJO5e68HLX48EanGORgk0oiXhh/DOrQkIOfL1hWZH2miZs FB7EbQhDwjjwWiWN/tm6vawmg+zETOv+luAeEXLsjSWc+ygRwxCW7eT/G+Wf0nQNhy1Q 5egg== X-Gm-Message-State: AOJu0YwWavNx2pgcw+yvOlxOmmB1OMpQfM2BZ9qHG2JUN1CKVlUYvm9Q LzMwH/KBa7t3NXOfymq33xQPYkx/bMzBnrxf8grIH0MMUm0Q034K X-Google-Smtp-Source: AGHT+IEVmr91INafLLcN+NX6rAWIWchNBGjB3qLEDxQMYuCAirU2pxQYk8LGqwVkkAqHmGlM/Yd82w== X-Received: by 2002:a05:6a21:710a:b0:1cf:51a1:8e89 with SMTP id adf61e73a8af0-1d4fa815ec4mr10490782637.47.1727546457353; Sat, 28 Sep 2024 11:00:57 -0700 (PDT) Received: from localhost.localdomain ([20.37.103.148]) by smtp.gmail.com with ESMTPSA id 41be03b00d2f7-7e6db605029sm3513516a12.81.2024.09.28.11.00.53 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 28 Sep 2024 11:00:56 -0700 (PDT) From: Shu Han To: akpm@linux-foundation.org, paul@paul-moore.com, jmorris@namei.org, serge@hallyn.com, Liam.Howlett@oracle.com, vbabka@suse.cz, lorenzo.stoakes@oracle.com Cc: linux-mm@kvack.org, linux-kernel@vger.kernel.org, linux-security-module@vger.kernel.org, syzbot+1cd571a672400ef3a930@syzkaller.appspotmail.com Subject: [RFC PATCH v4] mm: move the check of READ_IMPLIES_EXEC out of do_mmap() Date: Sun, 29 Sep 2024 02:00:44 +0800 Message-Id: <20240928180044.50-1-ebpqwerty472123@gmail.com> X-Mailer: git-send-email 2.25.1 MIME-Version: 1.0 X-Rspamd-Server: rspam06 X-Rspamd-Queue-Id: 12E0A2001A X-Stat-Signature: kxa7j7iwux6kqmxd86ct8w1zhwk4m316 X-Rspam-User: X-HE-Tag: 1727546458-270272 X-HE-Meta: U2FsdGVkX19g7zBnMti6AS11w5XzwYeIy4ow8fTx/dJnkDrT1+E3PlZhk0NpcSBcP69bBrocVrqHz6QExZCw+enAXI7GNkG4FCOJIBg6yC/gwDw7Zvl5AOaVMFNWGYLCL9vUmFGrZfiLSWgJAnPMD/HAcjt5ja+QPq6htKGD1pNeywpiNfA4TVekjXhFd0Ap05MqV1JxIVX2uyi7s46Q7xpxVKvhyFRzIDkOG7XP11y1ys7zOeSy/QVsAIsdoWRwfvh30GChCIXmQqlcG32abFp2zg2bxdcLhRtsUt6UqhAo8S8XCwVMaRGdiVhtQhtsJo8NwAxLZcLQPkUCo8VSSZXON3CR0f/F2HBCDE0jDcrqwp53ZHCUJEsWWWKHWB9ugKGqr13oZfQEjr1g/llV/PDi8xiovrqSWMjVhb1xNBluhQpAHn5Y1iU27USYbW2ZeQvEhba6ytTcndnwyV0RpuQhkEzIevrmTBvwrWhMBqsD7lIlyt/HaZbwwx4QZG0JnvTOLzWD5PnZ2F6oOafrDbwV85uoef5lCbMGmp2N1an1ztSebV9mDpYav2TC9GHpMgtzaqve64raCYgWKe3JTBvF6Jz0Vs7gLVGmVHXDMeQ7jhQuaTFFXkFdT75rWlkkHLWJV3ywa9jTRgNxqewaXnK4AbEfgnPps8ViXgC5NS8bIbDGAl7h5+Kw2RAETHHHuASmWuuZfn9kLu0YgxJn1ig8FlFasmIV9NQkmzx630miEY3T1MY63DkzIe+WVevdoridfcW3RyR3TovsowZAuQD1Mm2qlTBHWT04ro4lBF8rf72NTk9QQjOOdSm7lXGBM5fO3dE1ztLZ11PRLX08dPezD/rUkG6dOCD9X1wzhqpeCjHYI+CcgTpMtGDY9kEKib9uaa2htuGd3VhxPwjdL3Fhu1sUt+YBo76CCYD+cx/M+A18ARfOF+bNHyoVnZrIMkw3tqPgxzPRiKrRg3j ZBsih1hp 6RtVdQNhUvLsBK1Xtw18CoCpV98geS4k3Z8aS8xANAoe/EdOMHKkiDUu1bjsXk105+cS3b+9F+Um5iFKCnGu/GVh66aokkGmA72pNP7eV8LzuGOuPVFi3GxJWjcz52U9dnVBpvZWmWdinBFLw92C6feF0f5D0g0D8iq/Zm6Tg3rCz7VymJiLZnRMFIDY84tmEC1Xfsc6rh4qmxET4RcbGBmRgFruysjDxFZZZFXdxmde78/3+/LnBStJtUa67wkcd4/wXGz1Y0/EFtr1Jx/D7uxLZAVLVMze3XaYrgZ3Jta2vFYWH/nxoPt0LP2qjbMm5X6MtPThAMFRwZ6WEaoygTNNVPw4a05BJg/3ww7kjIiwVyd6/JroBSss4kb/+tAFF2htiy3I0gqkXZCKEK25sH2Mv5POqepgL+dOHzqxmvKw4O7GOj4nXLxlBhDjkct/XIcSYDJxakXT5lJffKjvSb+KfxnV6GXjS9mfhxzOXnqKpV6uxFUqbT1KH6IicKzL/mCoBRN76DvLKhUizhSUFDEEeHD5eaEf2FdWnAOGFPncZO4Q0Omcj9l932Ly/hzCrMmTUPOoV6CrpxFPYSTEiwxKggLMC/YbN25Y7MeRPZ0roYb5IEx9XjCSE3bT/001hkvVj/bXipHzgyf6iIgpfYqv3kxPvlGJdenOSx0Z52omprkTKh99YNSrvrdYdYtJEOKi2ko8rzrCp+ONbK3BA6TpB4gPxZtGHWqQ9 X-Bogosity: Ham, tests=bogofilter, spamicity=0.000000, version=1.2.4 Sender: owner-linux-mm@kvack.org Precedence: bulk X-Loop: owner-majordomo@kvack.org List-ID: List-Subscribe: List-Unsubscribe: This patch moves the check of READ_IMPLIES_EXEC out of do_mmap(), and calls the LSM hooks at the same time. Below is the reason. This patch is related to: 1. the personality flag READ_IMPLIES_EXEC, which can be changed in userspace and will add PROT_EXEC to prot in do_mmap() when it is set and some other conditions are met. 2. the LSM hook security_mmap_file, which requires the prot before and after 1. For now, this is the mmap() logic that actually executes (only the part we care): 1. call security_mmap_file() 1.1. calculate the final prot in mmap_prot() 1.2. call hooks by the original prot and the final prot 2. lock mmap_write_lock 3. call do_mmap() 3.1. calculate the final prot again according to the personality flag READ_IMPLIES_EXEC and other conditions 3.2. do the actual memory map with the final prot 4. unlock mmap_write_lock There are 2 problems with them: 1. The final prot is calculated twice, which is redundant. And currently, the !MMU case won't imply exec if the file's mmap_capabilities do not exist in step 3.1, but step 1.1 is different. 2. Some other call sites call do_mmap() without step 1, which is still secure if there is no step 3.1 since they map an anonymous page or a private file without PROT_EXEC, which is not cared for by LSM. So, it is implied that it is call sites' duty to do step 1.1 or let step 3.1 never take effect(let other conditions not be met, such as marking the mapped file as NO_EXEC). Especially the implicit duty in problem 2 is rarely noticed by the caller, which leads to some security issues(in aio[1] and remap_file_pages[2]). It is believed that the future call sites to do_mmap() is easily to harm the security if we don't take measures. The measures in this patch are moving step 3.1 out of do_mmap(), into the new function mmap_check_prot(), and calling the LSM hooks in the same function. The function is out of the mmap_write_lock. The measures remove the repeated logic in step 1.1 and let further call sites have 2 choices: 1. call mmap_check_prot() before calling do_mmap(), which is equal to the current steps 1-4. 2. call do_mmap() directly, which is still secure since there is no step 3.1 in do_mmap() anymore. The potential harm that remains without precedent is a new caller calls do_mmap() with a non-private file or have both PROT_WRITE|PROT_EXEC flags, which is mentioned in the comment of do_mmap(). mmap_region() is in the same state. The measures have a minor behavior change, which is: If the call site selects choice 2 and the conditions for READ_IMPLIES_EXEC are met, there will be no PROT_EXEC automatically added anymore. This is all call sites currently and why they should clearly be fine: 1. mm/util.c: choice 1. 2. ipc/shm.c: choice 1. 3. fs/aio.c: the conditions for READ_IMPLIES_EXEC will never be met since the patch for [1] adds the noexec flag. 4. arch/x86/kernel/shstk.c: must not be intended to have PROT_EXEC, since shadow stack is a stack that only store return addresses, executing it is never required. 5. mm/mmap.c: in the history, remap_file_pages won't change prot, which is changed in the emulation version after the deprecated mark exists. the patch only revert the side effect introduced in the emulation version. Solving the problem 2 in [2] by this way can remove the check introduced in the original patch(done in this patch) is also solving the deadlock mentioned in [3]. Link: https://project-zero.issues.chromium.org/issues/42452389 [1] Link: https://lore.kernel.org/all/20240919080905.4506-2-paul@paul-moore.com/ [2] Link: https://lore.kernel.org/linux-mm/66f7b10e.050a0220.46d20.0036.GAE@google.com/ [3] Reported-by: syzbot+1cd571a672400ef3a930@syzkaller.appspotmail.com Closes: https://lore.kernel.org/linux-mm/66f7b10e.050a0220.46d20.0036.GAE@google.com/ Fixes: ea7e2d5e49c0 ("mm: call the security_mmap_file() LSM hook in remap_file_pages()") Signed-off-by: Shu Han --- May require a better name for mmap_check_prot(). v4: Take mmap_region into consideration, which is found by lorenzo.stoakes@oracle.com, and refine the comment in the patch, and add the deadlock reason. v3: Send only one version as lorenzo.stoakes@oracle.com suggested and rewrite the description more clearly. https://lore.kernel.org/all/20240925145249.50-1-ebpqwerty472123@gmail.com/ v2: Add RFC tag as lorenzo.stoakes@oracle.com suggested, and refine the comment in the patch. https://lore.kernel.org/all/20240925120940.309-1-ebpqwerty472123@gmail.com/ v1: The original. https://lore.kernel.org/all/20240925063034.169-1-ebpqwerty472123@gmail.com/ Alternatives mentioned in v3 may not worked well, but this patch is still: 1. Add sufficient comments for do_mmap() without code changes. This method remains problem 1 unsolved. Hard to solve the remap_file_pages() issue properly. 2. Move security_mmap_file() back into do_mmap(). i.e. revert 8b3ec6814c83("take security_mmap_file() outside of ->mmap_sem") which may also cause the deadlock mentioned in [3]. --- include/linux/mm.h | 2 ++ include/linux/security.h | 8 +++---- ipc/shm.c | 2 +- mm/mmap.c | 51 +++++++++++++++++++++++++++++----------- mm/nommu.c | 49 +++++++++++++++++++++++++++++--------- mm/util.c | 2 +- security/security.c | 41 ++++---------------------------- 7 files changed, 87 insertions(+), 68 deletions(-) base-commit: f89722faa31466ff41aed21bdeb9cf34c2312858 diff --git a/include/linux/mm.h b/include/linux/mm.h index c4b238a20b76..83f334590b06 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -3392,6 +3392,8 @@ get_unmapped_area(struct file *file, unsigned long addr, unsigned long len, extern unsigned long mmap_region(struct file *file, unsigned long addr, unsigned long len, vm_flags_t vm_flags, unsigned long pgoff, struct list_head *uf); +extern int mmap_check_prot(struct file *file, unsigned long *prot, + unsigned long flags); extern unsigned long do_mmap(struct file *file, unsigned long addr, unsigned long len, unsigned long prot, unsigned long flags, vm_flags_t vm_flags, unsigned long pgoff, unsigned long *populate, diff --git a/include/linux/security.h b/include/linux/security.h index c37c32ebbdcd..e061bc9a0331 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -423,8 +423,8 @@ void security_file_free(struct file *file); int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg); int security_file_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg); -int security_mmap_file(struct file *file, unsigned long prot, - unsigned long flags); +int security_mmap_file(struct file *file, unsigned long reqprot, + unsigned long prot, unsigned long flags); int security_mmap_addr(unsigned long addr); int security_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot, unsigned long prot); @@ -1077,8 +1077,8 @@ static inline int security_file_ioctl_compat(struct file *file, return 0; } -static inline int security_mmap_file(struct file *file, unsigned long prot, - unsigned long flags) +static inline int security_mmap_file(struct file *file, unsigned long reqprot, + unsigned long prot, unsigned long flags) { return 0; } diff --git a/ipc/shm.c b/ipc/shm.c index 3e3071252dac..f1095ee3796d 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -1636,7 +1636,7 @@ long do_shmat(int shmid, char __user *shmaddr, int shmflg, sfd->vm_ops = NULL; file->private_data = sfd; - err = security_mmap_file(file, prot, flags); + err = mmap_check_prot(file, &prot, flags); if (err) goto out_fput; diff --git a/mm/mmap.c b/mm/mmap.c index 18fddcce03b8..3722c7f38a5a 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -1249,8 +1249,39 @@ static inline bool file_mmap_ok(struct file *file, struct inode *inode, return true; } +int mmap_check_prot(struct file *file, unsigned long *prot, + unsigned long flags) +{ + unsigned long req_prot = *prot; + unsigned long new_prot = req_prot; + int err; + + /* + * Does the application expect PROT_READ to imply PROT_EXEC? + * + * (the exception is when the underlying filesystem is noexec + * mounted, in which case we don't add PROT_EXEC.) + */ + if (((req_prot & (PROT_READ | PROT_EXEC)) == PROT_READ) && + (current->personality & READ_IMPLIES_EXEC) && + !(file && path_noexec(&file->f_path))) + new_prot |= PROT_EXEC; + + err = security_mmap_file(file, req_prot, new_prot, flags); + if (err) + return err; + + *prot = new_prot; + return 0; +} + /* * The caller must write-lock current->mm->mmap_lock. + * The caller must consider to call mmap_check_prot or + * security_mmap_file before if + * (file && !IS_PRIVATE(file_inode(file)) || ( + * ((prot & PROT_EXEC) || (vm_flags & VM_EXEC)) && + * ((prot & PROT_WRITE) || (vm_flags & VM_WRITE)))). */ unsigned long do_mmap(struct file *file, unsigned long addr, unsigned long len, unsigned long prot, @@ -1266,16 +1297,6 @@ unsigned long do_mmap(struct file *file, unsigned long addr, if (!len) return -EINVAL; - /* - * Does the application expect PROT_READ to imply PROT_EXEC? - * - * (the exception is when the underlying filesystem is noexec - * mounted, in which case we don't add PROT_EXEC.) - */ - if ((prot & PROT_READ) && (current->personality & READ_IMPLIES_EXEC)) - if (!(file && path_noexec(&file->f_path))) - prot |= PROT_EXEC; - /* force arch specific MAP_FIXED handling in get_unmapped_area */ if (flags & MAP_FIXED_NOREPLACE) flags |= MAP_FIXED; @@ -2846,6 +2867,12 @@ int do_munmap(struct mm_struct *mm, unsigned long start, size_t len, return do_vmi_munmap(&vmi, mm, start, len, uf, false); } +/* + * The caller must write-lock current->mm->mmap_lock. + * The caller must consider to call security_mmap_file before if + * ((file && !IS_PRIVATE(file_inode(file))) || + * (vm_flags & VM_EXEC) && (vm_flags & VM_WRITE)). + */ unsigned long mmap_region(struct file *file, unsigned long addr, unsigned long len, vm_flags_t vm_flags, unsigned long pgoff, struct list_head *uf) @@ -3198,12 +3225,8 @@ SYSCALL_DEFINE5(remap_file_pages, unsigned long, start, unsigned long, size, flags |= MAP_LOCKED; file = get_file(vma->vm_file); - ret = security_mmap_file(vma->vm_file, prot, flags); - if (ret) - goto out_fput; ret = do_mmap(vma->vm_file, start, size, prot, flags, 0, pgoff, &populate, NULL); -out_fput: fput(file); out: mmap_write_unlock(mm); diff --git a/mm/nommu.c b/mm/nommu.c index 7296e775e04e..96761add1295 100644 --- a/mm/nommu.c +++ b/mm/nommu.c @@ -792,12 +792,6 @@ static int validate_mmap_request(struct file *file, if (path_noexec(&file->f_path)) { if (prot & PROT_EXEC) return -EPERM; - } else if ((prot & PROT_READ) && !(prot & PROT_EXEC)) { - /* handle implication of PROT_EXEC by PROT_READ */ - if (current->personality & READ_IMPLIES_EXEC) { - if (capabilities & NOMMU_MAP_EXEC) - prot |= PROT_EXEC; - } } else if ((prot & PROT_READ) && (prot & PROT_EXEC) && !(capabilities & NOMMU_MAP_EXEC) @@ -810,11 +804,6 @@ static int validate_mmap_request(struct file *file, * privately mapped */ capabilities = NOMMU_MAP_COPY; - - /* handle PROT_EXEC implication by PROT_READ */ - if ((prot & PROT_READ) && - (current->personality & READ_IMPLIES_EXEC)) - prot |= PROT_EXEC; } /* allow the security API to have its say */ @@ -992,6 +981,44 @@ static int do_mmap_private(struct vm_area_struct *vma, return -ENOMEM; } +int mmap_check_prot(struct file *file, unsigned long *prot, + unsigned long flags) +{ + unsigned long req_prot = *prot; + unsigned long new_prot = req_prot; + int err; + + /* + * Does the application expect PROT_READ to imply PROT_EXEC? + * + * (the exception is when the underlying filesystem is noexec + * mounted or the file does not have NOMMU_MAP_EXEC + * (== VM_MAYEXEC), in which case we don't add PROT_EXEC.) + */ + if ((req_prot & (PROT_READ | PROT_EXEC)) != PROT_READ) + goto check; + if (!(current->personality & READ_IMPLIES_EXEC)) + goto check; + if (!file) { + new_prot |= PROT_EXEC; + goto check; + } + if (file->f_op->mmap_capabilities) { + unsigned int caps = file->f_op->mmap_capabilities(file); + + if (!(caps & NOMMU_MAP_EXEC)) + goto check; + new_prot |= PROT_EXEC; + } +check: + err = security_mmap_file(file, req_prot, new_prot, flags); + if (err) + return err; + + *prot = new_prot; + return 0; +} + /* * handle mapping creation for uClinux */ diff --git a/mm/util.c b/mm/util.c index bd283e2132e0..2eb4d6037610 100644 --- a/mm/util.c +++ b/mm/util.c @@ -581,7 +581,7 @@ unsigned long vm_mmap_pgoff(struct file *file, unsigned long addr, unsigned long populate; LIST_HEAD(uf); - ret = security_mmap_file(file, prot, flag); + ret = mmap_check_prot(file, &prot, flag); if (!ret) { if (mmap_write_lock_killable(mm)) return -EINTR; diff --git a/security/security.c b/security/security.c index 4564a0a1e4ef..25556629f588 100644 --- a/security/security.c +++ b/security/security.c @@ -2927,42 +2927,10 @@ int security_file_ioctl_compat(struct file *file, unsigned int cmd, } EXPORT_SYMBOL_GPL(security_file_ioctl_compat); -static inline unsigned long mmap_prot(struct file *file, unsigned long prot) -{ - /* - * Does we have PROT_READ and does the application expect - * it to imply PROT_EXEC? If not, nothing to talk about... - */ - if ((prot & (PROT_READ | PROT_EXEC)) != PROT_READ) - return prot; - if (!(current->personality & READ_IMPLIES_EXEC)) - return prot; - /* - * if that's an anonymous mapping, let it. - */ - if (!file) - return prot | PROT_EXEC; - /* - * ditto if it's not on noexec mount, except that on !MMU we need - * NOMMU_MAP_EXEC (== VM_MAYEXEC) in this case - */ - if (!path_noexec(&file->f_path)) { -#ifndef CONFIG_MMU - if (file->f_op->mmap_capabilities) { - unsigned caps = file->f_op->mmap_capabilities(file); - if (!(caps & NOMMU_MAP_EXEC)) - return prot; - } -#endif - return prot | PROT_EXEC; - } - /* anything on noexec mount won't get PROT_EXEC */ - return prot; -} - /** * security_mmap_file() - Check if mmap'ing a file is allowed * @file: file + * @reqprot: protection requested by user * @prot: protection applied by the kernel * @flags: flags * @@ -2971,11 +2939,10 @@ static inline unsigned long mmap_prot(struct file *file, unsigned long prot) * * Return: Returns 0 if permission is granted. */ -int security_mmap_file(struct file *file, unsigned long prot, - unsigned long flags) +int security_mmap_file(struct file *file, unsigned long reqprot, + unsigned long prot, unsigned long flags) { - return call_int_hook(mmap_file, file, prot, mmap_prot(file, prot), - flags); + return call_int_hook(mmap_file, file, reqprot, prot, flags); } /**