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 '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
















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 '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














7












7








7


3






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 '18 at 12:14







Valentin Bajrami

















asked Dec 21 '18 at 21:33









Valentin BajramiValentin Bajrami

6,10111627




6,10111627













  • 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



















  • 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

















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










5 Answers
5






active

oldest

votes


















22














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











  • @SergiyKolodyazhnyy Yes.

    – mattdm
    Dec 23 '18 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































    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 '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





















    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 '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











      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









      22














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











      • @SergiyKolodyazhnyy Yes.

        – mattdm
        Dec 23 '18 at 2:25
















      22














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











      • @SergiyKolodyazhnyy Yes.

        – mattdm
        Dec 23 '18 at 2:25














      22












      22








      22







      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 '18 at 2:27

























      answered Dec 21 '18 at 22:07









      mattdmmattdm

      28.8k1372116




      28.8k1372116













      • 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 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



















      • 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 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

















      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













      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




























        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


























          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 '18 at 12:15









          kasperdkasperd

          2,46011127




          2,46011127























              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 '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


















              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 '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
















              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 '18 at 21:44









              Romeo NinovRomeo Ninov

              6,54132028




              6,54132028













              • 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











              • @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













              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 '18 at 16:18









                  AstaraAstara

                  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 '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
















                      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 '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














                      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 '18 at 21:43









                      tinktink

                      4,49711221




                      4,49711221








                      • 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






                      • 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





                        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





                        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


















                      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.




                      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

                      Wiesbaden

                      Marschland

                      Dieringhausen