プロセス交換時の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

これまでは、これがやや正常なようです。だとしたら、ゼロに降りるとどうなるのだろうか。

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 は移動されません。

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 は、3 番目の引数、つまり検索を開始する最も高い fd を取ります。 20個未満の場合は、getdtablesize()によって返された最大オープンファイル数を使用します(以前はそうでした)。

jobs.c、shell.c、subst.c

  • move_to_high_fdの呼び出しを適切に変更しました。

ただし、64が使用される理由の説明はありません。おそらく、いくつかのシステムでは、nが63より大きくmaxfdないと推測されます。/dev/fd/n


1プロセス交換は1980年代半ばのkshによって導入されましたが、当時はプロセス交換をリダイレクトターゲットとして使用することはできませんでした。これは最近ksh93で変更されました。

関連情報