명령 앞에 bash stat() 및 access()가 너무 많습니다. 정상인가요?

명령 앞에 bash stat() 및 access()가 너무 많습니다. 정상인가요?

실행하도록 지시된 셸 에서 실행하면 strace실제 바이너리가 실행되기 전에 광범위한 통계를 보여주는 다음 출력이 제공됩니다.bashmkdirmkdir

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모든 시스템 호출을 처음부터 끝까지 저장한다는 인상을 받았습니다 . 내가 무엇을 놓치고 있나요?execvestat

이후 설명을 찾아 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` 라인과 유사하다는 것을 알고 있습니다.2767are because of searching the executable in the various directories in the current

이게 세일 중이면 dash한 통계만 하고 bash10번만 하세요. 다시 질문:이게 정상인가요?

고쳐 쓰다: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 , 및 을 사용하게 됩니다.glibcint eaccess(const char *pathname, int mode);eaccessgeteuidgetguidgetuidgetgidaccessfindcmd.c함수는 file_status이와 같이 eaccess를 두 번 실행합니다.

#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()) 경로의 각 요소를 두 번: find_in_path_element()at 행을 통해 한 번640is_directory()at 라인을 통해 한 번645화.

말씀하신 대로 file_status()소위 eaccess().

이는 최적화될 수 있지만 경로가 해시되고 이러한 모든 검색 및 통계는 명령이 처음 사용될 때만 발생하므로 큰 문제는 아니라는 점을 명심하십시오.

답변2

보고 있다배쉬 소스 코드, 정답은:

통화는 정상이며 다음을 포함한 여러 가지 요인으로 인해 발생합니다.

  1. bashfile_status호출이 포함된 함수를 실행하면 stat포함 쌍이 실행되고 대부분의 GNU/Linux 설정에서 실행됩니다.각기 다른eaccess에서 전화glibc
  2. glibceaccess달리기 의다시 stat그런 다음 메리 geteuid, getegid, getuidgetgid마지막으로 access(유용한 정보를 기록하지 않기 때문에 statglibc는 시스템 호출을 전혀 저장하고 싶지 않을 수도 있습니다 (컨텍스트 스위치는 중요하지 않습니다!).
  3. bash디렉터리를 반복할 수 있는 파일이 PATH실제로 실행 가능하고 이를 수행하려는 사용자가 읽을 수 있는지 확인하려고 합니다. (시험)
  4. bashhash연속 호출에 대한 검색 경로를 줄이기 위해 테이블 을 실행합니다 (두 번째 테스트 file_status).

이로 인해 중복되는 것처럼 보이는 시스템 호출이 대량으로 생성됩니다. PATH의 각 칸테이트는 6/11 시스템 호출을 수행하고 명령이 이전에 해시 테이블에 있는 것으로 확인되면 또 다른 6/11 시스템 호출이 수행되므로 명령이 여전히 유효한지 확인합니다.

file_statusBash의 함수는 findcmd.cLinux 컴퓨터용으로 컴파일할 때 다음과 같습니다(예: 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()처음에는 한 번 실행된 다음 eaccess()순차적으로 두 번 실행됩니다( glibc함수는 다음과 같이 실행됩니다.

  1. stat
  2. geteuid
  3. getguid
  4. getuid
  5. getgid
  6. 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

관련 정보