Executing a bash script or a c binary on a file system with noexec option












7














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?










share|improve this question
























  • 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 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 at 12:13
















7














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?










share|improve this question
























  • 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 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 at 12:13














7












7








7


2





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?










share|improve this question















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






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Dec 22 at 12:14

























asked Dec 21 at 21:33









Valentin Bajrami

5,89111627




5,89111627












  • 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 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 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 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 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 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 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 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 at 12:13










5 Answers
5






active

oldest

votes


















21














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.






share|improve this answer























  • Thank you for your detailed explenation and the link to LWN.net article is very helfull!
    – Valentin Bajrami
    Dec 22 at 8:13






  • 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 at 23:37










  • @SergiyKolodyazhnyy Yes.
    – mattdm
    Dec 23 at 2:25



















7














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.






share|improve this answer

















  • 1




    Thanks! Very helpful!
    – Valentin Bajrami
    Dec 22 at 12:19



















3














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






share|improve this answer





















  • 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 at 21:55










  • @ValentinBajrami, no, they execute on different way. Think about no execution flag on the file.
    – Romeo Ninov
    Dec 21 at 21:57





















1














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.






share|improve this answer





























    0














    Because the bash executable doesn't reside on said filesystem.






    share|improve this answer

















    • 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 at 21:56






    • 1




      Nope. That's not the way it works.
      – tink
      Dec 21 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 at 12:38











    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
    });


    }
    });














    draft saved

    draft discarded


















    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









    21














    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.






    share|improve this answer























    • Thank you for your detailed explenation and the link to LWN.net article is very helfull!
      – Valentin Bajrami
      Dec 22 at 8:13






    • 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 at 23:37










    • @SergiyKolodyazhnyy Yes.
      – mattdm
      Dec 23 at 2:25
















    21














    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.






    share|improve this answer























    • Thank you for your detailed explenation and the link to LWN.net article is very helfull!
      – Valentin Bajrami
      Dec 22 at 8:13






    • 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 at 23:37










    • @SergiyKolodyazhnyy Yes.
      – mattdm
      Dec 23 at 2:25














    21












    21








    21






    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.






    share|improve this answer














    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.







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited Dec 23 at 2:27

























    answered Dec 21 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 at 8:13






    • 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 at 23:37










    • @SergiyKolodyazhnyy Yes.
      – mattdm
      Dec 23 at 2:25


















    • Thank you for your detailed explenation and the link to LWN.net article is very helfull!
      – Valentin Bajrami
      Dec 22 at 8:13






    • 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 at 23:37










    • @SergiyKolodyazhnyy Yes.
      – mattdm
      Dec 23 at 2:25
















    Thank you for your detailed explenation and the link to LWN.net article is very helfull!
    – Valentin Bajrami
    Dec 22 at 8:13




    Thank you for your detailed explenation and the link to LWN.net article is very helfull!
    – Valentin Bajrami
    Dec 22 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 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 at 23:37












    @SergiyKolodyazhnyy Yes.
    – mattdm
    Dec 23 at 2:25




    @SergiyKolodyazhnyy Yes.
    – mattdm
    Dec 23 at 2:25













    7














    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.






    share|improve this answer

















    • 1




      Thanks! Very helpful!
      – Valentin Bajrami
      Dec 22 at 12:19
















    7














    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.






    share|improve this answer

















    • 1




      Thanks! Very helpful!
      – Valentin Bajrami
      Dec 22 at 12:19














    7












    7








    7






    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.






    share|improve this answer












    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.







    share|improve this answer












    share|improve this answer



    share|improve this answer










    answered Dec 22 at 12:15









    kasperd

    2,27911027




    2,27911027








    • 1




      Thanks! Very helpful!
      – Valentin Bajrami
      Dec 22 at 12:19














    • 1




      Thanks! Very helpful!
      – Valentin Bajrami
      Dec 22 at 12:19








    1




    1




    Thanks! Very helpful!
    – Valentin Bajrami
    Dec 22 at 12:19




    Thanks! Very helpful!
    – Valentin Bajrami
    Dec 22 at 12:19











    3














    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






    share|improve this answer





















    • 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 at 21:55










    • @ValentinBajrami, no, they execute on different way. Think about no execution flag on the file.
      – Romeo Ninov
      Dec 21 at 21:57


















    3














    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






    share|improve this answer





















    • 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 at 21:55










    • @ValentinBajrami, no, they execute on different way. Think about no execution flag on the file.
      – Romeo Ninov
      Dec 21 at 21:57
















    3












    3








    3






    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






    share|improve this answer












    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







    share|improve this answer












    share|improve this answer



    share|improve this answer










    answered Dec 21 at 21:44









    Romeo Ninov

    5,20231827




    5,20231827












    • 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 at 21:55










    • @ValentinBajrami, no, they execute on different way. Think about no execution flag on the file.
      – Romeo Ninov
      Dec 21 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 at 21:55










    • @ValentinBajrami, no, they execute on different way. Think about no execution flag on the file.
      – Romeo Ninov
      Dec 21 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 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 at 21:55












    @ValentinBajrami, no, they execute on different way. Think about no execution flag on the file.
    – Romeo Ninov
    Dec 21 at 21:57






    @ValentinBajrami, no, they execute on different way. Think about no execution flag on the file.
    – Romeo Ninov
    Dec 21 at 21:57













    1














    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.






    share|improve this answer


























      1














      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.






      share|improve this answer
























        1












        1








        1






        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.






        share|improve this answer












        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.







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Dec 22 at 16:18









        Astara

        20917




        20917























            0














            Because the bash executable doesn't reside on said filesystem.






            share|improve this answer

















            • 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 at 21:56






            • 1




              Nope. That's not the way it works.
              – tink
              Dec 21 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 at 12:38
















            0














            Because the bash executable doesn't reside on said filesystem.






            share|improve this answer

















            • 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 at 21:56






            • 1




              Nope. That's not the way it works.
              – tink
              Dec 21 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 at 12:38














            0












            0








            0






            Because the bash executable doesn't reside on said filesystem.






            share|improve this answer












            Because the bash executable doesn't reside on said filesystem.







            share|improve this answer












            share|improve this answer



            share|improve this answer










            answered Dec 21 at 21:43









            tink

            4,16411219




            4,16411219








            • 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 at 21:56






            • 1




              Nope. That's not the way it works.
              – tink
              Dec 21 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 at 12:38














            • 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 at 21:56






            • 1




              Nope. That's not the way it works.
              – tink
              Dec 21 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 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 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 at 21:56




            1




            1




            Nope. That's not the way it works.
            – tink
            Dec 21 at 21:58




            Nope. That's not the way it works.
            – tink
            Dec 21 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 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 at 12:38


















            draft saved

            draft discarded




















































            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.




            draft saved


            draft discarded














            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





















































            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







            Popular posts from this blog

            Список кардиналов, возведённых папой римским Каликстом III

            Deduzione

            Mysql.sock missing - “Can't connect to local MySQL server through socket”