Can scripts run even when they are not set as executable?

Let's say you have the file myscript containing the following:

#!/bin/bash
echo "Hello, World!"

If you make this file executable and run it with ./myscript, then the kernel will see that the first two bytes are #!, which means it's a script-file. The kernel will then use the rest of the line as the interpreter, and pass the file as its first argument. So, it runs:

/bin/bash myscript

and bash reads the file and executes the commands it contains.

Thus, for bash (or whatever interpreter your script requires) to "execute" the script, it only needs to be able to read the file.

So, for scripts, the execute bit just makes it a bit more convenient to execute it. As long as bash is executable, you can always run bash with the script file as argument, or run bash interactively and copy paste the script line by line into your terminal to have the commands executed.


Make sure you are not confusing "executing the shell script" with "run a shell script using sh".

This will not be affected by file permissions on file.sh:

sh file.sh

You are executing sh (which resolves to the program /bin/sh), which reads file.sh and executes it's code.

File permissions will have effect if you really execute the script itself :

./file.sh

Note that file permissions are not supported by non-Linux filesystems, like FAT. So even if you run chmod -x file.sh, the file will still have it's former permissions.

Execute permission is enforced by the filesystem. But programs can "execute" the code too by reading the file contents, which bypasses filesystem permissions on "execute".


The exec syscall of the Linux kernel fails with EACCES if the file is not executable

While you can do sh myprog.sh (which just reads the files and interprets is), trying to run the program as ./myprog.sh cannot work, since when you do that:

  • your Bash shell on the terminal uses the exec system call on ./myprog.sh
  • the shebangs are interpreted directly by the exec system call of the Linux kernel as explained at: https://stackoverflow.com/questions/2429511/why-do-people-write-the-usr-bin-env-python-shebang-on-the-first-line-of-a-pyt/40938801#40938801
  • the Linux kernel refuses to execute files without executable permission

This can be verified with main.c:

#define _XOPEN_SOURCE 700
#include <errno.h>
#include <stdio.h>
#include <unistd.h>

int main(void) {
    char *argv[] = {"myprog", NULL};
    char *envp[] = {NULL};
    int ret;
    ret = execve("myprog.sh", argv, envp);
    perror("execve");
    printf("%d\n", errno);
    printf("%d\n", EACCES);
}

and myprog.sh:

#!/bin/sh
echo worked

If myprog.sh is not executable, main fails with:

execve: Permission denied
13
13

Tested in Ubuntu 17.10, gcc -std=c99.

POSIX 7 mentions that at:

The exec functions, except for fexecve(), shall fail if:

[EACCES] Search permission is denied for a directory listed in the new process image file's path prefix, or the new process image file denies execution permission.

Further rationale can be found at: https://security.stackexchange.com/questions/66550/unix-execute-permission-can-be-easily-bypassed-is-it-superfluous-or-whats-the

Tags:

Scripts