Re: [RFC PATCH v1 1/2] fs: Add O_DENY_WRITE
Andy Lutomirski <[email protected]> Mon, 25 Aug 2025 14:56:18 -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 | <[email protected]> |
=EF=BB=BF > On Aug 25, 2025, at 11:10=E2=80=AFAM, Jeff Xu <[email protected]> wrote: >=20 > =EF=BB=BFOn Mon, Aug 25, 2025 at 9:43=E2=80=AFAM Andy Lutomirski <luto@ama= capital.net> wrote: >>> On Mon, Aug 25, 2025 at 2:31=E2=80=AFAM Micka=C3=ABl Sala=C3=BCn <mic@di= gikod.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 <mic@d= igikod.net> 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 <mic= @digikod.net> wrote: >>>>>>> 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 sc= ript >>>>>>> interpreters to get the guarantee that script' content cannot be alt= ered >>>>>>> while being read and interpreted. This is useful for generic distro= s >>>>>>> that may not have a write-xor-execute policy. See commit a5874fde3c= 08 >>>>>>> ("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 m= ake >>>>>> The kernel actually tried to get rid of this behavior on execve() in >>>>>> commit 2a010c41285345da60cece35575b4e0af7e7bf44.; but sadly that had >>>>>> 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 provide >>>>>>> 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 an= d earlier=E2=80=94it >>>>>> | signaled that attempts to write to the underlying fil= e >>>>>> | should fail with ETXTBSY. But this was a source of d= enial- >>>>>> | of-service attacks.)" >>>>>> It seems to me that the same issue applies to your patch - it would >>>>>> allow unprivileged processes to essentially lock files such that othe= r >>>>>> processes can't write to them anymore. This might allow unprivileged >>>>>> users to prevent root from updating config files or stuff like that i= f >>>>>> 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 locks >>>>> like this. I'm not sure how Windows handle these issues though. >>>>> Anyway, we should rely on the access control policy to control write a= nd >>>>> execute access in a consistent way (e.g. write-xor-execute). Thanks f= or >>>>> the references and the background! >>>> I'm confused. I understand that there are many contexts in which one >>>> 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 each= other. 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 instruc= tions for it. Sometimes open+read has the effect of interpreting the content= s of the file as something code-like. But, as I see it, deny-write is almost entirely orthogonal. If you open a fi= le with the intent of executing it (mmap-execute or interpret =E2=80=94 make= s little practical difference here), then the kernel can enforce some policy= . If the file is writable by a process that ought not have permission to exe= cute code in the context of the opening-for-execute process, then LSMs need d= eny-write to be enforced so that they can verify the contents at the time of= opening. But let=E2=80=99s step back a moment: is there any actual sensible security p= olicy that does this? If I want to *enforce* that a process only execute ap= proved code, then wouldn=E2=80=99t I do it be only allowing executing files t= hat the process can=E2=80=99t write? The reason that the removal of deny-write wasn=E2=80=99t security =E2=80=94 i= t 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, and you h= ave permission to run them, then you pretty much have permission to run what= ever code you like. So, if there=E2=80=99s a real security use case for deny-write, I=E2=80=99m s= till not seeing it. >> 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. >=20 > Thanks and regards, > -Jeff