我们有两个 RHEL 6 系统,它们运行相同版本的内核和 glibc(glibc–2.12–1.90.el6_3.6)。根据 POSIX 标准和 Linux 手册页,如果内核确定可执行文件不是可执行格式(例如 ELF),并且没有 shebang ( #!
) 行,则函数execl
、execlp
、execle
、execv
、execvp
和execvpe
(但不是execve
)将尝试使用 POSIX shell 执行该文件,在 Linux 上是/bin/sh
。
但是,在其中一个系统上,执行第一行使用:
函数的 shell 脚本execvp
会失败,而在另一台机器上,该脚本会/bin/sh
按预期使用执行。具体来说,我们使用这些测试程序;/tmp/test_script
使其可执行:
xc:
#包括 <stdio.h> #包括 <unistd.h> #包括 <stdlib.h> 外部字符**环境; int 主要 () { char *argv[] = { “/tmp/test_script”, NULL }; char *程序名 = argv[0]; 如果 (execvp(程序名, argv) == -1) { perror(“execvp”); 返回-1; } }
/tmp/测试脚本:
: echo“测试脚本已调用” 出口 0
我们已经在源 RPM 中搜索了已安装的 glibc 版本,它确实实现了所需的行为(execvp 是 execvpe 的简单包装器):
posix/execvpe.c:
如果 (strchr (文件,'/') != NULL) { /* 当包含斜杠时不要搜索。 */ __execve(文件,argv,envp); 如果 (errno == ENOEXEC) { /* 计算参数。 */ int argc = 0; while (argv[argc++]) ; size_t len = (argc + 1) * sizeof(char *); 字符**script_argv; 无效* ptr = NULL; 如果 (__libc_use_alloca(len)) script_argv = 分配(len); 别的 script_argv = ptr = malloc(len); 如果(script_argv!= NULL) { scripts_argv(文件, argv, argc, script_argv); __execve(script_argv[0], script_argv, envp); 释放(ptr); } } } 别的 ︙
这里scripts_argv
是一个简单的函数,它添加/bin/sh
到参数列表的前面,与通过 glibc 向用户空间公开的函数__execve
相同。execve
有其他人在 Linux 上遇到过这个问题吗?在我尝试过的其他所有系统上,此行为都是正确的。
答案1
好吧,我发现了问题所在。编译x.c
可执行文件后,我运行ldd
它,发现有一个liboit.so
动态链接到可执行文件的库。这个库是由ObserveIT Unix 审计员,它会拦截已登录用户的所有活动,包括系统调用。但是,我认为仅仅拦截系统调用不会导致观察到的行为,因为重新执行是/bin/sh
在 glibc 内部完成的。也许 ObserveIT 也模拟了 POSIX 函数,在这种情况下显然不符合要求。