From: "Mickaël Salaün" <mic@digikod.net>
To: Al Viro <viro@zeniv.linux.org.uk>,
Christian Brauner <brauner@kernel.org>,
Kees Cook <keescook@chromium.org>,
Paul Moore <paul@paul-moore.com>, Serge Hallyn <serge@hallyn.com>
Cc: "Mickaël Salaün" <mic@digikod.net>,
"Andy Lutomirski" <luto@kernel.org>,
"Arnd Bergmann" <arnd@arndb.de>,
"Christian Heimes" <christian@python.org>,
"Dmitry Vyukov" <dvyukov@google.com>,
"Elliott Hughes" <enh@google.com>,
"Fan Wu" <wufan@linux.microsoft.com>,
"Florian Weimer" <fweimer@redhat.com>,
"Jann Horn" <jannh@google.com>, "Jeff Xu" <jeffxu@google.com>,
"Jonathan Corbet" <corbet@lwn.net>,
"Jordan R Abrahams" <ajordanr@google.com>,
"Lakshmi Ramasubramanian" <nramas@linux.microsoft.com>,
"Luca Boccassi" <bluca@debian.org>,
"Matt Bobrowski" <mattbobrowski@google.com>,
"Miklos Szeredi" <mszeredi@redhat.com>,
"Mimi Zohar" <zohar@linux.ibm.com>,
"Nicolas Bouchinet" <nicolas.bouchinet@oss.cyber.gouv.fr>,
"Robert Waite" <rowait@microsoft.com>,
"Roberto Sassu" <roberto.sassu@huawei.com>,
"Scott Shell" <scottsh@microsoft.com>,
"Steve Dower" <steve.dower@python.org>,
"Steve Grubb" <sgrubb@redhat.com>,
kernel-hardening@lists.openwall.com, linux-api@vger.kernel.org,
linux-fsdevel@vger.kernel.org, linux-integrity@vger.kernel.org,
linux-kernel@vger.kernel.org,
linux-security-module@vger.kernel.org,
"Andy Lutomirski" <luto@amacapital.net>,
"Jeff Xu" <jeffxu@chromium.org>
Subject: [RFC PATCH v1 1/2] fs: Add O_DENY_WRITE
Date: Fri, 22 Aug 2025 19:07:59 +0200 [thread overview]
Message-ID: <20250822170800.2116980-2-mic@digikod.net> (raw)
In-Reply-To: <20250822170800.2116980-1-mic@digikod.net>
Add a new O_DENY_WRITE flag usable at open time and on opened file (e.g.
passed file descriptors). This changes the state of the opened file by
making it read-only until it is closed. The main use case is for script
interpreters to get the guarantee that script' content cannot be altered
while being read and interpreted. This is useful for generic distros
that may not have a write-xor-execute policy. See commit a5874fde3c08
("exec: Add a new AT_EXECVE_CHECK flag to execveat(2)")
Both execve(2) and the IOCTL to enable fsverity can already set this
property on files with deny_write_access(). This new O_DENY_WRITE make
it widely available. This is similar to what other OSs may provide
e.g., opening a file with only FILE_SHARE_READ on Windows.
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Christian Brauner <brauner@kernel.org>
Cc: Jeff Xu <jeffxu@chromium.org>
Cc: Kees Cook <keescook@chromium.org>
Cc: Paul Moore <paul@paul-moore.com>
Cc: Serge Hallyn <serge@hallyn.com>
Reported-by: Robert Waite <rowait@microsoft.com>
Signed-off-by: Mickaël Salaün <mic@digikod.net>
Link: https://lore.kernel.org/r/20250822170800.2116980-2-mic@digikod.net
---
fs/fcntl.c | 26 ++++++++++++++++++++++++--
fs/file_table.c | 2 ++
fs/namei.c | 6 ++++++
include/linux/fcntl.h | 2 +-
include/uapi/asm-generic/fcntl.h | 4 ++++
5 files changed, 37 insertions(+), 3 deletions(-)
diff --git a/fs/fcntl.c b/fs/fcntl.c
index 5598e4d57422..0c80c0fbc706 100644
--- a/fs/fcntl.c
+++ b/fs/fcntl.c
@@ -34,7 +34,8 @@
#include "internal.h"
-#define SETFL_MASK (O_APPEND | O_NONBLOCK | O_NDELAY | O_DIRECT | O_NOATIME)
+#define SETFL_MASK (O_APPEND | O_NONBLOCK | O_NDELAY | O_DIRECT | O_NOATIME | \
+ O_DENY_WRITE)
static int setfl(int fd, struct file * filp, unsigned int arg)
{
@@ -80,8 +81,29 @@ static int setfl(int fd, struct file * filp, unsigned int arg)
error = 0;
}
spin_lock(&filp->f_lock);
+
+ if (arg & O_DENY_WRITE) {
+ /* Only regular files. */
+ if (!S_ISREG(inode->i_mode)) {
+ error = -EINVAL;
+ goto unlock;
+ }
+
+ /* Only sets once. */
+ if (!(filp->f_flags & O_DENY_WRITE)) {
+ error = exe_file_deny_write_access(filp);
+ if (error)
+ goto unlock;
+ }
+ } else {
+ if (filp->f_flags & O_DENY_WRITE)
+ exe_file_allow_write_access(filp);
+ }
+
filp->f_flags = (arg & SETFL_MASK) | (filp->f_flags & ~SETFL_MASK);
filp->f_iocb_flags = iocb_flags(filp);
+
+unlock:
spin_unlock(&filp->f_lock);
out:
@@ -1158,7 +1180,7 @@ static int __init fcntl_init(void)
* Exceptions: O_NONBLOCK is a two bit define on parisc; O_NDELAY
* is defined as O_NONBLOCK on some platforms and not on others.
*/
- BUILD_BUG_ON(20 - 1 /* for O_RDONLY being 0 */ !=
+ BUILD_BUG_ON(21 - 1 /* for O_RDONLY being 0 */ !=
HWEIGHT32(
(VALID_OPEN_FLAGS & ~(O_NONBLOCK | O_NDELAY)) |
__FMODE_EXEC));
diff --git a/fs/file_table.c b/fs/file_table.c
index 81c72576e548..6ba896b6a53f 100644
--- a/fs/file_table.c
+++ b/fs/file_table.c
@@ -460,6 +460,8 @@ static void __fput(struct file *file)
locks_remove_file(file);
security_file_release(file);
+ if (unlikely(file->f_flags & O_DENY_WRITE))
+ exe_file_allow_write_access(file);
if (unlikely(file->f_flags & FASYNC)) {
if (file->f_op->fasync)
file->f_op->fasync(-1, file, 0);
diff --git a/fs/namei.c b/fs/namei.c
index cd43ff89fbaa..366530bf937d 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -3885,6 +3885,12 @@ static int do_open(struct nameidata *nd,
error = may_open(idmap, &nd->path, acc_mode, open_flag);
if (!error && !(file->f_mode & FMODE_OPENED))
error = vfs_open(&nd->path, file);
+ if (!error && (open_flag & O_DENY_WRITE)) {
+ if (S_ISREG(file_inode(file)->i_mode))
+ error = exe_file_deny_write_access(file);
+ else
+ error = -EINVAL;
+ }
if (!error)
error = security_file_post_open(file, op->acc_mode);
if (!error && do_truncate)
diff --git a/include/linux/fcntl.h b/include/linux/fcntl.h
index a332e79b3207..dad14101686f 100644
--- a/include/linux/fcntl.h
+++ b/include/linux/fcntl.h
@@ -10,7 +10,7 @@
(O_RDONLY | O_WRONLY | O_RDWR | O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC | \
O_APPEND | O_NDELAY | O_NONBLOCK | __O_SYNC | O_DSYNC | \
FASYNC | O_DIRECT | O_LARGEFILE | O_DIRECTORY | O_NOFOLLOW | \
- O_NOATIME | O_CLOEXEC | O_PATH | __O_TMPFILE)
+ O_NOATIME | O_CLOEXEC | O_PATH | __O_TMPFILE | O_DENY_WRITE)
/* List of all valid flags for the how->resolve argument: */
#define VALID_RESOLVE_FLAGS \
diff --git a/include/uapi/asm-generic/fcntl.h b/include/uapi/asm-generic/fcntl.h
index 613475285643..facd9136f5af 100644
--- a/include/uapi/asm-generic/fcntl.h
+++ b/include/uapi/asm-generic/fcntl.h
@@ -91,6 +91,10 @@
/* a horrid kludge trying to make sure that this will fail on old kernels */
#define O_TMPFILE (__O_TMPFILE | O_DIRECTORY)
+#ifndef O_DENY_WRITE
+#define O_DENY_WRITE 040000000
+#endif
+
#ifndef O_NDELAY
#define O_NDELAY O_NONBLOCK
#endif
--
2.50.1
next prev parent reply other threads:[~2025-08-22 17:08 UTC|newest]
Thread overview: 40+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-08-22 17:07 [RFC PATCH v1 0/2] Add O_DENY_WRITE (complement AT_EXECVE_CHECK) Mickaël Salaün
2025-08-22 17:07 ` Mickaël Salaün [this message]
2025-08-22 19:45 ` [RFC PATCH v1 1/2] fs: Add O_DENY_WRITE Jann Horn
2025-08-24 11:03 ` Mickaël Salaün
2025-08-24 18:04 ` Andy Lutomirski
2025-08-25 9:31 ` Mickaël Salaün
2025-08-25 9:39 ` Florian Weimer
2025-08-26 12:35 ` Mickaël Salaün
2025-08-25 16:43 ` Andy Lutomirski
2025-08-25 18:10 ` Jeff Xu
2025-08-25 17:57 ` Jeff Xu
2025-08-26 12:39 ` Mickaël Salaün
2025-08-26 20:29 ` Jeff Xu
2025-08-27 8:19 ` Mickaël Salaün
2025-08-28 20:17 ` Jeff Xu
2025-08-27 10:18 ` Aleksa Sarai
2025-08-27 10:29 ` Aleksa Sarai
2025-08-22 17:08 ` [RFC PATCH v1 2/2] selftests/exec: Add O_DENY_WRITE tests Mickaël Salaün
2025-08-26 9:07 ` [RFC PATCH v1 0/2] Add O_DENY_WRITE (complement AT_EXECVE_CHECK) Christian Brauner
2025-08-26 11:23 ` Mickaël Salaün
2025-08-26 12:30 ` Theodore Ts'o
2025-08-26 17:47 ` Mickaël Salaün
2025-08-26 20:50 ` Theodore Ts'o
2025-08-27 8:19 ` Mickaël Salaün
2025-08-27 17:35 ` Andy Lutomirski
2025-08-27 19:07 ` Mickaël Salaün
2025-08-27 20:35 ` Andy Lutomirski
2025-08-28 0:14 ` Aleksa Sarai
2025-08-28 0:32 ` Andy Lutomirski
2025-08-28 0:52 ` Aleksa Sarai
2025-08-28 21:01 ` Serge E. Hallyn
2025-09-01 11:05 ` Jann Horn
2025-09-01 13:18 ` Serge E. Hallyn
2025-09-01 16:01 ` Andy Lutomirski
2025-09-01 9:24 ` Roberto Sassu
2025-09-01 16:25 ` Andy Lutomirski
2025-09-01 17:01 ` Roberto Sassu
2025-09-02 8:57 ` Roberto Sassu
-- strict thread matches above, loose matches on Subject: below --
2025-08-25 21:56 [RFC PATCH v1 1/2] fs: Add O_DENY_WRITE Andy Lutomirski
2025-08-25 23:06 ` Jeff Xu
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250822170800.2116980-2-mic@digikod.net \
--to=mic@digikod.net \
--cc=ajordanr@google.com \
--cc=arnd@arndb.de \
--cc=bluca@debian.org \
--cc=brauner@kernel.org \
--cc=christian@python.org \
--cc=corbet@lwn.net \
--cc=dvyukov@google.com \
--cc=enh@google.com \
--cc=fweimer@redhat.com \
--cc=jannh@google.com \
--cc=jeffxu@chromium.org \
--cc=jeffxu@google.com \
--cc=keescook@chromium.org \
--cc=kernel-hardening@lists.openwall.com \
--cc=linux-api@vger.kernel.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-integrity@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-security-module@vger.kernel.org \
--cc=luto@amacapital.net \
--cc=luto@kernel.org \
--cc=mattbobrowski@google.com \
--cc=mszeredi@redhat.com \
--cc=nicolas.bouchinet@oss.cyber.gouv.fr \
--cc=nramas@linux.microsoft.com \
--cc=paul@paul-moore.com \
--cc=roberto.sassu@huawei.com \
--cc=rowait@microsoft.com \
--cc=scottsh@microsoft.com \
--cc=serge@hallyn.com \
--cc=sgrubb@redhat.com \
--cc=steve.dower@python.org \
--cc=viro@zeniv.linux.org.uk \
--cc=wufan@linux.microsoft.com \
--cc=zohar@linux.ibm.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).