From nobody Sat Sep 26 21:14:03 2026 Received: from mail-wr1-f45.google.com (mail-wr1-f45.google.com [209.85.221.45]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E39B1435502 for ; Sat, 29 Aug 2026 21:33:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.45 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788039189; cv=none; b=rAAmyo9skKg5rmcedRc7eWnLgSBN30uEe9WlPGez9/2v1BGAjmPHAFOrJ2ZMiIzClHegszBfe+r9/k/LTR5o30s+D0v3VOM4Rht7UrbCPvmzEu+EHiN3zOvDIfDh/7PRSJM5sLU/wTONs2YKBsiKpEgwxH9YwiHm24p/K/bZ+dg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788039189; c=relaxed/simple; bh=LO1QiG+tuqYXw1xkU6kH+n25tHtzPNegmiTgVWdxIs0=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=RM/49vWDY+JvBkavH5ABLq1G5sBky87ZiAK6973iPFQqVvV3Ug/LZWrEPERDDc6hN+eTzqlszGIXxDJqAKdmE+1C3g1NmCnlqIWxiA66+BEtueZaaU0sksrsXW/l/BLihmhL5f9KF8W38YE9CwpegX1g3uoHnP+QcU/oIHq6QnY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=l5pzNf96; arc=none smtp.client-ip=209.85.221.45 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="l5pzNf96" Received: by mail-wr1-f45.google.com with SMTP id ffacd0b85a97d-48436216a98so152672f8f.0 for ; Sat, 29 Aug 2026 14:33:07 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788039186; x=1788643986; darn=vger.kernel.org; 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:content-type; bh=XPdTmo/psYzNVIunLzBuzuNQ62GIR7HvuoO8VwCkzsQ=; b=l5pzNf96MTYV8/moSN0WI9ZuXaKDZa5UHbuy16V1DyGljLFlTwV1CHBrDnvAO8Qoc6 e+C1W9+hDhu6kCQH3ubu3UbQZt451RX8Ep+8ULGqms3kUVVwTBHdILpGtJiHJGnZ8pwA 35c/HQWRfteLGTcX9XtWAkMXAJeeDsJf4Y+UrLVhUm0chhLm/uLoeHpesdV8dIhJj8QB c9vj+O7T8JjfDTo050/B90yPDpAuhcXoSR1NbwafUI+pCQDpkYTqCc3vbnMuGgeJ4X7T Meekeo+ASAzUBPAG4ShXUu7sVEjvZ50qmnrspoQHEWXGwh07mTlzE68n0o0+atq/FLIK oAPQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788039186; x=1788643986; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=XPdTmo/psYzNVIunLzBuzuNQ62GIR7HvuoO8VwCkzsQ=; b=Lv+6YNgCEEVk9N9aqt51izWTbVdqDz1hgDhlV1B8iXLySRb72oiZKPJcyrDYA96/Xo JzfFE+StKDMuBnjvdRp7eskhCptvmUuNVPF5W7jzE2eKl+oPVQwVggayKA235nt4PNzS MlMhwsd1BsfzIVgLMmbCRUydiQaj7S/i1yT6eA+QrijlVNvSjFVDjUcI/TbSdBSAoLe1 h/wjoDdDNKZKTjqB2CkI3C3eTY41qfVjZASA/W3o1hZBB8COKcHrS/HPc3yzB+6ZyVHv cPIav2UJekuIWFyq6RTkDf9jzwpoWzs1Dm21/CeZmu3psitpRhhPfM23LwZ7M2tpHQJt /hDw== X-Forwarded-Encrypted: i=1; AKwUvBzCJnJ4HF1ypGkxMYZxMgky3cV8LjBq9Xr8DJVV4wkslC5T9iN93loMEx9/Zr8w27Ax6pIAKkC/YtY3oxk=@vger.kernel.org X-Gm-Message-State: AFuF++mWYjEPZQTBz/vepTZrnIzO0fo9ONpldrsql+TsMA7fvAPwOYJW PPyD52SqCcbWn7CHu2ZFx51m9NHfGwHGroyGHfK+xyRprnw2/b2/xe1U X-Gm-Gg: AYBFou3uA6LyHeJmIFwYZ6Hch+1aEZTAu2iMCB96rRZR9+3uC9ZLjTDmWu7xBc8Uq15 j0R6eXmFj+efzFE6SQ+V25MDHfR0R23mWPqPwRiIDaSr/05XJqcQvGDsCcWAQtS9QwGsgXKUJYM gzeOxdcqo0DDxu2cXOXf3ifxoED/q/1aUk8e/oBZFcbPXwKfWSHb4oavi+2c5XGxmEHrHHldJPb 7/70GVQ+0G80w9dt9s0SL3K8cuEnzw9Y9F6Fel69qH0CNZDsbeOHRQRqW6VvrYcrZayjkZgQFWj YVkyXsWKFp3q/+bsv7YwQHgdI8Dk7VRjyMYHqhCcqmxEk0PKk1HLQ2Lcz1xm4ziNiBazKCXfRZb LMpOqrl7vZcp7l15N3J+U8Q/9CKj/5m5TI4WLGig0k98mw27Bf9vQjXAtQSY9tg1ouYaUWxSyIh seFEuhFzCSlY//CyZcZBhiJ3ZhdYXl0hYGxIkP+EaNrIvmJi0xRUTZ7XvoaOlFwq+b3lnqZOtV4 dLF58OTVFg/OVS493wqQHsgGrkhdRS4/k1RFENTuMDZy+J9JwsFWn5K7owEPM/1QPd1dZAL1l6o /Iysqf/8ACZarqAjO/fu4Xi4nHvsLaVphf1kp/ikfzEm7ZFc8K3kXe8Rk01A3OCQNCA= X-Received: by 2002:a05:6000:41e9:b0:484:3621:6340 with SMTP id ffacd0b85a97d-48436216a9bmr3184979f8f.16.1788039186041; Sat, 29 Aug 2026 14:33:06 -0700 (PDT) Received: from localhost.localdomain (dynamic-2a02-3100-ae86-1b01-80e2-8851-4221-68d8.310.pool.telefonica.de. [2a02:3100:ae86:1b01:80e2:8851:4221:68d8]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-482fbb33070sm12540616f8f.36.2026.08.29.14.33.05 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Sat, 29 Aug 2026 14:33:05 -0700 (PDT) From: Karl Mehltretter To: selinux@vger.kernel.org Cc: Karl Mehltretter , Paul Moore , Stephen Smalley , Ondrej Mosnacek , Miklos Szeredi , Amir Goldstein , Christian Brauner , Baokun Li , linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, linux-kernel@vger.kernel.org, stable@vger.kernel.org Subject: [PATCH v3 1/2] selinux: preserve user SID across nested backing files Date: Sat, 29 Aug 2026 23:32:55 +0200 Message-Id: <20260829213256.51527-2-kmehltretter@gmail.com> X-Mailer: git-send-email 2.39.5 (Apple Git-154) In-Reply-To: <20260829213256.51527-1-kmehltretter@gmail.com> References: <20260829213256.51527-1-kmehltretter@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" SELinux saves the user file SID in a backing-file security blob so it remains available after mmap() replaces vma->vm_file with a backing file. For nested backing files (overlayfs over overlayfs, or FUSE passthrough backed by overlayfs), user_file may itself be a backing file. Its fsec->sid is the SID of the mounter that opened it, rather than the user that opened the top-level file. mprotect() then checks fd { use } against the mounter SID. This can incorrectly deny access without a domain transition, or check the wrong target SID after one. Copy the saved user SID when user_file is a backing file. Keep using the regular file SID for the first backing layer. With two nested overlayfs mounts and SELinux enforcing, mprotect(PROT_READ) returns EACCES with an fd { use } denial against the mounter SID. With this change, mprotect() succeeds. Tested on arm64 QEMU with a small BusyBox initramfs and a purpose-built SELinux policy. The original test was also repeated with Fedora Cloud Base 44 userspace and gave the same result. Fixes: 82544d36b172 ("selinux: fix overlayfs mmap() and mprotect() access c= hecks") Cc: Reviewed-by: Amir Goldstein Assisted-by: LLM Signed-off-by: Karl Mehltretter Reviewed-by: Stephen Smalley --- security/selinux/hooks.c | 9 ++++++++- security/selinux/include/objsec.h | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 035aaf113d1da..232b7e7bfcafd 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3843,13 +3843,20 @@ static int selinux_file_alloc_security(struct file = *file) return 0; } =20 +static inline u32 selinux_file_user_sid(const struct file *file) +{ + if (unlikely(file->f_mode & FMODE_BACKING)) + return selinux_backing_file(file)->uf_sid; + return selinux_file(file)->sid; +} + static int selinux_backing_file_alloc(struct file *backing_file, const struct file *user_file) { struct backing_file_security_struct *bfsec; =20 bfsec =3D selinux_backing_file(backing_file); - bfsec->uf_sid =3D selinux_file(user_file)->sid; + bfsec->uf_sid =3D selinux_file_user_sid(user_file); =20 return 0; } diff --git a/security/selinux/include/objsec.h b/security/selinux/include/o= bjsec.h index 3c0a16ec978b0..853f7266ed189 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h @@ -87,7 +87,7 @@ struct file_security_struct { }; =20 struct backing_file_security_struct { - u32 uf_sid; /* associated user file fsec->sid */ + u32 uf_sid; /* top-level user file fsec->sid */ }; =20 struct superblock_security_struct { --=20 2.53.0 From nobody Sat Sep 26 21:14:03 2026 Received: from mail-wr1-f42.google.com (mail-wr1-f42.google.com [209.85.221.42]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C466A43713E for ; Sat, 29 Aug 2026 21:33:09 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.42 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788039192; cv=none; b=jmEqQVWO1Mk+DiAzE3qBea94zg66MzlDDgOOwzf/5pNFc8C8G/aXb5Qch2AaHb1Cc8IUFMG/GlYqQqwhLv6I7Rfhf+tWRh4JUhrHD6IKgQkFKqV7YpVkZyEv9jul9d/wz+HRZvBfaBc1IR73BV80TD94RQRItsBaSjV6QfLoRSY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788039192; c=relaxed/simple; bh=co+2fxzxIKsrqg4QHh3FK11V9lST4YMEzCqjZ1hMMe4=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=mDB6t0b4XIIf5yyyZZwCYmE0A9Xm0CV3zIYXRAyMCWAFWw/VIvQenU4NbmV8LlCZAtqC9eV3legcQuJHm/c6HkgDozj5Dv430tdlwseJ4NILdQOIvL+c95xYfD+1+crqPkOeQIgJXuC3Wb2mMaiGYaOm5+CKsr/NDHNBE9nAUoc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=OI3OzQYj; arc=none smtp.client-ip=209.85.221.42 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="OI3OzQYj" Received: by mail-wr1-f42.google.com with SMTP id ffacd0b85a97d-482e4998d28so1546039f8f.2 for ; Sat, 29 Aug 2026 14:33:09 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1788039188; x=1788643988; darn=vger.kernel.org; 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:content-type; bh=Kgtzxw+fLI0xgDG63CMY9iJwpkHw7jAnoCmbSNosjoM=; b=OI3OzQYjMJaScY9Nrt4ilDXIt8x6p9Q4tC4RBQgBhP59cHu7C5RuBq77vzRPPpcReh EEyTwZpVEblQZcfv9Nnls1xaausUUCgGJgV6OvlrPmDIWFxOz1NO5VM9vq5U34BxcLFJ bm2XuLp0Yzxd3ZsebduwI/tZBcpxTn7HO1Bkl4w4XW5EmBNjIrkJ0xpule2yT2ltgS/D mmDaKU9KgMvhmRMx6t1UG7turKc0ODGrU0RLJ8CwZi/9ij7+qXw+Ol6/qoY+vimcwfwB 1g44DxI1L1PL3DnJ8P8BNooRQaMFEpFBNMQzzs3d19PJB5ghYxW7vsjDIYTNPhbDPndi 1GkA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1788039188; x=1788643988; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=Kgtzxw+fLI0xgDG63CMY9iJwpkHw7jAnoCmbSNosjoM=; b=VTXXOmTWlirUW62BrTm5po44iEXrK+Pt4+HeaG77zWM/fVarc6LGMV69LX/dpqVk2e sc4SDDPkH1bl9XgkUAOSBMc5Y+kRBP2+mgpENJ3ten1HB7EruK0vKeom0hEBgPslORCP CVot1KDZkzqdw1rAWc2pAEKltlfUBr5uHl6grK1748RTJWzvOA5MEglbcYYcZsJp5Fy/ 4IJHCisJmVVMq9jn9fb9h4Eri6UrqxcTeXIoVFk0zfgBId+2TF+xWPrkG+hbuhbHGOXs QWIPT4NzL911EezGSGAccQYjIQvzjKG/xUqTa7TwdbhMUpPTtV6SkXeSFgfrMuadsQhK hfNQ== X-Forwarded-Encrypted: i=1; AKwUvBwSxCAJm0N3fcvZN92Ezkqq2BhsrtREo6nTCe/T/uB1ohGx8ClB+gCmWwHvSI5WFomhc/Ut+VhR4CYfA4Q=@vger.kernel.org X-Gm-Message-State: AFuF++lPyrYCdOZPePPE2S/csEK0rTujXOjWAP2WUJH65VEOdgBUgd7p S556/vnu+8edpxVqFpc9m+JF5TIklwKg6cu+U3aH3IhX6DpGN21CnmFg X-Gm-Gg: AYBFou3DJmxrzMTvrqQd/F1TbX1Px8GYOo/fTIwIWpUd6nk/4eCDXYfmOt4QdjbilGV 9nd1lFy4FCIl4GyWDL2w8tcElXow8x/UhLWHvSmCdX066YPa/xeHPWo2UgfdaabyecCKT2HEe3G YpBU9S9Kyr07dnKBC1FBA5WU0Lteur+rFIvcVWqrdC36Aw8argg/u+YDhXGR9BAGFvjaUavvCd7 Nxib7NB/FTvEtgAwcJrEnKqW+offT5tWkkUJCCb4zAvRduojdcySug5SaV1mWjK35u1J/5hxQyw FG6kt31qRloHbXsN+Ga9bTp2iFmgYEXPX5OArUsH3dkXe/gfQpcRbw3MVQ4kpg8N5bO3AfYlF/c obDiILouqe00sHDQhHwxmfQN3NJ+PfK4S9ILvkGieWEhyypnxfhzN0pgHDFLN3G4mWcoT+OPdn7 0/EOWFm8oVH6vBv1kOPykKiEJ92zoFeojQWlLAqjhIeOPrAd4IcqgLRhTHjLQA5HJSwOaPCDeQw GYlobB1UGag6TSNE9OLB9f7d1+wySvHTODuM422AMBbCHdN1mb/7M/1oNlWPzx5fdmMIJ7ISZb9 EJeSiPjar7ovCx/nBzmubXUxJHEJ9o7NCOV4lJU+PrgxB0IEikIXbbPYR9YIdd0IZck= X-Received: by 2002:a05:6000:420b:b0:482:e10e:58df with SMTP id ffacd0b85a97d-482f7a03ba8mr20017068f8f.21.1788039187820; Sat, 29 Aug 2026 14:33:07 -0700 (PDT) Received: from localhost.localdomain (dynamic-2a02-3100-ae86-1b01-80e2-8851-4221-68d8.310.pool.telefonica.de. [2a02:3100:ae86:1b01:80e2:8851:4221:68d8]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-482fbb33070sm12540616f8f.36.2026.08.29.14.33.06 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Sat, 29 Aug 2026 14:33:06 -0700 (PDT) From: Karl Mehltretter To: selinux@vger.kernel.org Cc: Karl Mehltretter , Paul Moore , Stephen Smalley , Ondrej Mosnacek , Miklos Szeredi , Amir Goldstein , Christian Brauner , Baokun Li , linux-fsdevel@vger.kernel.org, linux-unionfs@vger.kernel.org, linux-kernel@vger.kernel.org, stable@vger.kernel.org Subject: [PATCH v3 2/2] selinux: recheck intermediate backing files on mprotect Date: Sat, 29 Aug 2026 23:32:56 +0200 Message-Id: <20260829213256.51527-3-kmehltretter@gmail.com> X-Mailer: git-send-email 2.39.5 (Apple Git-154) In-Reply-To: <20260829213256.51527-1-kmehltretter@gmail.com> References: <20260829213256.51527-1-kmehltretter@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" mprotect() can be used to bypass the SELinux checks that mmap() performs against the intermediate layers of a stacked filesystem. mmap() checks every backing layer as the request descends through the stack. mprotect() only has the lowest backing file in vma->vm_file, so it rechecks the top-level user and the lowest mounter, but skips the mounters of every layer in between. With two nested overlayfs mounts and a policy denying mounter_t -> middle_file_t:file { execute }, a direct mmap(PROT_EXEC) is denied: avc: denied { execute } for pid=3D71 comm=3D"nested_exec" path=3D"/payload" dev=3D"overlay" ino=3D9 scontext=3Duser_u:base_r:mounter_t tcontext=3Duser_u:object_r:middle_file_t tclass=3Dfile permissive=3D0 while mmap(PROT_NONE) followed by mprotect(PROT_EXEC) succeeds. Preserve each intermediate path, mounter SID and file-description SID in the backing-file security blob, copying the saved entries when another backing layer is opened. Allocate the array only for nested backing files, and release it and the path references in the backing_file_free hook. During mprotect(), recheck fd { use } and the requested inode permissions for every saved mounter, and include the intermediate layers in the execmod checks. Policy for nested stacking may then need to grant intermediate mounters what a direct mmap() already requires, and execmod on intermediate labels for binaries using text relocations. Tested on arm64 QEMU with a small BusyBox initramfs and a purpose-built SELinux policy, on a mainline tree containing commit f2381b546e7e ("fs: fix user path of nested backing files"). Fixes: 82544d36b172 ("selinux: fix overlayfs mmap() and mprotect() access c= hecks") Cc: Assisted-by: LLM Signed-off-by: Karl Mehltretter Reviewed-by: Stephen Smalley --- security/selinux/hooks.c | 141 ++++++++++++++++++++++++++---- security/selinux/include/objsec.h | 8 ++ 2 files changed, 133 insertions(+), 16 deletions(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 232b7e7bfcafd..b6750edcc4783 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -1674,26 +1674,32 @@ static int cred_has_capability(const struct cred *c= red, return rc; } =20 -/* Check whether a task has a particular permission to an inode. - The 'adp' parameter is optional and allows other audit - data to be passed (e.g. the dentry). */ -static int inode_has_perm(const struct cred *cred, - struct inode *inode, - u32 perms, - struct common_audit_data *adp) +/* + * Check whether a SID has a particular permission to an inode. The 'adp' + * parameter is optional and allows other audit data to be passed (e.g. the + * dentry). + */ +static int inode_sid_has_perm(u32 sid, struct inode *inode, u32 perms, + struct common_audit_data *adp) { struct inode_security_struct *isec; - u32 sid; =20 if (unlikely(IS_PRIVATE(inode))) return 0; =20 - sid =3D cred_sid(cred); isec =3D selinux_inode(inode); =20 return avc_has_perm(sid, isec->sid, isec->sclass, perms, adp); } =20 +static int inode_has_perm(const struct cred *cred, + struct inode *inode, + u32 perms, + struct common_audit_data *adp) +{ + return inode_sid_has_perm(cred_sid(cred), inode, perms, adp); +} + /* Same as inode_has_perm, but pass explicit audit data containing the dentry to help the auditing code to more easily generate the pathname if needed. */ @@ -3854,13 +3860,63 @@ static int selinux_backing_file_alloc(struct file *= backing_file, const struct file *user_file) { struct backing_file_security_struct *bfsec; + const struct backing_file_security_struct *ubfsec; + struct backing_file_security_layer *layer; + u32 i; =20 bfsec =3D selinux_backing_file(backing_file); bfsec->uf_sid =3D selinux_file_user_sid(user_file); + if (!(user_file->f_mode & FMODE_BACKING)) + return 0; + + ubfsec =3D selinux_backing_file(user_file); + /* a wrapped count would make kmalloc_array() return ZERO_SIZE_PTR */ + if (unlikely(ubfsec->layer_count =3D=3D U32_MAX)) + return -EOVERFLOW; + + /* + * The final VMA only retains the lowest backing file, so record the + * whole chain here rather than in the mmap hook, where concurrent + * mappings would have to be serialized. Size it dynamically: erofs + * inode sharing adds a backing file without bumping s_stack_depth. + */ + bfsec->layers =3D kmalloc_array(ubfsec->layer_count + 1, + sizeof(*bfsec->layers), GFP_KERNEL); + if (!bfsec->layers) + return -ENOMEM; + + for (i =3D 0; i < ubfsec->layer_count; i++) { + layer =3D &bfsec->layers[i]; + *layer =3D ubfsec->layers[i]; + path_get(&layer->path); + } + + /* f_path, not file_user_path(): this layer, not the top-level file */ + layer =3D &bfsec->layers[i]; + layer->path =3D user_file->f_path; + layer->mounter_sid =3D cred_sid(user_file->f_cred); + layer->fd_sid =3D selinux_file(user_file)->sid; + path_get(&layer->path); + bfsec->layer_count =3D ubfsec->layer_count + 1; =20 return 0; } =20 +static void selinux_backing_file_free(struct file *backing_file) +{ + struct backing_file_security_struct *bfsec; + + /* security_backing_file_free() may be called twice after an error */ + if (!backing_file_security(backing_file)) + return; + + bfsec =3D selinux_backing_file(backing_file); + while (bfsec->layer_count) + path_put(&bfsec->layers[--bfsec->layer_count].path); + kfree(bfsec->layers); + bfsec->layers =3D NULL; +} + /* * Check whether a task has the ioctl permission and cmd * operation to an inode. @@ -3978,6 +4034,53 @@ static int selinux_file_ioctl_compat(struct file *fi= le, unsigned int cmd, =20 static int default_noexec __ro_after_init; =20 +static u32 file_map_prot_to_av(unsigned long prot, bool shared) +{ + u32 av =3D FILE__READ; + + if (shared && (prot & PROT_WRITE)) + av |=3D FILE__WRITE; + if (prot & PROT_EXEC) + av |=3D FILE__EXECUTE; + + return av; +} + +static int backing_mounters_has_perm(const struct file *file, u32 av) +{ + const struct backing_file_security_struct *bfsec; + const struct backing_file_security_layer *layer; + struct common_audit_data ad; + struct inode *inode; + u32 i; + int rc; + + if (WARN_ON_ONCE(!(file->f_mode & FMODE_BACKING))) + return -EIO; + + bfsec =3D selinux_backing_file(file); + for (i =3D 0; i < bfsec->layer_count; i++) { + layer =3D &bfsec->layers[i]; + inode =3D d_inode(layer->path.dentry); + + ad.type =3D LSM_AUDIT_DATA_PATH; + ad.u.path =3D layer->path; + + if (layer->mounter_sid !=3D layer->fd_sid) { + rc =3D avc_has_perm(layer->mounter_sid, layer->fd_sid, + SECCLASS_FD, FD__USE, &ad); + if (rc) + return rc; + } + + rc =3D inode_sid_has_perm(layer->mounter_sid, inode, av, &ad); + if (rc) + return rc; + } + + return 0; +} + static int __file_map_prot_check(const struct file *file, unsigned long pr= ot, bool shared, bool mounter_check, bool bf_user_file) @@ -4011,14 +4114,10 @@ static int __file_map_prot_check(const struct file = *file, unsigned long prot, if (file) { const struct cred *cred =3D mounter_check ? file->f_cred : current_cred(); - /* "read" always possible, "write" only if shared */ - u32 av =3D FILE__READ; - if (shared && prot_write) - av |=3D FILE__WRITE; - if (prot_exec) - av |=3D FILE__EXECUTE; =20 - return __file_has_perm(cred, file, av, bf_user_file); + return __file_has_perm(cred, file, + file_map_prot_to_av(prot, shared), + bf_user_file); } =20 return 0; @@ -4113,6 +4212,7 @@ static int selinux_file_mprotect(struct vm_area_struc= t *vma, int rc; const struct cred *cred =3D current_cred(); u32 sid =3D cred_sid(cred); + u32 av; const struct file *file =3D vma->vm_file; bool backing_file; bool shared =3D vma->vm_flags & VM_SHARED; @@ -4156,6 +4256,10 @@ static int selinux_file_mprotect(struct vm_area_stru= ct *vma, if (rc) return rc; if (backing_file) { + rc =3D backing_mounters_has_perm(file, + FILE__EXECMOD); + if (rc) + return rc; rc =3D file_has_perm(file->f_cred, file, FILE__EXECMOD); if (rc) @@ -4168,6 +4272,10 @@ static int selinux_file_mprotect(struct vm_area_stru= ct *vma, if (rc) return rc; if (backing_file) { + av =3D file_map_prot_to_av(prot, shared); + rc =3D backing_mounters_has_perm(file, av); + if (rc) + return rc; rc =3D file_map_prot_check(file, prot, shared, true); if (rc) return rc; @@ -7642,6 +7750,7 @@ static struct security_hook_list selinux_hooks[] __ro= _after_init =3D { LSM_HOOK_INIT(file_permission, selinux_file_permission), LSM_HOOK_INIT(file_alloc_security, selinux_file_alloc_security), LSM_HOOK_INIT(backing_file_alloc, selinux_backing_file_alloc), + LSM_HOOK_INIT(backing_file_free, selinux_backing_file_free), LSM_HOOK_INIT(file_ioctl, selinux_file_ioctl), LSM_HOOK_INIT(file_ioctl_compat, selinux_file_ioctl_compat), LSM_HOOK_INIT(mmap_file, selinux_mmap_file), diff --git a/security/selinux/include/objsec.h b/security/selinux/include/o= bjsec.h index 853f7266ed189..2f21568251ffe 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h @@ -86,8 +86,16 @@ struct file_security_struct { u32 pseqno; /* Policy seqno at the time of file open */ }; =20 +struct backing_file_security_layer { + struct path path; /* this layer's real path */ + u32 mounter_sid; /* SID of the mounter that opened it */ + u32 fd_sid; /* SID of its open file description */ +}; + struct backing_file_security_struct { u32 uf_sid; /* top-level user file fsec->sid */ + u32 layer_count; /* number of intermediate backing files */ + struct backing_file_security_layer *layers; }; =20 struct superblock_security_struct { --=20 2.53.0