Executing a bash script or a c binary on a file system with noexec option
Can anyone explain in details what is going on with the following. Let's imagine I am mounting a directory with noexec
option as follows:
mount -o noexec /dev/mapper/fedora-data /data
So to verify this I ran mount | grep data
:
/dev/mapper/fedora-data on /data type ext4 (rw,noexec,relatime,seclabel,data=ordered)
Now within /data
I'm creating a simple script called hello_world
as follows:
#!/bin/bash
echo "Hello World"
whoami
So I made the script executable by chmod u+x hello_world
(this will however have no effect on a file system with noexec
options) and I tried running it:
# ./hello_world
-bash: ./hello_world: Permission denied
However, prepanding bash
to the file yields to:
# bash hello_world
Hello World
root
So then I created a simple hello_world.c
with the following contents:
#include <stdio.h>
int main()
{
printf("Hello Worldn");
return 0;
}
Compiled it using cc -o hello_world.c hello_world
Now running:
# ./hello_world
-bash: ./hello_world: Permission denied
So I tried to run it using
/lib64/ld-linux-x86-64.so.2 hello_world
The error:
./hello_world: error while loading shared libraries: ./hello_world: failed to map segment from shared object: Operation not permitted
So this is of course true since ldd
returns the following:
ldd hello_world
ldd: warning: you do not have execution permission for `./hello_world'
not a dynamic executable
On another system where noexec
mount option doesn't apply I see:
ldd hellow_world
linux-vdso.so.1 (0x00007ffc1c127000)
libc.so.6 => /lib64/libc.so.6 (0x00007facd9d5a000)
/lib64/ld-linux-x86-64.so.2 (0x00007facd9f3e000)
Now my question is this: Why does running a bash script on a file system with noexec
option work but not a c
compiled program? What is happening under the hood?
linux bash fedora filesystems c
add a comment |
Can anyone explain in details what is going on with the following. Let's imagine I am mounting a directory with noexec
option as follows:
mount -o noexec /dev/mapper/fedora-data /data
So to verify this I ran mount | grep data
:
/dev/mapper/fedora-data on /data type ext4 (rw,noexec,relatime,seclabel,data=ordered)
Now within /data
I'm creating a simple script called hello_world
as follows:
#!/bin/bash
echo "Hello World"
whoami
So I made the script executable by chmod u+x hello_world
(this will however have no effect on a file system with noexec
options) and I tried running it:
# ./hello_world
-bash: ./hello_world: Permission denied
However, prepanding bash
to the file yields to:
# bash hello_world
Hello World
root
So then I created a simple hello_world.c
with the following contents:
#include <stdio.h>
int main()
{
printf("Hello Worldn");
return 0;
}
Compiled it using cc -o hello_world.c hello_world
Now running:
# ./hello_world
-bash: ./hello_world: Permission denied
So I tried to run it using
/lib64/ld-linux-x86-64.so.2 hello_world
The error:
./hello_world: error while loading shared libraries: ./hello_world: failed to map segment from shared object: Operation not permitted
So this is of course true since ldd
returns the following:
ldd hello_world
ldd: warning: you do not have execution permission for `./hello_world'
not a dynamic executable
On another system where noexec
mount option doesn't apply I see:
ldd hellow_world
linux-vdso.so.1 (0x00007ffc1c127000)
libc.so.6 => /lib64/libc.so.6 (0x00007facd9d5a000)
/lib64/ld-linux-x86-64.so.2 (0x00007facd9f3e000)
Now my question is this: Why does running a bash script on a file system with noexec
option work but not a c
compiled program? What is happening under the hood?
linux bash fedora filesystems c
note:chmod o+x «filename»
will not give execute permission to file owning user, or group. Usechmod ugo+x «filename»
orchmod +x «filename»
– ctrl-alt-delor
Dec 22 '18 at 11:54
You are right. I usedchmod u+x hello_world
but forgot to change that in the question. Thanks for pointing that out.
– Valentin Bajrami
Dec 22 '18 at 12:13
add a comment |
Can anyone explain in details what is going on with the following. Let's imagine I am mounting a directory with noexec
option as follows:
mount -o noexec /dev/mapper/fedora-data /data
So to verify this I ran mount | grep data
:
/dev/mapper/fedora-data on /data type ext4 (rw,noexec,relatime,seclabel,data=ordered)
Now within /data
I'm creating a simple script called hello_world
as follows:
#!/bin/bash
echo "Hello World"
whoami
So I made the script executable by chmod u+x hello_world
(this will however have no effect on a file system with noexec
options) and I tried running it:
# ./hello_world
-bash: ./hello_world: Permission denied
However, prepanding bash
to the file yields to:
# bash hello_world
Hello World
root
So then I created a simple hello_world.c
with the following contents:
#include <stdio.h>
int main()
{
printf("Hello Worldn");
return 0;
}
Compiled it using cc -o hello_world.c hello_world
Now running:
# ./hello_world
-bash: ./hello_world: Permission denied
So I tried to run it using
/lib64/ld-linux-x86-64.so.2 hello_world
The error:
./hello_world: error while loading shared libraries: ./hello_world: failed to map segment from shared object: Operation not permitted
So this is of course true since ldd
returns the following:
ldd hello_world
ldd: warning: you do not have execution permission for `./hello_world'
not a dynamic executable
On another system where noexec
mount option doesn't apply I see:
ldd hellow_world
linux-vdso.so.1 (0x00007ffc1c127000)
libc.so.6 => /lib64/libc.so.6 (0x00007facd9d5a000)
/lib64/ld-linux-x86-64.so.2 (0x00007facd9f3e000)
Now my question is this: Why does running a bash script on a file system with noexec
option work but not a c
compiled program? What is happening under the hood?
linux bash fedora filesystems c
Can anyone explain in details what is going on with the following. Let's imagine I am mounting a directory with noexec
option as follows:
mount -o noexec /dev/mapper/fedora-data /data
So to verify this I ran mount | grep data
:
/dev/mapper/fedora-data on /data type ext4 (rw,noexec,relatime,seclabel,data=ordered)
Now within /data
I'm creating a simple script called hello_world
as follows:
#!/bin/bash
echo "Hello World"
whoami
So I made the script executable by chmod u+x hello_world
(this will however have no effect on a file system with noexec
options) and I tried running it:
# ./hello_world
-bash: ./hello_world: Permission denied
However, prepanding bash
to the file yields to:
# bash hello_world
Hello World
root
So then I created a simple hello_world.c
with the following contents:
#include <stdio.h>
int main()
{
printf("Hello Worldn");
return 0;
}
Compiled it using cc -o hello_world.c hello_world
Now running:
# ./hello_world
-bash: ./hello_world: Permission denied
So I tried to run it using
/lib64/ld-linux-x86-64.so.2 hello_world
The error:
./hello_world: error while loading shared libraries: ./hello_world: failed to map segment from shared object: Operation not permitted
So this is of course true since ldd
returns the following:
ldd hello_world
ldd: warning: you do not have execution permission for `./hello_world'
not a dynamic executable
On another system where noexec
mount option doesn't apply I see:
ldd hellow_world
linux-vdso.so.1 (0x00007ffc1c127000)
libc.so.6 => /lib64/libc.so.6 (0x00007facd9d5a000)
/lib64/ld-linux-x86-64.so.2 (0x00007facd9f3e000)
Now my question is this: Why does running a bash script on a file system with noexec
option work but not a c
compiled program? What is happening under the hood?
linux bash fedora filesystems c
linux bash fedora filesystems c
edited Dec 22 '18 at 12:14
asked Dec 21 '18 at 21:33
Valentin Bajrami
5,90611627
5,90611627
note:chmod o+x «filename»
will not give execute permission to file owning user, or group. Usechmod ugo+x «filename»
orchmod +x «filename»
– ctrl-alt-delor
Dec 22 '18 at 11:54
You are right. I usedchmod u+x hello_world
but forgot to change that in the question. Thanks for pointing that out.
– Valentin Bajrami
Dec 22 '18 at 12:13
add a comment |
note:chmod o+x «filename»
will not give execute permission to file owning user, or group. Usechmod ugo+x «filename»
orchmod +x «filename»
– ctrl-alt-delor
Dec 22 '18 at 11:54
You are right. I usedchmod u+x hello_world
but forgot to change that in the question. Thanks for pointing that out.
– Valentin Bajrami
Dec 22 '18 at 12:13
note:
chmod o+x «filename»
will not give execute permission to file owning user, or group. Use chmod ugo+x «filename»
or chmod +x «filename»
– ctrl-alt-delor
Dec 22 '18 at 11:54
note:
chmod o+x «filename»
will not give execute permission to file owning user, or group. Use chmod ugo+x «filename»
or chmod +x «filename»
– ctrl-alt-delor
Dec 22 '18 at 11:54
You are right. I used
chmod u+x hello_world
but forgot to change that in the question. Thanks for pointing that out.– Valentin Bajrami
Dec 22 '18 at 12:13
You are right. I used
chmod u+x hello_world
but forgot to change that in the question. Thanks for pointing that out.– Valentin Bajrami
Dec 22 '18 at 12:13
add a comment |
5 Answers
5
active
oldest
votes
What's happening in both cases is the same: to execute a file directly, the execute bit needs to be set, and the filesystem can't be mounted noexec. But these things don't stop anything from reading those files.
When the bash script is run as ./hello_world
and the file isn't executable (either no exec permission bit, or noexec on the filesystem), the #!
line isn't even checked, because the system doesn't even load the file. The script is never "executed" in the relevant sense.
In the case of bash ./hello_world
, well, The noexec filesystem option just plain isn't as smart as you'd like it to be. The bash
command that's run is /bin/bash
, and /bin
isn't on a filesystem with noexec
. So, it runs no problem. The system doesn't care that bash (or python or perl or whatever) is an interpreter. It just runs the command you gave (/bin/bash
) with the argument which happens to be a file. In the case of bash or another shell, that file contains a list of commands to execute, but now we're "past" anything that's going to check file execute bits. That check isn't responsible for what happens later.
Consider this case:
$ cat hello_world | /bin/bash
… or for those who do not like Pointless Use of Cat:
$ /bin/bash < hello_world
The "shbang" #!
sequence at the beginning of a file is just some nice magic for doing effectively the same thing when you try to execute the file as a command. You might find this LWN.net article helpful: How programs get run.
Thank you for your detailed explenation and the link to LWN.net article is very helfull!
– Valentin Bajrami
Dec 22 '18 at 8:13
1
If my guess is correct, what also plays a role is that/bin/bash
is stored on filesystem withoutnoexec
, whereas OP's script is onnoexec
filesystem.
– Sergiy Kolodyazhnyy
Dec 22 '18 at 23:37
@SergiyKolodyazhnyy Yes.
– mattdm
Dec 23 '18 at 2:25
add a comment |
Previous answers explain why the noexec
setting doesn't prevent a script from being run when the interpreter (in your case /bin/bash
) is explicitly called from the command line. But if that was all there was to it, this command would have worked as well:
/lib64/ld-linux-x86-64.so.2 hello_world
And as you noted that doesn't work. That's because noexec
has another effect as well. The kernel will not allow memory mapped files from that file system with PROT_EXEC
enabled.
Memory mapped files are used in multiple scenarios. The two most common scenarios are for executables and libraries. When a program is started using the execve
system call, the kernel will internally create memory mappings for the linker and executable. Any other libraries needed are memory mapped by the linker through the mmap
system call with PROT_EXEC
enabled. If you tried to use a library from a filesystem with noexec
the kernel would refuse to do the mmap
call.
When you invoked /lib64/ld-linux-x86-64.so.2 hello_world
the execve
system call will only create a memory mapping for the linker and the linker will open the hello_world
executable and attempt to create a memory mapping in pretty much the same way it would have done for a library. And this is the point at which the kernel refuse to perform the mmap
call and you get the error:
./hello_world: error while loading shared libraries: ./hello_world: failed to map segment from shared object: Operation not permitted
The noexec
setting still allows memory mappings without execute permission (as is sometimes used for data files) and it also allows normal reading of files which is why bash hello_world
worked for you.
1
Thanks! Very helpful!
– Valentin Bajrami
Dec 22 '18 at 12:19
add a comment |
Executing command on this way:
bash hello_world
you make bash
read from file hello_world
(which is not forbidden).
In other cases OS try to run this file hello_world
and fail because of noexec
flag
Well the kernel will check the shebang to decide what instructions to expect/run. So running./hello_world
would mean the same right or am I missing the point here?
– Valentin Bajrami
Dec 21 '18 at 21:55
@ValentinBajrami, no, they execute on different way. Think about no execution flag on the file.
– Romeo Ninov
Dec 21 '18 at 21:57
add a comment |
When you run the script via bash It just reads in the file and interprets it.
However when you pass the name to the kernel -- it really inspects the file for "#!" and loads the interpreter specified according to the kernel option "CONFIG_BINFMT_SCRIPT".
It says:
Say Y here if you want to execute interpreted scripts starting with
"#!" followed by the path to an interpreter.
You can build this support as a module; however, until that module
gets loaded, you cannot run scripts. Thus, if you want to load this
module from an initramfs, the portion of the initramfs before loading
this module must consist of compiled binaries only.
Most systems will not boot if you say M or N here. If unsure, say Y.
The above is the help text associated with this option. For another interesting
difference. I wrote my script:
> cat myprog.sh
#!/bin/cat
echo "Hello World"
> chmod +x myprog.sh
Running it with bash still runs bash's interpreter:
> bash myprog.sh
Hello World
However the kernel now does:
> myprog.sh
#!/bin/cat
echo "Hello World"
The kernel printed out the script including the 1st line because it called
'cat'.
In the case of the C program , you aren't calling an interpreter to
run the binary. The kernel tries to run it directly.
Still if you loaded all of your executable into memory using some
debuggers, you could still "run" your program as it's being loaded
via the debugger.
The 'noexec' option is like turning off the execute bit on your binary and
disables the kernel from running the binary "natively".
This does make a difference, BTW, if your program had a SetUID bit
set on the program -- loading it with an interpreter won't set your
UID, only when the kernel loads it can that privilege be enabled.
FWIW -- windows has the same type of mechanism.
If you add ".sh" as a "executable suffix" like ".exe" or ".vbs", windows
will automatically run your file according to how you have setup
".sh" files to be executed. Theoretically, you could setup
".txt" files to automatically be typed out if you entered their name
on the command line.
Similarly you could put some short call to a program to print out text
files to the screen. That's a reason not to leave your self logged in
at a public location.
add a comment |
Because the bash executable doesn't reside on said filesystem.
2
That is true but running./hello_world
would also us/bin/bash
which doesn't reside on a file system withnoexec
option mounted. If that is true then thec
program would run as well corerct?
– Valentin Bajrami
Dec 21 '18 at 21:56
1
Nope. That's not the way it works.
– tink
Dec 21 '18 at 21:58
2
@ValentinBajrami The shebang line in the script isn't read until after the noexec and execute-bit hurdles have been cleared.
– Blrfl
Dec 22 '18 at 12:38
add a comment |
Your Answer
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "106"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f490402%2fexecuting-a-bash-script-or-a-c-binary-on-a-file-system-with-noexec-option%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
5 Answers
5
active
oldest
votes
5 Answers
5
active
oldest
votes
active
oldest
votes
active
oldest
votes
What's happening in both cases is the same: to execute a file directly, the execute bit needs to be set, and the filesystem can't be mounted noexec. But these things don't stop anything from reading those files.
When the bash script is run as ./hello_world
and the file isn't executable (either no exec permission bit, or noexec on the filesystem), the #!
line isn't even checked, because the system doesn't even load the file. The script is never "executed" in the relevant sense.
In the case of bash ./hello_world
, well, The noexec filesystem option just plain isn't as smart as you'd like it to be. The bash
command that's run is /bin/bash
, and /bin
isn't on a filesystem with noexec
. So, it runs no problem. The system doesn't care that bash (or python or perl or whatever) is an interpreter. It just runs the command you gave (/bin/bash
) with the argument which happens to be a file. In the case of bash or another shell, that file contains a list of commands to execute, but now we're "past" anything that's going to check file execute bits. That check isn't responsible for what happens later.
Consider this case:
$ cat hello_world | /bin/bash
… or for those who do not like Pointless Use of Cat:
$ /bin/bash < hello_world
The "shbang" #!
sequence at the beginning of a file is just some nice magic for doing effectively the same thing when you try to execute the file as a command. You might find this LWN.net article helpful: How programs get run.
Thank you for your detailed explenation and the link to LWN.net article is very helfull!
– Valentin Bajrami
Dec 22 '18 at 8:13
1
If my guess is correct, what also plays a role is that/bin/bash
is stored on filesystem withoutnoexec
, whereas OP's script is onnoexec
filesystem.
– Sergiy Kolodyazhnyy
Dec 22 '18 at 23:37
@SergiyKolodyazhnyy Yes.
– mattdm
Dec 23 '18 at 2:25
add a comment |
What's happening in both cases is the same: to execute a file directly, the execute bit needs to be set, and the filesystem can't be mounted noexec. But these things don't stop anything from reading those files.
When the bash script is run as ./hello_world
and the file isn't executable (either no exec permission bit, or noexec on the filesystem), the #!
line isn't even checked, because the system doesn't even load the file. The script is never "executed" in the relevant sense.
In the case of bash ./hello_world
, well, The noexec filesystem option just plain isn't as smart as you'd like it to be. The bash
command that's run is /bin/bash
, and /bin
isn't on a filesystem with noexec
. So, it runs no problem. The system doesn't care that bash (or python or perl or whatever) is an interpreter. It just runs the command you gave (/bin/bash
) with the argument which happens to be a file. In the case of bash or another shell, that file contains a list of commands to execute, but now we're "past" anything that's going to check file execute bits. That check isn't responsible for what happens later.
Consider this case:
$ cat hello_world | /bin/bash
… or for those who do not like Pointless Use of Cat:
$ /bin/bash < hello_world
The "shbang" #!
sequence at the beginning of a file is just some nice magic for doing effectively the same thing when you try to execute the file as a command. You might find this LWN.net article helpful: How programs get run.
Thank you for your detailed explenation and the link to LWN.net article is very helfull!
– Valentin Bajrami
Dec 22 '18 at 8:13
1
If my guess is correct, what also plays a role is that/bin/bash
is stored on filesystem withoutnoexec
, whereas OP's script is onnoexec
filesystem.
– Sergiy Kolodyazhnyy
Dec 22 '18 at 23:37
@SergiyKolodyazhnyy Yes.
– mattdm
Dec 23 '18 at 2:25
add a comment |
What's happening in both cases is the same: to execute a file directly, the execute bit needs to be set, and the filesystem can't be mounted noexec. But these things don't stop anything from reading those files.
When the bash script is run as ./hello_world
and the file isn't executable (either no exec permission bit, or noexec on the filesystem), the #!
line isn't even checked, because the system doesn't even load the file. The script is never "executed" in the relevant sense.
In the case of bash ./hello_world
, well, The noexec filesystem option just plain isn't as smart as you'd like it to be. The bash
command that's run is /bin/bash
, and /bin
isn't on a filesystem with noexec
. So, it runs no problem. The system doesn't care that bash (or python or perl or whatever) is an interpreter. It just runs the command you gave (/bin/bash
) with the argument which happens to be a file. In the case of bash or another shell, that file contains a list of commands to execute, but now we're "past" anything that's going to check file execute bits. That check isn't responsible for what happens later.
Consider this case:
$ cat hello_world | /bin/bash
… or for those who do not like Pointless Use of Cat:
$ /bin/bash < hello_world
The "shbang" #!
sequence at the beginning of a file is just some nice magic for doing effectively the same thing when you try to execute the file as a command. You might find this LWN.net article helpful: How programs get run.
What's happening in both cases is the same: to execute a file directly, the execute bit needs to be set, and the filesystem can't be mounted noexec. But these things don't stop anything from reading those files.
When the bash script is run as ./hello_world
and the file isn't executable (either no exec permission bit, or noexec on the filesystem), the #!
line isn't even checked, because the system doesn't even load the file. The script is never "executed" in the relevant sense.
In the case of bash ./hello_world
, well, The noexec filesystem option just plain isn't as smart as you'd like it to be. The bash
command that's run is /bin/bash
, and /bin
isn't on a filesystem with noexec
. So, it runs no problem. The system doesn't care that bash (or python or perl or whatever) is an interpreter. It just runs the command you gave (/bin/bash
) with the argument which happens to be a file. In the case of bash or another shell, that file contains a list of commands to execute, but now we're "past" anything that's going to check file execute bits. That check isn't responsible for what happens later.
Consider this case:
$ cat hello_world | /bin/bash
… or for those who do not like Pointless Use of Cat:
$ /bin/bash < hello_world
The "shbang" #!
sequence at the beginning of a file is just some nice magic for doing effectively the same thing when you try to execute the file as a command. You might find this LWN.net article helpful: How programs get run.
edited Dec 23 '18 at 2:27
answered Dec 21 '18 at 22:07
mattdm
28.2k1172112
28.2k1172112
Thank you for your detailed explenation and the link to LWN.net article is very helfull!
– Valentin Bajrami
Dec 22 '18 at 8:13
1
If my guess is correct, what also plays a role is that/bin/bash
is stored on filesystem withoutnoexec
, whereas OP's script is onnoexec
filesystem.
– Sergiy Kolodyazhnyy
Dec 22 '18 at 23:37
@SergiyKolodyazhnyy Yes.
– mattdm
Dec 23 '18 at 2:25
add a comment |
Thank you for your detailed explenation and the link to LWN.net article is very helfull!
– Valentin Bajrami
Dec 22 '18 at 8:13
1
If my guess is correct, what also plays a role is that/bin/bash
is stored on filesystem withoutnoexec
, whereas OP's script is onnoexec
filesystem.
– Sergiy Kolodyazhnyy
Dec 22 '18 at 23:37
@SergiyKolodyazhnyy Yes.
– mattdm
Dec 23 '18 at 2:25
Thank you for your detailed explenation and the link to LWN.net article is very helfull!
– Valentin Bajrami
Dec 22 '18 at 8:13
Thank you for your detailed explenation and the link to LWN.net article is very helfull!
– Valentin Bajrami
Dec 22 '18 at 8:13
1
1
If my guess is correct, what also plays a role is that
/bin/bash
is stored on filesystem without noexec
, whereas OP's script is on noexec
filesystem.– Sergiy Kolodyazhnyy
Dec 22 '18 at 23:37
If my guess is correct, what also plays a role is that
/bin/bash
is stored on filesystem without noexec
, whereas OP's script is on noexec
filesystem.– Sergiy Kolodyazhnyy
Dec 22 '18 at 23:37
@SergiyKolodyazhnyy Yes.
– mattdm
Dec 23 '18 at 2:25
@SergiyKolodyazhnyy Yes.
– mattdm
Dec 23 '18 at 2:25
add a comment |
Previous answers explain why the noexec
setting doesn't prevent a script from being run when the interpreter (in your case /bin/bash
) is explicitly called from the command line. But if that was all there was to it, this command would have worked as well:
/lib64/ld-linux-x86-64.so.2 hello_world
And as you noted that doesn't work. That's because noexec
has another effect as well. The kernel will not allow memory mapped files from that file system with PROT_EXEC
enabled.
Memory mapped files are used in multiple scenarios. The two most common scenarios are for executables and libraries. When a program is started using the execve
system call, the kernel will internally create memory mappings for the linker and executable. Any other libraries needed are memory mapped by the linker through the mmap
system call with PROT_EXEC
enabled. If you tried to use a library from a filesystem with noexec
the kernel would refuse to do the mmap
call.
When you invoked /lib64/ld-linux-x86-64.so.2 hello_world
the execve
system call will only create a memory mapping for the linker and the linker will open the hello_world
executable and attempt to create a memory mapping in pretty much the same way it would have done for a library. And this is the point at which the kernel refuse to perform the mmap
call and you get the error:
./hello_world: error while loading shared libraries: ./hello_world: failed to map segment from shared object: Operation not permitted
The noexec
setting still allows memory mappings without execute permission (as is sometimes used for data files) and it also allows normal reading of files which is why bash hello_world
worked for you.
1
Thanks! Very helpful!
– Valentin Bajrami
Dec 22 '18 at 12:19
add a comment |
Previous answers explain why the noexec
setting doesn't prevent a script from being run when the interpreter (in your case /bin/bash
) is explicitly called from the command line. But if that was all there was to it, this command would have worked as well:
/lib64/ld-linux-x86-64.so.2 hello_world
And as you noted that doesn't work. That's because noexec
has another effect as well. The kernel will not allow memory mapped files from that file system with PROT_EXEC
enabled.
Memory mapped files are used in multiple scenarios. The two most common scenarios are for executables and libraries. When a program is started using the execve
system call, the kernel will internally create memory mappings for the linker and executable. Any other libraries needed are memory mapped by the linker through the mmap
system call with PROT_EXEC
enabled. If you tried to use a library from a filesystem with noexec
the kernel would refuse to do the mmap
call.
When you invoked /lib64/ld-linux-x86-64.so.2 hello_world
the execve
system call will only create a memory mapping for the linker and the linker will open the hello_world
executable and attempt to create a memory mapping in pretty much the same way it would have done for a library. And this is the point at which the kernel refuse to perform the mmap
call and you get the error:
./hello_world: error while loading shared libraries: ./hello_world: failed to map segment from shared object: Operation not permitted
The noexec
setting still allows memory mappings without execute permission (as is sometimes used for data files) and it also allows normal reading of files which is why bash hello_world
worked for you.
1
Thanks! Very helpful!
– Valentin Bajrami
Dec 22 '18 at 12:19
add a comment |
Previous answers explain why the noexec
setting doesn't prevent a script from being run when the interpreter (in your case /bin/bash
) is explicitly called from the command line. But if that was all there was to it, this command would have worked as well:
/lib64/ld-linux-x86-64.so.2 hello_world
And as you noted that doesn't work. That's because noexec
has another effect as well. The kernel will not allow memory mapped files from that file system with PROT_EXEC
enabled.
Memory mapped files are used in multiple scenarios. The two most common scenarios are for executables and libraries. When a program is started using the execve
system call, the kernel will internally create memory mappings for the linker and executable. Any other libraries needed are memory mapped by the linker through the mmap
system call with PROT_EXEC
enabled. If you tried to use a library from a filesystem with noexec
the kernel would refuse to do the mmap
call.
When you invoked /lib64/ld-linux-x86-64.so.2 hello_world
the execve
system call will only create a memory mapping for the linker and the linker will open the hello_world
executable and attempt to create a memory mapping in pretty much the same way it would have done for a library. And this is the point at which the kernel refuse to perform the mmap
call and you get the error:
./hello_world: error while loading shared libraries: ./hello_world: failed to map segment from shared object: Operation not permitted
The noexec
setting still allows memory mappings without execute permission (as is sometimes used for data files) and it also allows normal reading of files which is why bash hello_world
worked for you.
Previous answers explain why the noexec
setting doesn't prevent a script from being run when the interpreter (in your case /bin/bash
) is explicitly called from the command line. But if that was all there was to it, this command would have worked as well:
/lib64/ld-linux-x86-64.so.2 hello_world
And as you noted that doesn't work. That's because noexec
has another effect as well. The kernel will not allow memory mapped files from that file system with PROT_EXEC
enabled.
Memory mapped files are used in multiple scenarios. The two most common scenarios are for executables and libraries. When a program is started using the execve
system call, the kernel will internally create memory mappings for the linker and executable. Any other libraries needed are memory mapped by the linker through the mmap
system call with PROT_EXEC
enabled. If you tried to use a library from a filesystem with noexec
the kernel would refuse to do the mmap
call.
When you invoked /lib64/ld-linux-x86-64.so.2 hello_world
the execve
system call will only create a memory mapping for the linker and the linker will open the hello_world
executable and attempt to create a memory mapping in pretty much the same way it would have done for a library. And this is the point at which the kernel refuse to perform the mmap
call and you get the error:
./hello_world: error while loading shared libraries: ./hello_world: failed to map segment from shared object: Operation not permitted
The noexec
setting still allows memory mappings without execute permission (as is sometimes used for data files) and it also allows normal reading of files which is why bash hello_world
worked for you.
answered Dec 22 '18 at 12:15
kasperd
2,27911027
2,27911027
1
Thanks! Very helpful!
– Valentin Bajrami
Dec 22 '18 at 12:19
add a comment |
1
Thanks! Very helpful!
– Valentin Bajrami
Dec 22 '18 at 12:19
1
1
Thanks! Very helpful!
– Valentin Bajrami
Dec 22 '18 at 12:19
Thanks! Very helpful!
– Valentin Bajrami
Dec 22 '18 at 12:19
add a comment |
Executing command on this way:
bash hello_world
you make bash
read from file hello_world
(which is not forbidden).
In other cases OS try to run this file hello_world
and fail because of noexec
flag
Well the kernel will check the shebang to decide what instructions to expect/run. So running./hello_world
would mean the same right or am I missing the point here?
– Valentin Bajrami
Dec 21 '18 at 21:55
@ValentinBajrami, no, they execute on different way. Think about no execution flag on the file.
– Romeo Ninov
Dec 21 '18 at 21:57
add a comment |
Executing command on this way:
bash hello_world
you make bash
read from file hello_world
(which is not forbidden).
In other cases OS try to run this file hello_world
and fail because of noexec
flag
Well the kernel will check the shebang to decide what instructions to expect/run. So running./hello_world
would mean the same right or am I missing the point here?
– Valentin Bajrami
Dec 21 '18 at 21:55
@ValentinBajrami, no, they execute on different way. Think about no execution flag on the file.
– Romeo Ninov
Dec 21 '18 at 21:57
add a comment |
Executing command on this way:
bash hello_world
you make bash
read from file hello_world
(which is not forbidden).
In other cases OS try to run this file hello_world
and fail because of noexec
flag
Executing command on this way:
bash hello_world
you make bash
read from file hello_world
(which is not forbidden).
In other cases OS try to run this file hello_world
and fail because of noexec
flag
answered Dec 21 '18 at 21:44
Romeo Ninov
5,21231827
5,21231827
Well the kernel will check the shebang to decide what instructions to expect/run. So running./hello_world
would mean the same right or am I missing the point here?
– Valentin Bajrami
Dec 21 '18 at 21:55
@ValentinBajrami, no, they execute on different way. Think about no execution flag on the file.
– Romeo Ninov
Dec 21 '18 at 21:57
add a comment |
Well the kernel will check the shebang to decide what instructions to expect/run. So running./hello_world
would mean the same right or am I missing the point here?
– Valentin Bajrami
Dec 21 '18 at 21:55
@ValentinBajrami, no, they execute on different way. Think about no execution flag on the file.
– Romeo Ninov
Dec 21 '18 at 21:57
Well the kernel will check the shebang to decide what instructions to expect/run. So running
./hello_world
would mean the same right or am I missing the point here?– Valentin Bajrami
Dec 21 '18 at 21:55
Well the kernel will check the shebang to decide what instructions to expect/run. So running
./hello_world
would mean the same right or am I missing the point here?– Valentin Bajrami
Dec 21 '18 at 21:55
@ValentinBajrami, no, they execute on different way. Think about no execution flag on the file.
– Romeo Ninov
Dec 21 '18 at 21:57
@ValentinBajrami, no, they execute on different way. Think about no execution flag on the file.
– Romeo Ninov
Dec 21 '18 at 21:57
add a comment |
When you run the script via bash It just reads in the file and interprets it.
However when you pass the name to the kernel -- it really inspects the file for "#!" and loads the interpreter specified according to the kernel option "CONFIG_BINFMT_SCRIPT".
It says:
Say Y here if you want to execute interpreted scripts starting with
"#!" followed by the path to an interpreter.
You can build this support as a module; however, until that module
gets loaded, you cannot run scripts. Thus, if you want to load this
module from an initramfs, the portion of the initramfs before loading
this module must consist of compiled binaries only.
Most systems will not boot if you say M or N here. If unsure, say Y.
The above is the help text associated with this option. For another interesting
difference. I wrote my script:
> cat myprog.sh
#!/bin/cat
echo "Hello World"
> chmod +x myprog.sh
Running it with bash still runs bash's interpreter:
> bash myprog.sh
Hello World
However the kernel now does:
> myprog.sh
#!/bin/cat
echo "Hello World"
The kernel printed out the script including the 1st line because it called
'cat'.
In the case of the C program , you aren't calling an interpreter to
run the binary. The kernel tries to run it directly.
Still if you loaded all of your executable into memory using some
debuggers, you could still "run" your program as it's being loaded
via the debugger.
The 'noexec' option is like turning off the execute bit on your binary and
disables the kernel from running the binary "natively".
This does make a difference, BTW, if your program had a SetUID bit
set on the program -- loading it with an interpreter won't set your
UID, only when the kernel loads it can that privilege be enabled.
FWIW -- windows has the same type of mechanism.
If you add ".sh" as a "executable suffix" like ".exe" or ".vbs", windows
will automatically run your file according to how you have setup
".sh" files to be executed. Theoretically, you could setup
".txt" files to automatically be typed out if you entered their name
on the command line.
Similarly you could put some short call to a program to print out text
files to the screen. That's a reason not to leave your self logged in
at a public location.
add a comment |
When you run the script via bash It just reads in the file and interprets it.
However when you pass the name to the kernel -- it really inspects the file for "#!" and loads the interpreter specified according to the kernel option "CONFIG_BINFMT_SCRIPT".
It says:
Say Y here if you want to execute interpreted scripts starting with
"#!" followed by the path to an interpreter.
You can build this support as a module; however, until that module
gets loaded, you cannot run scripts. Thus, if you want to load this
module from an initramfs, the portion of the initramfs before loading
this module must consist of compiled binaries only.
Most systems will not boot if you say M or N here. If unsure, say Y.
The above is the help text associated with this option. For another interesting
difference. I wrote my script:
> cat myprog.sh
#!/bin/cat
echo "Hello World"
> chmod +x myprog.sh
Running it with bash still runs bash's interpreter:
> bash myprog.sh
Hello World
However the kernel now does:
> myprog.sh
#!/bin/cat
echo "Hello World"
The kernel printed out the script including the 1st line because it called
'cat'.
In the case of the C program , you aren't calling an interpreter to
run the binary. The kernel tries to run it directly.
Still if you loaded all of your executable into memory using some
debuggers, you could still "run" your program as it's being loaded
via the debugger.
The 'noexec' option is like turning off the execute bit on your binary and
disables the kernel from running the binary "natively".
This does make a difference, BTW, if your program had a SetUID bit
set on the program -- loading it with an interpreter won't set your
UID, only when the kernel loads it can that privilege be enabled.
FWIW -- windows has the same type of mechanism.
If you add ".sh" as a "executable suffix" like ".exe" or ".vbs", windows
will automatically run your file according to how you have setup
".sh" files to be executed. Theoretically, you could setup
".txt" files to automatically be typed out if you entered their name
on the command line.
Similarly you could put some short call to a program to print out text
files to the screen. That's a reason not to leave your self logged in
at a public location.
add a comment |
When you run the script via bash It just reads in the file and interprets it.
However when you pass the name to the kernel -- it really inspects the file for "#!" and loads the interpreter specified according to the kernel option "CONFIG_BINFMT_SCRIPT".
It says:
Say Y here if you want to execute interpreted scripts starting with
"#!" followed by the path to an interpreter.
You can build this support as a module; however, until that module
gets loaded, you cannot run scripts. Thus, if you want to load this
module from an initramfs, the portion of the initramfs before loading
this module must consist of compiled binaries only.
Most systems will not boot if you say M or N here. If unsure, say Y.
The above is the help text associated with this option. For another interesting
difference. I wrote my script:
> cat myprog.sh
#!/bin/cat
echo "Hello World"
> chmod +x myprog.sh
Running it with bash still runs bash's interpreter:
> bash myprog.sh
Hello World
However the kernel now does:
> myprog.sh
#!/bin/cat
echo "Hello World"
The kernel printed out the script including the 1st line because it called
'cat'.
In the case of the C program , you aren't calling an interpreter to
run the binary. The kernel tries to run it directly.
Still if you loaded all of your executable into memory using some
debuggers, you could still "run" your program as it's being loaded
via the debugger.
The 'noexec' option is like turning off the execute bit on your binary and
disables the kernel from running the binary "natively".
This does make a difference, BTW, if your program had a SetUID bit
set on the program -- loading it with an interpreter won't set your
UID, only when the kernel loads it can that privilege be enabled.
FWIW -- windows has the same type of mechanism.
If you add ".sh" as a "executable suffix" like ".exe" or ".vbs", windows
will automatically run your file according to how you have setup
".sh" files to be executed. Theoretically, you could setup
".txt" files to automatically be typed out if you entered their name
on the command line.
Similarly you could put some short call to a program to print out text
files to the screen. That's a reason not to leave your self logged in
at a public location.
When you run the script via bash It just reads in the file and interprets it.
However when you pass the name to the kernel -- it really inspects the file for "#!" and loads the interpreter specified according to the kernel option "CONFIG_BINFMT_SCRIPT".
It says:
Say Y here if you want to execute interpreted scripts starting with
"#!" followed by the path to an interpreter.
You can build this support as a module; however, until that module
gets loaded, you cannot run scripts. Thus, if you want to load this
module from an initramfs, the portion of the initramfs before loading
this module must consist of compiled binaries only.
Most systems will not boot if you say M or N here. If unsure, say Y.
The above is the help text associated with this option. For another interesting
difference. I wrote my script:
> cat myprog.sh
#!/bin/cat
echo "Hello World"
> chmod +x myprog.sh
Running it with bash still runs bash's interpreter:
> bash myprog.sh
Hello World
However the kernel now does:
> myprog.sh
#!/bin/cat
echo "Hello World"
The kernel printed out the script including the 1st line because it called
'cat'.
In the case of the C program , you aren't calling an interpreter to
run the binary. The kernel tries to run it directly.
Still if you loaded all of your executable into memory using some
debuggers, you could still "run" your program as it's being loaded
via the debugger.
The 'noexec' option is like turning off the execute bit on your binary and
disables the kernel from running the binary "natively".
This does make a difference, BTW, if your program had a SetUID bit
set on the program -- loading it with an interpreter won't set your
UID, only when the kernel loads it can that privilege be enabled.
FWIW -- windows has the same type of mechanism.
If you add ".sh" as a "executable suffix" like ".exe" or ".vbs", windows
will automatically run your file according to how you have setup
".sh" files to be executed. Theoretically, you could setup
".txt" files to automatically be typed out if you entered their name
on the command line.
Similarly you could put some short call to a program to print out text
files to the screen. That's a reason not to leave your self logged in
at a public location.
answered Dec 22 '18 at 16:18
Astara
20917
20917
add a comment |
add a comment |
Because the bash executable doesn't reside on said filesystem.
2
That is true but running./hello_world
would also us/bin/bash
which doesn't reside on a file system withnoexec
option mounted. If that is true then thec
program would run as well corerct?
– Valentin Bajrami
Dec 21 '18 at 21:56
1
Nope. That's not the way it works.
– tink
Dec 21 '18 at 21:58
2
@ValentinBajrami The shebang line in the script isn't read until after the noexec and execute-bit hurdles have been cleared.
– Blrfl
Dec 22 '18 at 12:38
add a comment |
Because the bash executable doesn't reside on said filesystem.
2
That is true but running./hello_world
would also us/bin/bash
which doesn't reside on a file system withnoexec
option mounted. If that is true then thec
program would run as well corerct?
– Valentin Bajrami
Dec 21 '18 at 21:56
1
Nope. That's not the way it works.
– tink
Dec 21 '18 at 21:58
2
@ValentinBajrami The shebang line in the script isn't read until after the noexec and execute-bit hurdles have been cleared.
– Blrfl
Dec 22 '18 at 12:38
add a comment |
Because the bash executable doesn't reside on said filesystem.
Because the bash executable doesn't reside on said filesystem.
answered Dec 21 '18 at 21:43
tink
4,16311219
4,16311219
2
That is true but running./hello_world
would also us/bin/bash
which doesn't reside on a file system withnoexec
option mounted. If that is true then thec
program would run as well corerct?
– Valentin Bajrami
Dec 21 '18 at 21:56
1
Nope. That's not the way it works.
– tink
Dec 21 '18 at 21:58
2
@ValentinBajrami The shebang line in the script isn't read until after the noexec and execute-bit hurdles have been cleared.
– Blrfl
Dec 22 '18 at 12:38
add a comment |
2
That is true but running./hello_world
would also us/bin/bash
which doesn't reside on a file system withnoexec
option mounted. If that is true then thec
program would run as well corerct?
– Valentin Bajrami
Dec 21 '18 at 21:56
1
Nope. That's not the way it works.
– tink
Dec 21 '18 at 21:58
2
@ValentinBajrami The shebang line in the script isn't read until after the noexec and execute-bit hurdles have been cleared.
– Blrfl
Dec 22 '18 at 12:38
2
2
That is true but running
./hello_world
would also us /bin/bash
which doesn't reside on a file system with noexec
option mounted. If that is true then the c
program would run as well corerct?– Valentin Bajrami
Dec 21 '18 at 21:56
That is true but running
./hello_world
would also us /bin/bash
which doesn't reside on a file system with noexec
option mounted. If that is true then the c
program would run as well corerct?– Valentin Bajrami
Dec 21 '18 at 21:56
1
1
Nope. That's not the way it works.
– tink
Dec 21 '18 at 21:58
Nope. That's not the way it works.
– tink
Dec 21 '18 at 21:58
2
2
@ValentinBajrami The shebang line in the script isn't read until after the noexec and execute-bit hurdles have been cleared.
– Blrfl
Dec 22 '18 at 12:38
@ValentinBajrami The shebang line in the script isn't read until after the noexec and execute-bit hurdles have been cleared.
– Blrfl
Dec 22 '18 at 12:38
add a comment |
Thanks for contributing an answer to Unix & Linux Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2funix.stackexchange.com%2fquestions%2f490402%2fexecuting-a-bash-script-or-a-c-binary-on-a-file-system-with-noexec-option%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
note:
chmod o+x «filename»
will not give execute permission to file owning user, or group. Usechmod ugo+x «filename»
orchmod +x «filename»
– ctrl-alt-delor
Dec 22 '18 at 11:54
You are right. I used
chmod u+x hello_world
but forgot to change that in the question. Thanks for pointing that out.– Valentin Bajrami
Dec 22 '18 at 12:13