LWN: Comments on "From O_MAYEXEC to trusted_for()" https://lwn.net/Articles/832959/ This is a special feed containing comments posted to the individual LWN article titled "From O_MAYEXEC to trusted_for()". en-us Sun, 16 Nov 2025 03:10:42 +0000 Sun, 16 Nov 2025 03:10:42 +0000 https://www.rssboard.org/rss-specification lwn@lwn.net From O_MAYEXEC to trusted_for() https://lwn.net/Articles/837983/ https://lwn.net/Articles/837983/ nix <div class="FormattedComment"> Aha! Thank you: as with so many decades-old misconceptions this one is really obvious in hindsight once pointed out.<br> <p> Occasionally I fall into the trap of thinking I know a lot about C, and then something dead obvious and simple like this pops up and I realise I know nothing.<br> </div> Fri, 20 Nov 2020 22:12:59 +0000 From O_MAYEXEC to trusted_for() https://lwn.net/Articles/836935/ https://lwn.net/Articles/836935/ foom Const in a <em>declaration</em> is what's meaningless (and ignored by the compiler). <p> E.g. if you have a declaration in a header: <pre> void foo(const int k); </pre> You can still entirely validly and correctly have the implementation: <pre> void foo(int k) { k++; } </pre> Thu, 12 Nov 2020 00:29:49 +0000 From O_MAYEXEC to trusted_for() https://lwn.net/Articles/836807/ https://lwn.net/Articles/836807/ anselm <blockquote><em> Const applied to integers... it has no effect, but it doesn't even feel like useful for documentation purposes. I can't figure out what it might mean even conceptually. </em></blockquote> <p> Hm. No effect? </p> <pre> $ cat t.c void foo(int k) { k++; } void cfoo(const int k) { k++; } $ gcc t.c t.c: In function ‘cfoo’: t.c:6:6: error: increment of read-only parameter ‘k’ 6 | k++; | ^~ </pre> <p> Looks like you don't get to change a <tt>const int</tt> parameter inside a function. This doesn't seem to require huge conceptual leaps. </p> <p> As far as the C language is concerned, it's OK to change the (non-<tt>const</tt>) <tt>int</tt> parameter inside the <tt>foo()</tt> function, if one doesn't mind that the caller of <tt>foo()</tt> never sees the change. In effect, the parameter is just another local variable. </p> Tue, 10 Nov 2020 22:31:15 +0000 From O_MAYEXEC to trusted_for() https://lwn.net/Articles/836779/ https://lwn.net/Articles/836779/ ksandstr <div class="FormattedComment"> The consting applied to parameters passed by value is just noise. And the enum issue applies in userspace alone, since the user-to-kernel syscall interface promotes integral-typed parameters to native word size anyway; so the manpage trusted_for(2) would specify that as an int.<br> <p> Sort of smells like a literal-minded machine translation of a completely different language, in fact.<br> </div> Tue, 10 Nov 2020 17:09:31 +0000 From O_MAYEXEC to trusted_for() https://lwn.net/Articles/835972/ https://lwn.net/Articles/835972/ nix <div class="FormattedComment"> Hm. Don&#x27;t like that syscall much. Not because of the name, though:<br> <p> - what&#x27;s with all the const? Const applied to pointers is meaningful. Const applied to integers... it has no effect, but it doesn&#x27;t even feel like useful for documentation purposes. I can&#x27;t figure out what it might mean even conceptually. You can&#x27;t modify this... integer constant? Well, no, you generally can&#x27;t mutate abstract mathematical entities like integers without being God (or older FORTRAN ;) ).<br> - is it sane to use enums in system calls, given that their bit-width is implementation-defined and can change merely because you *add values* to the enumeration?<br> <p> </div> Tue, 03 Nov 2020 21:01:25 +0000 From O_MAYEXEC to trusted_for() https://lwn.net/Articles/835970/ https://lwn.net/Articles/835970/ nix <blockquote> Also, on a partially related note, there was some buffer overflow or "stack smashing" attack involving large command lines </blockquote> This was incredibly annoying and continues to break my workflows to this day, but apparently not breaking userspace doesn't apply when it can be abused by attackers and it might be inconvenient to fix it. Tue, 03 Nov 2020 20:53:53 +0000 From O_MAYEXEC to trusted_for() https://lwn.net/Articles/833937/ https://lwn.net/Articles/833937/ mjg59 <div class="FormattedComment"> Just leaving it up to the interpreters to do validation means you can&#x27;t do signature validation without implementing equivalent code in every interpreter. Passing information up to the kernel lets you apply an appropriate IMA check instead.<br> </div> Sat, 10 Oct 2020 02:15:53 +0000 From O_MAYEXEC to trusted_for() https://lwn.net/Articles/833934/ https://lwn.net/Articles/833934/ sbelmon <div class="FormattedComment"> I&#x27;m baffled. Since the interpreter has to cooperate anyway, why not have it check some setting in /etc that says &quot;hey, interpreters, don&#x27;t run any script that isn&#x27;t +x&quot;?<br> </div> Fri, 09 Oct 2020 23:18:56 +0000 From O_MAYEXEC to trusted_for() https://lwn.net/Articles/833739/ https://lwn.net/Articles/833739/ jamesmorris <div class="FormattedComment"> Yes, but this crosses a trust boundary.<br> </div> Thu, 08 Oct 2020 06:16:54 +0000 From O_MAYEXEC to trusted_for() https://lwn.net/Articles/833738/ https://lwn.net/Articles/833738/ skx <p>It is funny you say that, because I wrote a Linux Security Module which does almost exactly that:</p> <ul> <li>Every time the kernel tries to execute a binary..</li> <li>I calls back to user-space to run a "is this permitted" executable.</li> <li>If the user-space program returns 0 the execution is permitted, otherwise it is denied.</li> </ul> <p>On the one hand this is horrific, on the other hand it does work. It would allow you to write policies in userspace.</p> <p>The overhead is high, for every executable that is launched you have to also launch the policy-program.</p> <p>No chance in hell of this getting merged into the kernel, so I didn't even try, but I had fun learning:</p> <ul> <li><a href="https://github.com/skx/linux-security-modules/tree/master/security/can-exec">https://github.com/skx/linux-security-modules/tree/master/security/can-exec</a></li> </ul> Thu, 08 Oct 2020 05:21:37 +0000 From O_MAYEXEC to trusted_for() https://lwn.net/Articles/833732/ https://lwn.net/Articles/833732/ NYKevin <div class="FormattedComment"> You would also need to C-ify every single .py file in both the standard library and all of your external dependencies. Since you can&#x27;t use PyRun_SimpleString() for anything that doesn&#x27;t live in __main__, this quickly becomes a lot more complicated than what you describe.<br> <p> Cython can *mostly* do that work automatically, but I don&#x27;t think they&#x27;ve yet managed to reach perfect 1:1 compatibility with pure Python modules, so you would need to run a battery of unit tests over the resulting output to be sure it actually works.<br> </div> Thu, 08 Oct 2020 03:29:15 +0000 From O_MAYEXEC to trusted_for() https://lwn.net/Articles/833635/ https://lwn.net/Articles/833635/ zev <i>stdin might be a tty/pty, an anonymous pipe, etc., which are non-executable on most systems most of the time. But most interpreters want to treat a tty, at least, as if it were executable (because otherwise you can't have a REPL), so now you have to figure out whether to allow any other stdin-related exceptions, and if so, how to check for them.</i> <br> <br> On the kinds of systems this is (I guess?) purportedly targeted at, I'd expect losing REPL functionality wouldn't be considered a problem. <br> <br> However, that in turn makes me wonder if they might be better served by eliminating the general-purpose interpreter binary entirely and replacing each script that depends on it with the equivalent of <pre> #include &lt;Python.h&gt; int main(void) { PyRun_SimpleString("...script source code..."); } </pre> (A partial application of the script to the interpreter, basically.) Seems like it'd be a much more foolproof approach. Wed, 07 Oct 2020 06:46:11 +0000 From O_MAYEXEC to trusted_for() https://lwn.net/Articles/833619/ https://lwn.net/Articles/833619/ sbaugh <div class="FormattedComment"> That could happen in user space too, though, couldn&#x27;t it? Can&#x27;t user space query the integrity-verified-status of a file?<br> </div> Tue, 06 Oct 2020 16:48:37 +0000 From O_MAYEXEC to trusted_for() https://lwn.net/Articles/833617/ https://lwn.net/Articles/833617/ NYKevin <div class="FormattedComment"> I think you&#x27;re right, but I can still think of a number of problems with this:<br> <p> - euidaccess(3) appears to fall back to mode bits if uid != euid. The version I looked at doesn&#x27;t even check for EROFS in this case (a fact which is conveniently omitted from the man page). But interpreters probably shouldn&#x27;t be setuid in the first place.<br> - stdin might be a tty/pty, an anonymous pipe, etc., which are non-executable on most systems most of the time. But most interpreters want to treat a tty, at least, as if it were executable (because otherwise you can&#x27;t have a REPL), so now you have to figure out whether to allow any other stdin-related exceptions, and if so, how to check for them.<br> - You need proc to be mounted and accessible.<br> - /proc/[pid]/fd is Linux-specific. So is trusted_for(), but trusted_for() was specifically designed for this individual use case, while /proc was not, so interpreters would have had to foresee that this extra permissions enforcement would be useful on Linux in particular. They would be unable to do it on systems that lacked a /proc or lacked a /proc/[pid]/fd.<br> - I&#x27;m having some difficulty figuring out exactly when /proc/[pid]/fd was introduced. For very old interpreters, it might not have been available at the time they made this decision (especially for languages that originated on a pre-Linux Unix).<br> <p> And the single most important reason:<br> <p> - Most people are going to read the access/euidaccess man pages, which tell you not to use them, and then they will conclude that you should not use them. This is the same problem that /dev/urandom had.<br> </div> Tue, 06 Oct 2020 16:38:21 +0000 From O_MAYEXEC to trusted_for() https://lwn.net/Articles/833618/ https://lwn.net/Articles/833618/ jamesmorris <div class="FormattedComment"> One use-case is the flag then being used by an LSM (e.g. IPE) to cause the file to be integrity-verified.<br> <p> <p> </div> Tue, 06 Oct 2020 16:31:14 +0000 From O_MAYEXEC to trusted_for() https://lwn.net/Articles/833556/ https://lwn.net/Articles/833556/ matthias <div class="FormattedComment"> <font class="QuotedText">&gt; If a userspace daemon can&#x27;t get enough information to determine whether the access should be allowed, perhaps userspace should be given that information in some way...</font><br> <p> You mean by adding a trusted_for() system call? ;)<br> <p> Sorry, could not resist.<br> </div> Tue, 06 Oct 2020 05:14:17 +0000 From O_MAYEXEC to trusted_for() https://lwn.net/Articles/833445/ https://lwn.net/Articles/833445/ MrWim <code>access("/proc/self/fd/n", X_OK)</code> should be safe from TOCTTOU I would have thought. Sat, 03 Oct 2020 23:09:24 +0000 From O_MAYEXEC to trusted_for() https://lwn.net/Articles/833440/ https://lwn.net/Articles/833440/ sbaugh <div class="FormattedComment"> With this kind of interface, it&#x27;s less clear why the kernel is even involved. Couldn&#x27;t this check be performed in userspace, by querying some kind of userspace policy daemon? If a userspace daemon can&#x27;t get enough information to determine whether the access should be allowed, perhaps userspace should be given that information in some way...<br> <p> Maybe that sounds excessively micro-kernel-y, but this whole scheme will require a carefully tuned and controlled userspace anyway...<br> </div> Sat, 03 Oct 2020 21:34:40 +0000 From O_MAYEXEC to trusted_for() https://lwn.net/Articles/833437/ https://lwn.net/Articles/833437/ NYKevin <div class="FormattedComment"> <font class="QuotedText">&gt; TBH it&#x27;s kind of a design mistake for an interpreter to not check for execute flag from the beginning.</font><br> <p> The only way (that I know of) to do that is to call access(2), which has a Big Honking Warning telling you not to use it as a security boundary because of TOCTTOU races.<br> <p> Sure, you can call fstat(2) and manually evaluate the mode bits against the EUID, but then the operating system cannot enforce filesystem noexec and similar policies, which the Android people almost certainly want to do. So instead the interpreter has to know about every system-level policy that might possibly want to restrict execute permission, for every platform where it runs, and manually check them all one at a time, which is gross and uncivilized. Much better to just ask the kernel &quot;Hey, can I execute that?&quot; - exactly the question trusted_for() is intended to answer.<br> </div> Sat, 03 Oct 2020 19:58:18 +0000 From O_MAYEXEC to trusted_for() https://lwn.net/Articles/833417/ https://lwn.net/Articles/833417/ dxin <div class="FormattedComment"> &gt;There may be embedded systems where the user is not supposed to do arbitrary stuff<br> <p> That&#x27;s the entire Android ecosystem BTW, aka 90%+ of Linux users. I&#x27;m pretty sure the need for this comes from Android as it&#x27;s the only distro that has complete control over the entire FS. For any other distro that control is partially in the hands of admins and partially in the users&#x27;.<br> <p> The usecase is pretty clear IMO. The interpreter wants to know if it should proceed to execute a script. Until this feature there&#x27;s simply nowhere to ask. To take full advantage of this feature of course every interpreter that ships with the distro will be patched to disable execuating from stdin and respect what kernel returns from trusted_for().<br> <p> TBH it&#x27;s kind of a design mistake for an interpreter to not check for execute flag from the beginning.<br> </div> Sat, 03 Oct 2020 07:03:31 +0000 From O_MAYEXEC to trusted_for() https://lwn.net/Articles/833405/ https://lwn.net/Articles/833405/ martin.pitt <div class="FormattedComment"> I totally don&#x27;t see the point of this entire approach. As the author admits himself, this is super-easy to work around, and there is not even a well-defined goal. There may be embedded systems where the user is not supposed to do arbitrary stuff, but then disable login or bash/python/awk/sed/perl/etc. But this doesn&#x27;t work with standard distros obviously. <br> <p> If you want to lock down a system, protect the filesystem/OS *objects* with MAC like SELinux or AppArmor, and restrict resource usage with cgroups. That&#x27;s a proven and robust path. Whereas this is just dead weight and snake oil, sorry. <br> </div> Sat, 03 Oct 2020 04:38:11 +0000 From O_MAYEXEC to trusted_for() https://lwn.net/Articles/833361/ https://lwn.net/Articles/833361/ mwsealey <div class="FormattedComment"> <font class="QuotedText">&gt; If the operating system on which bash is running provides these special files, bash will use them; otherwise it will emulate them internally</font><br> <p> About time for someone to write a system-level /dev/tcp and /dev/udp... isn&#x27;t this something systemd-networkd should provide?<br> </div> Fri, 02 Oct 2020 15:30:31 +0000 From O_MAYEXEC to trusted_for() https://lwn.net/Articles/833311/ https://lwn.net/Articles/833311/ geert I thought so, too. Until I ran "man bash", and found: <pre> Bash handles several filenames specially when they are used in redirec‐ tions, as described in the following table. If the operating system on which bash is running provides these special files, bash will use them; otherwise it will emulate them internally with the behavior described below. /dev/tcp/host/port If host is a valid hostname or Internet address, and port is an integer port number or service name, bash attempts to open the corresponding TCP socket. /dev/udp/host/port If host is a valid hostname or Internet address, and port is an integer port number or service name, bash attempts to open the corresponding UDP socket. </pre> And it works! <pre> machineA$ nc -l -p 8000 machineB$ echo hello &gt; /dev/tcp/machineA/8000 </pre> Fri, 02 Oct 2020 12:40:35 +0000 From O_MAYEXEC to trusted_for() https://lwn.net/Articles/833310/ https://lwn.net/Articles/833310/ NAR <div class="FormattedComment"> The shell is less powerful than Python, e.g. as far as I know, you can&#x27;t open TCP connections to remote sites from the shell without using external programs like curl, wget, netcat, etc. <br> </div> Fri, 02 Oct 2020 11:41:56 +0000 From O_MAYEXEC to trusted_for() https://lwn.net/Articles/833298/ https://lwn.net/Articles/833298/ richiejp <div class="FormattedComment"> I mean -c not -s xD<br> </div> Fri, 02 Oct 2020 08:41:37 +0000 From O_MAYEXEC to trusted_for() https://lwn.net/Articles/833296/ https://lwn.net/Articles/833296/ richiejp <div class="FormattedComment"> Well the problem is not with -s in general, but using it when you could pass the file name instead. If you load the script contents into memory with `cat` and then pass it as an argument to Python with -s, Python can&#x27;t check the original file with `trusted_for`. It either has to assume the script is trusted, disable -s or sh/cat needs to check the permissions before passing the data to Python. I suppose there is the same issue with passing data on stdio, which is mentioned in the article.<br> <p> Also, on a partially related note, there was some buffer overflow or &quot;stack smashing&quot; attack involving large command lines and now the linux command line length is much more limited to prevent that, so you probably don&#x27;t want to use `-s` in shell scripts unless it is a string of known length generated in the script or static.<br> <p> BTW &quot;most people&quot; includes myself when I&#x27;m in a less trusted state.<br> </div> Fri, 02 Oct 2020 08:38:03 +0000 From O_MAYEXEC to trusted_for() https://lwn.net/Articles/833293/ https://lwn.net/Articles/833293/ zarak <div class="FormattedComment"> Can you elaborate the issue with `-c` in a script ? I must be part of &quot;most people&quot;.<br> </div> Fri, 02 Oct 2020 07:13:00 +0000 From O_MAYEXEC to trusted_for() https://lwn.net/Articles/833290/ https://lwn.net/Articles/833290/ richiejp <div class="FormattedComment"> Because most people won&#x27;t understand the security implications of using `-c` in a shell script. You have to put up barriers to people taking the insecure path.<br> </div> Fri, 02 Oct 2020 05:42:28 +0000 From O_MAYEXEC to trusted_for() https://lwn.net/Articles/833287/ https://lwn.net/Articles/833287/ comex <div class="FormattedComment"> I can think of two possibilities; both pretty marginal though:<br> <p> - Preventing attackers from pivoting from some limited or unstable form of ‘code execution’, like return-oriented programming after exploiting some C code, to a more flexible and stable Python script. But ROP is Turing complete, so there’s no real *need* to pivot in the first place; it’s just more convenient for an attacker.<br> <p> - Guarding against some kind of command injection vulnerability that involves arguments directly passed to execve() or similar rather than going through a shell. But that would be a very unusual vulnerability.<br> </div> Fri, 02 Oct 2020 05:15:34 +0000 From O_MAYEXEC to trusted_for() https://lwn.net/Articles/833274/ https://lwn.net/Articles/833274/ NYKevin <div class="FormattedComment"> <font class="QuotedText">&gt; What exactly even defines an interpreter though anyway? Or execution of code?</font><br> <p> My understanding of this entire effort is that these questions are fundamentally userspace&#x27;s problem. If the Python interpreter tells the kernel &quot;I want to execute this file,&quot; then that counts as &quot;executing&quot; that file. If the Python interpreter does not so indicate, then it doesn&#x27;t count. As far as the kernel is concerned, userspace is a black box that can call trusted_for() whenever it wants, and the kernel gives it answers based on security policies, in the same way as userspace can call flock() whenever it wants, and the kernel gives it answers based on previous calls to flock(). flock() is not enforced in any meaningful way; it&#x27;s up to userspace to decide what it means and when it ought to be called.<br> <p> In practice, I would imagine that it will be completely impractical for Python to audit all possible instances of code being evaluated at runtime, *and* tie those audit events back to an identifiable file or file descriptor (for example, if the user does exec(open(&quot;foo.py&quot;).read()), how does exec() know what file that string came from?). The former is already in PEP 578, but the latter does not seem possible from my understanding of how Python actually works. So instead, you basically have two options:<br> <p> 1. Some kind of &quot;restricted mode Python,&quot; which would disable most runtime code-evaluation altogether unless it can be directly tied to a file (but see <a href="https://www.python.org/dev/peps/pep-0578/#why-not-a-sandbox">https://www.python.org/dev/peps/pep-0578/#why-not-a-sandbox</a>).<br> 2. Put Python inside of a container, VM, or hypervisor, and use that as your security boundary instead of trying to lock down Python.<br> <p> IMHO #2 is by far the most straightforward approach to something like this. It does not require any changes to either Python or the kernel. But there are certainly other interpreted languages that are easier to lock down than Python, and defense in depth is a good idea anyway, so this is probably still worth doing regardless of factors specific to Python.<br> </div> Thu, 01 Oct 2020 21:58:55 +0000 From O_MAYEXEC to trusted_for() https://lwn.net/Articles/833268/ https://lwn.net/Articles/833268/ zev The cover letter of the patch series mentions things like modifying the python interpreter to disable <code>-c</code>, but if you can control a shell enough to attempt <code>python -c "$(cat foo.py)"</code> as a workaround for not being able to run <code>python foo.py</code> directly, why not just have the shell do your evil bidding itself? <br><br> What exactly even defines an interpreter though anyway? Or execution of code? As soon as you've got a data-dependent conditional branch, the line between data and code starts to melt into much more of a continuous spectrum. Thu, 01 Oct 2020 21:10:11 +0000