使用 noexec 选项在文件系统上执行 bash 脚本或 ac 二进制文件

使用 noexec 选项在文件系统上执行 bash 脚本或 ac 二进制文件

任何人都可以详细解释以下发生了什么。假设我正在安装一个目录,其noexec选项如下:

mount -o noexec /dev/mapper/fedora-data /data

因此,为了验证这一点,我运行了mount | grep data

/dev/mapper/fedora-data on /data type ext4 (rw,noexec,relatime,seclabel,data=ordered)

现在,/data我正在创建一个简单的脚本,hello_world如下所示:

#!/bin/bash

echo "Hello World"
whoami

因此,我使脚本可执行chmod u+x hello_world(但这对带有选项的文件系统没有影响noexec),并尝试运行它:

# ./hello_world
-bash: ./hello_world: Permission denied

但是,bash对文件进行预处理会产生:

# bash hello_world
Hello World
root

然后我创建了一个简单的hello_world.c包含以下内容的内容:

#include <stdio.h>

int main()
{
    printf("Hello World\n");
    return 0;
}

使用编译它 cc -o hello_world hello_world.c

现在运行:

# ./hello_world
-bash: ./hello_world: Permission denied

所以我尝试使用运行它

/lib64/ld-linux-x86-64.so.2 hello_world

错误:

./hello_world: error while loading shared libraries: ./hello_world: failed to map segment from shared object: Operation not permitted

所以这当然是正确的,因为ldd返回以下内容:

ldd hello_world
ldd: warning: you do not have execution permission for `./hello_world'
    not a dynamic executable

noexec在另一个不适用挂载选项的系统上,我看到:

ldd hello_world
    linux-vdso.so.1 (0x00007ffc1c127000)
    libc.so.6 => /lib64/libc.so.6 (0x00007facd9d5a000)
    /lib64/ld-linux-x86-64.so.2 (0x00007facd9f3e000)

现在我的问题是:为什么使用选项在文件系统上运行 bash 脚本可以noexec工作,但c编译后的程序却不行?幕后发生了什么?

答案1

两种情况发生的情况是相同的:要直接执行文件,需要设置执行位,并且无法使用 noexec 挂载文件系统。但这些事情并不能阻止任何事情阅读那些文件。

当 bash 脚本运行./hello_world并且文件不可执行时(没有 exec 权限位,或者文件系统上的 noexec),该#!甚至没有检查,因为系统甚至不加载该文件。该脚本永远不会在相关意义上“执行”。

在这种情况下bash ./hello_world,noexec 文件系统选项显然并不像您希望的那样智能。bash运行的命令是,/bin/bash并且/bin不在带有 的文件系统上noexec。所以,运行起来没有问题。系统并不关心 bash(或 python、perl 或其他)是解释器。它只是运行您给出的命令 ( /bin/bash),参数恰好是一个文件。对于 bash 或其他 shell,该文件包含要执行的命令列表,但现在我们“过去”了任何要检查文件执行位的内容。该检查不对以后发生的事情负责。

考虑这种情况:

$ cat hello_world | /bin/bash

...或者对于那些不喜欢毫无意义地使用猫的人:

$ /bin/bash < hello_world

#!文件开头的“shbang”序列只是一些很好的魔法,当您尝试将文件作为命令执行时,它可以有效地执行相同的操作。您可能会发现这篇 LWN.net 文章很有帮助:程序如何运行

答案2

前面的答案解释了为什么当从命令行显式调用noexec解释器(在您的情况下)时,该设置不会阻止脚本运行。/bin/bash但如果这就是全部,那么这个命令也可以工作:

/lib64/ld-linux-x86-64.so.2 hello_world

正如您所指出的那样,这是行不通的。那是因为noexec还有另一个效果。内核将不允许来自该文件系统的内存映射文件PROT_EXEC启用。

内存映射文件用于多种场景。两种最常见的情况是针对可执行文件和库。当使用系统调用启动程序时execve,内核将在内部为链接器和可执行文件创建内存映射。所需的任何其他库均由链接器通过启用的mmap系统调用进行内存映射PROT_EXEC。如果您尝试使用文件系统中的库,noexec内核将拒绝执行该mmap调用。

当您调用/lib64/ld-linux-x86-64.so.2 hello_world系统execve调用时,只会为链接器创建内存映射,链接器将打开hello_world可执行文件并尝试以与为库所做的几乎相同的方式创建内存映射。此时内核拒绝执行调用mmap,并且您会收到错误:

./hello_world: error while loading shared libraries: ./hello_world: failed to map segment from shared object: Operation not permitted

noexec设置仍然允许在没有执行权限的情况下进行内存映射(有时用于数据文件),并且它还允许正常读取文件,这就是bash hello_world为您工作的原因。

答案3

以此方式执行命令:

bash hello_world

bash从文件中读取hello_world(这不是禁止的)。

在其他情况下,操作系统尝试运行此文件hello_world并因noexec标志而失败

答案4

因为 bash 可执行文件并不驻留在所述文件系统上。

相关内容