Re: [RFC PATCH v1 1/2] fs: Add O_DENY_WRITE

Jeff Xu <[email protected]> Mon, 25 Aug 2025 16:06:34 -0700
Newsgroups com.openwall.lists.kernel-hardening,org.kernel.vger.linux-api,org.kernel.vger.linux-fsdevel,org.kernel.vger.linux-integrity,org.kernel.vger.linux-kernel,org.kernel.vger.linux-security-module
Message-ID <CALmYWFuijKhKO+xOJfcLT2OQnJJTC1WrNG5yevLdRBNdVtWcUA@mail.gmail.com>
On Mon, Aug 25, 2025 at 2:56=E2=80=AFPM Andy Lutomirski <[email protected]=
t> wrote:
>
>
> > On Aug 25, 2025, at 11:10=E2=80=AFAM, Jeff Xu <[email protected]> wrote=
:
> >
> > =EF=BB=BFOn Mon, Aug 25, 2025 at 9:43=E2=80=AFAM Andy Lutomirski <luto@=
amacapital.net> wrote:
> >>> On Mon, Aug 25, 2025 at 2:31=E2=80=AFAM Micka=C3=ABl Sala=C3=BCn <mic=
@digikod.net> wrote:
> >>> On Sun, Aug 24, 2025 at 11:04:03AM -0700, Andy Lutomirski wrote:
> >>>> On Sun, Aug 24, 2025 at 4:03=E2=80=AFAM Micka=C3=ABl Sala=C3=BCn <mi=
[email protected]> wrote:
> >>>>> On Fri, Aug 22, 2025 at 09:45:32PM +0200, Jann Horn wrote:
> >>>>>> On Fri, Aug 22, 2025 at 7:08=E2=80=AFPM Micka=C3=ABl Sala=C3=BCn <=
[email protected]> wrote:
> >>>>>>> Add a new O_DENY_WRITE flag usable at open time and on opened fil=
e (e.g.
> >>>>>>> passed file descriptors).  This changes the state of the opened f=
ile 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 dis=
tros
> >>>>>>> that may not have a write-xor-execute policy.  See commit a5874fd=
e3c08
> >>>>>>> ("exec: Add a new AT_EXECVE_CHECK flag to execveat(2)")
> >>>>>>> Both execve(2) and the IOCTL to enable fsverity can already set t=
his
> >>>>>>> property on files with deny_write_access().  This new O_DENY_WRIT=
E make
> >>>>>> The kernel actually tried to get rid of this behavior on execve() =
in
> >>>>>> commit 2a010c41285345da60cece35575b4e0af7e7bf44.; but sadly that h=
ad
> >>>>>> to be reverted in commit 3b832035387ff508fdcf0fba66701afc78f79e3d
> >>>>>> because it broke userspace assumptions.
> >>>>> Oh, good to know.
> >>>>>>> it widely available.  This is similar to what other OSs may provi=
de
> >>>>>>> e.g., opening a file with only FILE_SHARE_READ on Windows.
> >>>>>> We used to have the analogous mmap() flag MAP_DENYWRITE, and that =
was
> >>>>>> removed for security reasons; as
> >>>>>> https://man7.org/linux/man-pages/man2/mmap.2.html says:
> >>>>>> |        MAP_DENYWRITE
> >>>>>> |               This flag is ignored.  (Long ago=E2=80=94Linux 2.0=
 and earlier=E2=80=94it
> >>>>>> |               signaled that attempts to write to the underlying =
file
> >>>>>> |               should fail with ETXTBSY.  But this was a source o=
f denial-
> >>>>>> |               of-service attacks.)"
> >>>>>> It seems to me that the same issue applies to your patch - it woul=
d
> >>>>>> allow unprivileged processes to essentially lock files such that o=
ther
> >>>>>> processes can't write to them anymore. This might allow unprivileg=
ed
> >>>>>> users to prevent root from updating config files or stuff like tha=
t if
> >>>>>> they're updated in-place.
> >>>>> Yes, I agree, but since it is the case for executed files I though =
it
> >>>>> was worth starting a discussion on this topic.  This new flag could=
 be
> >>>>> restricted to executable files, but we should avoid system-wide loc=
ks
> >>>>> like this.  I'm not sure how Windows handle these issues though.
> >>>>> Anyway, we should rely on the access control policy to control writ=
e and
> >>>>> execute access in a consistent way (e.g. write-xor-execute).  Thank=
s for
> >>>>> the references and the background!
> >>>> I'm confused.  I understand that there are many contexts in which on=
e
> >>>> would want to prevent execution of unapproved content, which might
> >>>> include preventing a given process from modifying some code and then
> >>>> executing it.
> >>>> I don't understand what these deny-write features have to do with it=
.
> >>>> These features merely prevent someone from modifying code *that is
> >>>> currently in use*, which is not at all the same thing as preventing
> >>>> modifying code that might get executed -- one can often modify
> >>>> contents *before* executing those contents.
> >>> The order of checks would be:
> >>> 1. open script with O_DENY_WRITE
> >>> 2. check executability with AT_EXECVE_CHECK
> >>> 3. read the content and interpret it
> >> Hmm.  Common LSM configurations should be able to handle this without
> >> deny write, I think.  If you don't want a program to be able to make
> >> their own scripts, then don't allow AT_EXECVE_CHECK to succeed on a
> >> script that the program can write.
> > Yes, Common LSM could handle this, however, due to historic and app
> > backward compability reason, sometimes it is impossible to enforce
> > that kind of policy in practice, therefore as an alternative, a
> > machinism such as AT_EXECVE_CHECK is really useful.
>
> Can you clarify?  I=E2=80=99m suspicious that we=E2=80=99re taking past e=
ach other.
>
Apology, my response isn't clear.

> AT_EXECVE_CHECK solves a problem that there are actions that effectively =
=E2=80=9Cexecute=E2=80=9D a file that don=E2=80=99t execute literal CPU ins=
tructions for it. Sometimes open+read has the effect of interpreting the co=
ntents of the file as something code-like.
>
Yes. We have the same understanding of this.
As an example, shell script or java byte code, their file permission
can be rw, but no x bit set. The interpreter reads those and executes
them.

> But, as I see it, deny-write is almost entirely orthogonal. If you open a=
 file with the intent of executing it (mmap-execute or interpret =E2=80=94 =
makes little practical difference here), then the kernel can enforce some p=
olicy. If the file is writable by a process that ought not have permission =
to execute code in the context of the opening-for-execute process, then LSM=
s need deny-write to be enforced so that they can verify the contents at th=
e time of opening.
>
> But let=E2=80=99s step back a moment: is there any actual sensible securi=
ty policy that does this?  If I want to *enforce* that a process only execu=
te approved code, then wouldn=E2=80=99t I do it be only allowing executing =
files that the process can=E2=80=99t write?
>
I imagine the following situation: an app has both "rw" access to the
file that holds the script code, the "w" is needed because the app
updates the script sometimes.

What is a reasonable sandbox solution for such an app? There are maybe
two options:

1> split the app as two processes: processA has "w" access to the
script for updating when needed. Process B has "r" access but no "w",
for executing. ProcessA and ProcessB will coordinate to avoid racing
on the script update.

2> The process will use AT_EXECVE_CHECK (added by interpreter) to
validate the file before opening , and the file content held by the
process should be immutable while being validated and executed later
by interpreter.

option 1 is the ideal, and IIUC, you promote this too. However, that
requires refactoring the app as two processes.
option 2 is an alternative. Because it doesn't require the change from
the apps, therefore a solution worth considering.

> The reason that the removal of deny-write wasn=E2=80=99t security =E2=80=
=94 it was a functionality issue: a linker accidentally modified an in-use =
binary. If you have permission to use gcc or lld, etc to create binaries, a=
nd you have permission to run them, then you pretty much have permission to=
 run whatever code you like.
>
> So, if there=E2=80=99s a real security use case for deny-write, I=E2=80=
=99m still not seeing it.
>
Although the current patch might not be ideal due to the potential DOS
attack, it does offer a starting point to address the needs. Let's
continue the discussion based on this patch and explore different
ideas.

Thanks and regards,
-Jeff

> >> Keep in mind that trying to lock this down too hard is pointless for
> >> users who are allowed to to ptrace-write to their own processes.  Or
> >> for users who can do JIT, or for users who can run a REPL, etc.
> > The ptrace-write and /proc/pid/mem writing are on my radar, at least
> > for ChomeOS and Android.
> > AT_EXECVE_CHECK is orthogonal to those IMO, I hope eventually all
> > those paths will be hardened.
> >
> > Thanks and regards,
> > -Jeff