実行するように指示されたシェルで実行すると、strace
実際のバイナリが実行される前に広範な統計を示す次の出力が得られます。bash
mkdir
mkdir
BASH$> strace -f sh -c "bash -c \"mkdir /tmp\" 2>&1 | nl | grep -e "execve\|stat\|access"
[.....]
2766 [pid 17371] stat(".", {st_mode=S_IFDIR|0750, st_size=17262, ...}) = 0
2767 [pid 17371] stat("/usr/local/sbin/mkdir", 0x7ffd87aad0a0) = -1 ENOENT 2767 (No such file or directory)
2768 [pid 17371] stat("/usr/local/bin/mkdir", 0x7ffd87aad0a0) = -1 ENOENT (No such file or directory)
2769 [pid 17371] stat("/usr/bin/mkdir", {st_mode=S_IFREG|0755, st_size=51136, ...}) = 0
2770 [pid 17371] stat("/usr/bin/mkdir", {st_mode=S_IFREG|0755, st_size=51136, ...}) = 0
2771 [pid 17371] access("/usr/bin/mkdir", X_OK) = 0
2772 [pid 17371] stat("/usr/bin/mkdir", {st_mode=S_IFREG|0755, st_size=51136, ...}) = 0
2773 [pid 17371] access("/usr/bin/mkdir", R_OK) = 0
2774 [pid 17371] stat("/usr/bin/mkdir", {st_mode=S_IFREG|0755, st_size=51136, ...}) = 0
2775 [pid 17371] stat("/usr/bin/mkdir", {st_mode=S_IFREG|0755, st_size=51136, ...}) = 0
2776 [pid 17371] access("/usr/bin/mkdir", X_OK) = 0
2777 [pid 17371] stat("/usr/bin/mkdir", {st_mode=S_IFREG|0755, st_size=51136, ...}) = 0
2778 [pid 17371] access("/usr/bin/mkdir", R_OK) = 0
2779 [pid 17371] execve("/usr/bin/mkdir", ["mkdir", "/tmp"], 0x557ec7e15920 /* 5 vars */) = 0
/usr/bin/mkdir
stat()
私の質問は:頻繁に編集されるのが普通ですか(それではなぜですか?)出力ラインには番号が付けられています。特にこの行が実行され2776
たらどういう意味があるのか知りたいです2771
。また、私はbashがすぐに情報を提供する必要があるため、2770
すべてのシステムコールを最初から最後まで保存するという印象を受けました。私は何を見逃していますか?execve
stat
以降の説明を調べて、代替dash
シェルがどのように機能するかを確認し、次のstat()
内容も表示されます。
DASH$> strace -f sh -c "dash -c \"mkdir /tmp\" 2>&1 | nl | grep -e "execve\|stat\|access"
[....]
2792 [pid 17372] stat("/usr/local/sbin/mkdir", 0x7ffc66010b50) = -1 ENOENT (No such file or directory)
2793 [pid 17372] stat("/usr/local/bin/mkdir", 0x7ffc66010b50) = -1 ENOENT (No such file or directory)
2794 [pid 17372] stat("/usr/sbin/mkdir", {st_mode=S_IFREG|0755, st_size=51136, ...}) = 0
2795 [pid 17372] execve("/usr/sbin/mkdir", ["mkdir", "/run"], 0x55d8d3453bb8 /* 6 vars */) = 0
私は2792
、2793
が2768 PATH`行に似ていることを知っています。2767
are because of searching the executable in the various directories in the current
これがセール中であれば、一統計dash
だけしてbash
10回だけしてください。もう一度質問:これは普通ですか?
修正する:Bash統計には、より多くのgeteuid()
、getguid()
およびgetuid()
混合項目があります。getgid()
BASH$>strace -f sh -c "bash -c \"mkdir /tmp\"" 2>&1 | grep -e "execve\|stat\|access\|geteuid\|getegid\|getuid\|getgid"
[....]
[pid 24534] stat("/usr/local/bin/mkdir", 0x7fffda480f30) = -1 ENOENT (No such file or directory)
[pid 24534] stat("/usr/local/sbin/mkdir", 0x7fffda480f30) = -1 ENOENT (No such file or directory)
[pid 24534] stat("/usr/bin/mkdir", {st_mode=S_IFREG|0755, st_size=51136, ...}) = 0
[pid 24534] stat("/usr/bin/mkdir", {st_mode=S_IFREG|0755, st_size=51136, ...}) = 0
[pid 24534] geteuid() = 1000
[pid 24534] getegid() = 1000
[pid 24534] getuid() = 1000
[pid 24534] getgid() = 1000
[pid 24534] access("/usr/bin/mkdir", X_OK) = 0
[pid 24534] stat("/usr/bin/mkdir", {st_mode=S_IFREG|0755, st_size=51136, ...}) = 0
[pid 24534] geteuid() = 1000
[pid 24534] getegid() = 1000
[pid 24534] getuid() = 1000
[pid 24534] getgid() = 1000
[pid 24534] access("/usr/bin/mkdir", R_OK) = 0
[pid 24534] stat("/usr/bin/mkdir", {st_mode=S_IFREG|0755, st_size=51136, ...}) = 0
[pid 24534] stat("/usr/bin/mkdir", {st_mode=S_IFREG|0755, st_size=51136, ...}) = 0
[pid 24534] geteuid() = 1000
[pid 24534] getegid() = 1000
[pid 24534] getuid() = 1000
[pid 24534] getgid() = 1000
[pid 24534] access("/usr/bin/mkdir", X_OK) = 0
[pid 24534] stat("/usr/bin/mkdir", {st_mode=S_IFREG|0755, st_size=51136, ...}) = 0
[pid 24534] geteuid() = 1000
[pid 24534] getegid() = 1000
[pid 24534] getuid() = 1000
[pid 24534] getgid() = 1000
[pid 24534] access("/usr/bin/mkdir", R_OK) = 0
[pid 24534] execve("/usr/bin/mkdir", ["mkdir", "/tmp"], 0x55adcd4dc040 /* 55 vars */) = 0
それでは、これが「ここで何が起こっているのか」についての手がかりを提供できますか?悪用を防ぐためにいくつかのテストが実施されていますかsetuid
?
**更新2:** geteuid()
、getguid()
およびgetuid()
アクセスの組み合わせは、ライブラリ機能の使用の特徴であるようですgetgid()
。使用するたびにbashが実行されるため、all、およびを使用します。glibc
int eaccess(const char *pathname, int mode);
eaccess
geteuid
getguid
getuid
getgid
access
findcmd.c関数はfile_status
このようにeaccessを2回実行します。
#if defined (HAVE_EACCESS)
/* Use eaccess(2) if we have it to take things like ACLs and other
file access mechanisms into account. eaccess uses the effective
user and group IDs, not the real ones. We could use sh_eaccess,
but we don't want any special treatment for /dev/fd. */
if (eaccess (name, X_OK) == 0)
r |= FS_EXECABLE;
if (eaccess (name, R_OK) == 0)
r |= FS_READABLE;
これらの各eaccessは、4つのシステムコールに接続できます。
答え1
ループを見てください。findcmd.c:find_user_command_in_path()
。
stat()
(からfile_status()
)パスの各要素を2回:find_in_path_element()
at行を介して1回640is_directory()
atラインを通って一度645話。
おっしゃった通りfile_status()
いわゆるeaccess()
。
これは最適化できますが、パスがハッシュされ、これらのすべての検索と統計はコマンドが最初に使用されたときにのみ発生するため、大きな問題ではないことに注意してください。
答え2
見ているバッシュソースコード、正解は:
はい通話は正常であり、次のようないくつかの要因が原因で発生します。
bash
file_status
呼び出しを含む関数を実行すると、インクルードペアが実行され、stat
ほとんどのGNU / Linux設定で実行されます。それぞれ違うeaccess
から電話glibc
glibc
eaccess
走る再びstat
その後、メアリーgeteuid
、、、そして最後に(有用な情報を記録しないので、glibcはシステムコールをまったく保存したくないかもしれません(コンテキストスイッチは重要ではありません!)getegid
。getuid
getgid
access
stat
bash
ディレクトリを繰り返すことができるファイルがPATH
実際に実行可能であり、それを実行したいユーザーが読み取ることができることを確認したいと思います。 (試験)bash
hash
連続呼び出しの検索パスを減らすためにテーブルを実行します(2番目のテストfile_status
)。
これにより、重複しているように見えるシステムコールが一括生成されます。 PATHの各カンテートは6/11システムコールを実行し、コマンドが以前にハッシュテーブルにあると確認された場合は、別の6/11システムコールが実行されるため、コマンドがまだ有効であることを確認します。
file_status
Bashの関数は、findcmd.c
Linuxコンピュータ用にコンパイルするときに次のようになります(例:ifdefs評価)。
int
file_status (name)
const char *name;
{
struct stat finfo;
int r;
/* Determine whether this file exists or not. */
if (stat (name, &finfo) < 0)
return (0);
/* If the file is a directory, then it is not "executable" in the
sense of the shell. */
if (S_ISDIR (finfo.st_mode))
return (FS_EXISTS|FS_DIRECTORY);
r = FS_EXISTS;
/* Use eaccess(2) if we have it to take things like ACLs and other
file access mechanisms into account. eaccess uses the effective
user and group IDs, not the real ones. We could use sh_eaccess,
but we don't want any special treatment for /dev/fd. */
if (exec_name_should_ignore (name) == 0 && eaccess (name, X_OK) == 0)
r |= FS_EXECABLE;
if (eaccess (name, R_OK) == 0)
r |= FS_READABLE;
return r;
}
stat()
最初は1回実行され、順番にeaccess()
2回実行されます(glibc
関数は次のように実行されます。)
stat
geteuid
getguid
getuid
getgid
access
)
したがって、この部分は元のbash出力を担当します。
[pid 24534] stat("/usr/bin/mkdir", {st_mode=S_IFREG|0755, st_size=51136, ...}) = 0
[pid 24534] geteuid() = 1000
[pid 24534] getegid() = 1000
[pid 24534] getuid() = 1000
[pid 24534] getgid() = 1000
[pid 24534] access("/usr/bin/mkdir", X_OK) = 0
[pid 24534] stat("/usr/bin/mkdir", {st_mode=S_IFREG|0755, st_size=51136, ...}) = 0
[pid 24534] geteuid() = 1000
[pid 24534] getegid() = 1000
[pid 24534] getuid() = 1000
[pid 24534] getgid() = 1000
[pid 24534] access("/usr/bin/mkdir", R_OK) = 0