web-dev-qa-db-ja.com

コマンドの前に、bash stat()とaccess()を頻繁に使用するのは正常ですか?

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_はどのような意味を持つのでしょうか。また、execveが一度に情報を提供するはずだったので、bashは_2770_から最後の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_は、行_2767_、2768 _are because of searching the executable in the various directories in the current_ PATH`と同様です。

これが割り引かれる場合、dashは単一の統計のみを実行し、bashは10を実行します。もう一度質問を提起します:これは正常ですか?

UPDATE: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
_

だから多分これはbashで「ここで起こっている」ことへの手がかりを与えることができますか? setuidエクスプロイトを防ぐためにいくつかのチェックを行っていますか?

**更新2:** geteuid()getguid()getuid()getgid()とアクセスの組み合わせは、glibcint eaccess(const char *pathname, int mode);ライブラリ関数。 eaccessを使用するたびに、すべてのgeteuidgetguidgetuidgetgid、およびaccessが使用されます。これは、bashが実行されるためです findcmd.c の_file_status_関数が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つのシステムコールにリンクされている可能性があります。

6

findcmd.c:find_user_command_in_path() のループを確認する必要があります。

stat()は、パス内の要素ごとに2回( file_status() から)呼び出されます。1回はfind_in_path_element()640 で、1回はis_directory()645

おっしゃるように、file_status()が呼び出されるのもeaccess()です。

これは最適化できますが、パスがハッシュされ、コマンドが初めて使用されたときにのみこの検索と統計が行われるため、大したことではないことに注意してください。

3
mosvy