On Mon, Aug 25, 2025 at 2:56 PM Andy Lutomirski <l...@amacapital.net> wrote: > > > > On Aug 25, 2025, at 11:10 AM, Jeff Xu <jef...@google.com> wrote: > > > > On Mon, Aug 25, 2025 at 9:43 AM Andy Lutomirski <l...@amacapital.net> > > wrote: > >>> On Mon, Aug 25, 2025 at 2:31 AM Mickaël Salaün <m...@digikod.net> wrote: > >>> On Sun, Aug 24, 2025 at 11:04:03AM -0700, Andy Lutomirski wrote: > >>>> On Sun, Aug 24, 2025 at 4:03 AM Mickaël Salaün <m...@digikod.net> wrote: > >>>>> On Fri, Aug 22, 2025 at 09:45:32PM +0200, Jann Horn wrote: > >>>>>> On Fri, Aug 22, 2025 at 7:08 PM Mickaël Salaün <m...@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 > >>>>>>> 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 > >>>>>> 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—Linux 2.0 and > >>>>>> earlier—it > >>>>>> | signaled that attempts to write to the underlying file > >>>>>> | should fail with ETXTBSY. But this was a source of > >>>>>> denial- > >>>>>> | 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 other > >>>>>> processes can't write to them anymore. This might allow unprivileged > >>>>>> users to prevent root from updating config files or stuff like that 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 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 and > >>>>> execute access in a consistent way (e.g. write-xor-execute). Thanks for > >>>>> 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’m suspicious that we’re taking past each other. > Apology, my response isn't clear.
> AT_EXECVE_CHECK solves a problem that there are actions that effectively > “execute” a file that don’t execute literal CPU instructions for it. > Sometimes open+read has the effect of interpreting the contents 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 — makes > 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 > execute code in the context of the opening-for-execute process, then LSMs > need deny-write to be enforced so that they can verify the contents at the > time of opening. > > But let’s step back a moment: is there any actual sensible security policy > that does this? If I want to *enforce* that a process only execute approved > code, then wouldn’t I do it be only allowing executing files that the process > can’t 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’t security — 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, and you have > permission to run them, then you pretty much have permission to run whatever > code you like. > > So, if there’s a real security use case for deny-write, I’m 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