If the current user only has execute (--x) permissions on a file, under which user does the interpreter (specified by #!/path/to/interpreter at the beginning of the file) run?

It couldn't be the current user, because he doesn't have permission to read the file. It couldn't be root, because then arbitrary code included in the interpreter would gain root access.

As which user, then, does the interpreter process run?

Edit: I think my question assumes that the file has already been read enough to know which interpreter it specifies, when in reality it wouldn't get that far. The current shell (usually b/a/sh) interpreting the command to execute the target file would attempt to read it, and fail.

  • Current user. Executing a file doesn't really need read permission, as long as the interpreter support that. Although most script language doesn't support that, please clarify what interpreter you're using May 5, 2019 at 1:30
  • @炸鱼薯条德里克 it doesn't matter what interpreter it is. if it cannot read the file, it cannot "interpret" it either.
    – user313992
    May 5, 2019 at 1:34
  • If the interpreter is setuid root, it'll be able to read the file. As the other commenter posted, it may help to know what the concrete situation is.
    – ctt
    May 5, 2019 at 1:42
  • @ctt do you know of any interpreter which is installed as setuid or cap_dac_override+ep by default?
    – user313992
    May 5, 2019 at 1:47
  • 1
    No. bash will first try to run an executable script via the execve system call (which does not care about its reading perms), and only if that fails with ENOEXEC it will try to read it itself and run it as a script. Your assumptions from the Edit: part are wrong.
    – user313992
    May 5, 2019 at 2:07

3 Answers 3


If the user has no read permission on an executable script, then trying to run it will fail, unless she has the CAP_DAC_OVERRIDE capability (eg. she's root):

$ cat > yup; chmod 100 yup
#! /bin/sh
echo yup
$ ./yup
/bin/sh: 0: Can't open ./yup

The interpreter (whether failing or successful) will always run as the current user, ignoring any setuid bits or setcap extended attributes of the script.

Executable scripts are different from binaries in the fact that the interpreter should be able to open and read in order to run them. However, notice that they're simply passed as an argument to the interpreter, which may not try to read them at all, but do something completely different:

$ cat > interp; chmod 755 interp
#! /bin/sh
printf 'you said %s\n' "$1"
$ cat > script; chmod 100 script
#! ./interp
nothing to see here
$ ./script
you said ./script

Of course, the interpreter itself may be a setuid or cap_dac_override=ep-setcap binary (or pass down the script's path as an argument to such a binary), in which case it will run with elevated privileges and could ignore any file permissions.

Unreadable setuid scripts on Linux via binfmt_misc

On Linux you can bypass all the restrictions on executable scripts (and wreck your system ;-)) by using the binfmt_misc module:

As root:

# echo ':interp-test:M::#! ./interp::./interp:C' \
    > /proc/sys/fs/binfmt_misc/register

# cat > /tmp/script <<'EOT'; chmod 4001 /tmp/script # just exec + setuid
#! ./interp
id -u

As an ordinary user:

$ echo 'int main(void){ dup2(getauxval(AT_EXECFD), 0); execl("/bin/sh", "sh", "-p", (void*)0); }' |
    cc -include sys/auxv.h -include unistd.h -x c - -o ./interp
$ /tmp/script


More information in Documentation/admin-guide/binfmt-misc.rst in the kernel source.

The -p option may cause an error with some shells (where it could be simply dropped), but is needed with newer versions of dash and bash in order to prevent them from dropping privileges even if not asked for.


Executing a script works in two phases. First the kernel reads the beginning of the file and sees that it starts with #!, so it reads the shebang line and determines what interpreter to call. The kernel then transforms the original command line into the path to the interpreter, the option(s)¹ on the shebang line if any, the path to the file and the original options. It then executes this command line, mostly as if this had been the command line all along, but without doing any further shebang processing.

So far, the kernel has checked that the caller has execute permission over the script file. Read permission has not come into play. The kernel reads the beginning of the file as part of executing it, so this is controlled by the execute permission, not by the read permission.

In the second phase, the interpreter is executed. Since it sees the script file name on its command line, it will presumably try to open it. This is the point at which read permission is necessary. If the interpreter doesn't have the permission to read the file, its open call will fail, and then presumably the interpreter will print an error message and give up. I say “presumably” because this is what every sensible interpreter does, but there is no technical obligation that things happen this way. If you use a program that isn't an interpreter on the shebang line, it doesn't make any difference in the first phase, but the program will do whatever it does in the second phase. For example, a “script” starting with #!/bin/echo will just print the script file name and any additional command line argument, then exit, and the script only needs to be executable for that.

¹ Many kernels, including Linux, only allow one option.


In general, scripts can not be executed without "r" permission, even if you own the file

$ ls -l tst
---x--x--x 1 sweh sweh 24 May  4 21:22 tst*

$ ./tst
/bin/bash: ./tst: Permission denied

$ sudo cat tst

echo hello

With your edited question.

The #! part of the program is interpreted by the kernel as part of the exec() system call. So to get that far the script doesn't need to be readable.

Effectively what happens, in my example, is that the kernel converts my ./tst into a /bin/bash ./tst call.

This conversion explains why scripts need to have r access to be processed, but the kernel just needs x to determine the interpreter to be used.

  • I understand kenel just ignore permission check to determine the interpreter to be used(without x permission just cause EACCES at the first place), but does this conversion always happen when process execve a file-with-shebang? Can binfmt_misc overwrite this behavior? May 5, 2019 at 3:09

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .