프로세스 교체 시 fd 할당 순서

프로세스 교체 시 fd 할당 순서

이 답변에서 영감을 얻었습니다https://security.stackexchange.com/a/166645 이 명령을 실행할 때 이상한 순서가 적용되는 이유가 궁금합니다.

root@6cb8704148bf:/usr/app# echo <(printf "111")
/dev/fd/63
root@6cb8704148bf:/usr/app# echo <(printf "111")
/dev/fd/63
root@6cb8704148bf:/usr/app# echo <(printf "111") <(printf "222")
/dev/fd/63 /dev/fd/62

지금까지는 이것이 다소 정상적인 것처럼 보입니다. 그렇다면 계속 0으로 내려가면 어떻게 되는지 궁금합니다.

root@6cb8704148bf:/usr/app# echo <(printf "111") <(printf "222") <(printf "222")  <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222")  <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222")  <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222")  <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222") <(printf "222")
/dev/fd/63 /dev/fd/62 /dev/fd/61 /dev/fd/60 /dev/fd/59 /dev/fd/58 /dev/fd/57 /dev/fd/56 /dev/fd/55 /dev/fd/54 /dev/fd/53 /dev/fd/52 /dev/fd/51 /dev/fd/50 /dev/fd/49 /dev/fd/48 /dev/fd/47 /dev/fd/46 /dev/fd/45 /dev/fd/44 /dev/fd/43 /dev/fd/42 /dev/fd/41 /dev/fd/40 /dev/fd/39 /dev/fd/38 /dev/fd/37 /dev/fd/36 /dev/fd/35 /dev/fd/34 /dev/fd/33 /dev/fd/32 /dev/fd/31 /dev/fd/30 /dev/fd/29 /dev/fd/28 /dev/fd/27 /dev/fd/26 /dev/fd/25 /dev/fd/24 /dev/fd/23 /dev/fd/22 /dev/fd/21 /dev/fd/20 /dev/fd/19 /dev/fd/18 /dev/fd/17 /dev/fd/16 /dev/fd/15 /dev/fd/14 /dev/fd/13 /dev/fd/12 /dev/fd/11 /dev/fd/10 /dev/fd/9 /dev/fd/8 /dev/fd/7 /dev/fd/6 /dev/fd/5 /dev/fd/3 /dev/fd/4 /dev/fd/64 /dev/fd/65 /dev/fd/66 /dev/fd/67 /dev/fd/68 /dev/fd/69 /dev/fd/70 /dev/fd/71 /dev/fd/72 /dev/fd/73 /dev/fd/74 /dev/fd/75 /dev/fd/76 /dev/fd/77 /dev/fd/78 /dev/fd/79 /dev/fd/80 /dev/fd/81 /dev/fd/82 /dev/fd/83 /dev/fd/84 /dev/fd/85 /dev/fd/86 /dev/fd/87 /dev/fd/88 /dev/fd/89 /dev/fd/90 /dev/fd/91 /dev/fd/92 /dev/fd/93 /dev/fd/94 /dev/fd/95 /dev/fd/96 /dev/fd/97 /dev/fd/98 /dev/fd/99 /dev/fd/100 /dev/fd/101

왜 이런 일이 일어나는가:

63
..
5
3
4
64
...

이 순서를 설명할 수 있는 사람이 있나요?

답변1

당신이 보면암호, 다음 내용이 표시됩니다.

  /* Move the parent end of the pipe to some high file descriptor, to
     avoid clashes with FDs used by the script. */
  parent_pipe_fd = move_to_high_fd (parent_pipe_fd, 1, 64);

아이디어는 sh의 fd 0부터 9까지 작업을 수행할 수 있는 사용자를 위해 예약되어 있다는 것입니다 cmd < x > y 2> z ... 9> .... 따라서 쉘은 모든 내부 FD에 대해 이 범위 밖의 fd를 사용하려고 시도합니다. 이는 프로세스 교체에만 국한되지 않고 공동 프로세스에도 사용되는 것을 볼 수 있습니다. 예를 들면 다음과 같습니다.

$ bash -c 'coproc :; echo "${COPROC[@]}"'
63 60

그리고 내장을 리디렉션하거나 zsh 에서 가져올 때 파일 설명자를 저장하는 것과 같은 일부 다른 경우에는 fd >= 10( 사용 ) {fd}>...을 얻으려고 시도합니다 .fcntl (fd, F_DUPFD, SHELL_FD_BASE)

$ bash -c 'exec {fd}</dev/null; echo "$fd"'
10

이것move_to_high_fd()이 함수의 코드, 전달된 인수(여기서는 64) 아래에서 첫 번째 사용 가능한 fd를 찾고 maxfd해당 fd가 그보다 큰 경우 3fd를 해당 위치로 이동합니다. 실패하면 fd 4 ~ 63이 모두 사용 중이면 fd는 이동되지 않으며 이는 3, 4, 64를 얻는 이유를 설명합니다.

zsh처럼 9 이상의 첫 번째 fd를 얻는 대신 "높은" 값에서 거꾸로 수행하는 이유는 bash가 실제로 사용자가 표준의 확장으로 9 이상의 fd를 사용할 수 있도록 허용한다는 사실과 관련이 있다고 생각합니다.

이 코드 또는 이와 유사한 코드는 1996년 2.0에 등장했지만 프로세스 교체는 2.0.1에서만 사용되기 시작했습니다. 이전에는 프로세스 교체로 fd가 이동되지 않았으므로 ksh93에서 발생한 것과 동일한 유형의 문제가 발생할 수 있습니다. 이 문제는 이러한 fd를 이동하지 않았습니다.

$ ksh -c 'echo <(:)'
/dev/fd/3
$ ksh -c 'exec 3< <(echo test); cat <&3'
ksh: 3: cannot open [Bad file descriptor]

문제를 해결하기 위해 무엇을 하시나요?예약하다이전 fd를 교체하는 프로세스를 실행합니다.

$ ksh -c 'exec 3<&0 3< <(echo test); cat <&3'
test

현재 bash버전에도 동일한 문제가 있습니다.

$ bash -c 'exec 63< <(echo test); cat <&63'
bash: line 1: 63: Bad file descriptor
$ bash -c 'exec 63<&0 63< <(echo test); cat <&63'
test

충돌을 예방하는 것은 move_to_high_fd()아니며 사람들이 낮은 fd 숫자를 선호한다는 가정하에 작동합니다.

왜 64인지 모르겠습니다. CWRU/changelogbash-2.05b에서 언급됨:

subst.c
        - in process_substitute, call move_to_high_fd with `maxfd' parameter
          of -1 instead of 64, so move_to_high_fd will use its maximum

bash-2.05b-beta1와 릴리스 사이에 있지만 bash-2.05b-beta22.05b 릴리스 이전에 복원된 것으로 보입니다.

move_to_high_fd()변경 로그에 따르면 이 변경 로그 항목은 bash-2.0-alpha4와 bash-2.0-beta1 사이에 도입되었습니다.

universal.c

  • 새로운 함수: move_to_high_fd(fd)는 FD를 허용된 최대값에 가까운 파일 설명자로 이동하려고 시도하고 새 fd를 반환하고 이전 fd를 닫습니다(또는 문제가 발생하면 이전 fd를 반환합니다).

처음에는 인수가 허용되지 않지만 maxfd허용되는 가장 높은 값(최대 256개)을 결정합니다. maxfdbash-2.01-alpha1과 bash-2.01-beta1 사이에 다음을 추가했습니다.

universal.c, universal.h

  • move_to_high_fd는 이제 세 번째 인수, 즉 찾기 시작할 가장 높은 fd를 취합니다. 20개 미만인 경우 getdtablesize()에서 반환한 최대 열린 파일 수를 사용합니다(이전에는 그랬습니다).

jobs.c, shell.c, subst.c

  • move_to_high_fd에 대한 호출을 적절하게 변경했습니다.

그러나 때때로 64가 사용되는 이유에 대한 설명은 없습니다 . 아마도 일부 시스템에서는 n이 63보다 크지 maxfd않을 것으로 추측됩니다 ./dev/fd/n


1 프로세스 교체는 1980년대 중반 ksh에 의해 도입되었지만 당시에는 프로세스 교체를 리디렉션 대상으로 사용할 수 없었습니다. 이것은 최근 ksh93에서 변경되었습니다.

관련 정보