Skip to content

Commit

Permalink
util: work around bash-3.1 bug that 10>&- fails to close the fd
Browse files Browse the repository at this point in the history
  • Loading branch information
akinomyoga committed Feb 21, 2024
1 parent 5f0b88f commit b593819
Show file tree
Hide file tree
Showing 8 changed files with 403 additions and 91 deletions.
8 changes: 4 additions & 4 deletions ble.pp
Original file line number Diff line number Diff line change
Expand Up @@ -422,11 +422,11 @@ function ble/base/xtrace/adjust {
fi
set +x

((level==0)) || return 0
((_ble_bash>=40000&&level==0)) || return 0
_ble_bash_xtrace_debug_enabled=
if [[ ${bleopt_debug_xtrace:-/dev/null} == /dev/null ]]; then
if [[ $_ble_bash_xtrace_debug_fd ]]; then
builtin eval "exec $_ble_bash_xtrace_debug_fd>&-" || return 0
builtin eval "exec $_ble_bash_xtrace_debug_fd>&-" || return 0 # disable=#D2164 (here bash4+)
_ble_bash_xtrace_debug_filename=
_ble_bash_xtrace_debug_fd=
fi
Expand Down Expand Up @@ -468,7 +468,7 @@ function ble/base/xtrace/restore {
fi
builtin unset -v '_ble_bash_xtrace[level]'

((level==0)) || return 0
((_ble_bash>=40000&&level==0)) || return 0
if [[ $_ble_bash_xtrace_debug_enabled ]]; then
ble/base/xtrace/.log "$FUNCNAME"
_ble_bash_xtrace_debug_enabled=
Expand All @@ -480,7 +480,7 @@ function ble/base/xtrace/restore {
if [[ $_ble_bash_XTRACEFD_dup ]]; then
# BASH_XTRACEFD の fd を元の出力先に繋ぎ直す
builtin eval "exec $BASH_XTRACEFD>&$_ble_bash_XTRACEFD_dup" &&
builtin eval "exec $_ble_bash_XTRACEFD_dup>&-" || ((1))
builtin eval "exec $_ble_bash_XTRACEFD_dup>&-" || ((1)) # disble=#D2164 (here bash4+)
else
# BASH_XTRACEFD の fd は新しく割り当てた fd なので値上書きで閉じて良い
if [[ $_ble_bash_XTRACEFD_set ]]; then
Expand Down
1 change: 1 addition & 0 deletions docs/ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@
- util(joblist): fix job detection in Bash 5.3 `#D2157` xxxxxxxx
- util,complete: work around regex `/=.../` failing in Solaris nawk `#D2162` xxxxxxxx
- main: fix issues in MSYS1 `#D2163` xxxxxxxx
- util: work around bash-3.1 bug that `10>&-` fails to close the fd `#D2164` xxxxxxxx

## Contrib

Expand Down
42 changes: 42 additions & 0 deletions lib/test-bash.sh
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,48 @@ ble/test/start-section 'bash' 61
ble/test '(set -H; builtin history -c; builtin history -p "$line")' stdout="$line"
fi

# BUG bash-3.1 and 3.2 [Ref #D0857]
# A file descriptor >= 10 cannot be redirected if it is already in use. In
# bash-3.2, we can first close the file descriptor and then perform the
# redirect. In bash-3.1, because of the next bug, one cannot simply close
# the file descriptor. One needs to move the file descriptor to another
# number.
if [[ -d /proc/$$/fd ]]; then
(
exec 7>/dev/null 77>/dev/null # disable=#D0857
exec 7>/dev/tty 77>/dev/tty # disable=#D0857
ble/util/getpid
if ((30100<=_ble_bash&&_ble_bash<40000)); then
# bug
ble/test '[[ -t 7 ]]'
ble/test '[[ ! -t 77 ]]'
else
# expected
ble/test '[[ -t 7 ]]'
ble/test '[[ -t 77 ]]'
fi
)
fi

# BUG bash-3.1 [Ref #D2164]
# file descriptor >= 10 cannot be closed by exec 77>&-.
if [[ -d /proc/$$/fd ]]; then
(
exec 7>/dev/null 77>/dev/null # disable=#D0857
exec 7>&- 77>&- # disable=#D2164
ble/util/getpid
if ((30100<=_ble_bash&&_ble_bash<30200)); then
# bug
ble/test '[[ ! -e /proc/$BASHPID/fd/7 ]]'
ble/test '[[ -e /proc/$BASHPID/fd/77 ]]'
else
# expected
ble/test '[[ ! -e /proc/$BASHPID/fd/7 ]]'
ble/test '[[ ! -e /proc/$BASHPID/fd/77 ]]'
fi
)
fi

# BUG bash-3.0 [Ref #D1956]
# 関数定義の一番外側でリダイレクトしてもリダイレクトされない。例えば、
# function func { ls -l /proc/$BASHPID/fd/{0..2}; } <&"$fd0" >&"$fd1"
Expand Down
10 changes: 9 additions & 1 deletion make_command.sh
Original file line number Diff line number Diff line change
Expand Up @@ -274,8 +274,16 @@ function sub:scan/bash301bug {
# する。
grc ' [0-9]{2}&?[<>]' --exclude=./{test,ext} --exclude=./make_command.sh --exclude=ChangeLog.md --color |
sed -E 'h;s/'"$_make_rex_escseq"'//g;s/^[^:]*:[0-9]+:[[:space:]]*//
/^#/d
/#D0857/d
/ [0-9]{2}[<>]&- [0-9]{2}&?[<>]/d
/ [0-9]{2}[<>]&-/d
g'

# bash-3.1 では 10 以上の fd は >&- 等で閉じる事ができない。
grc ' ([0-9]{2}|\$[a-zA-Z_0-9]+)&?[<>]&-' --exclude=./{test,ext} --exclude=./make_command.sh --exclude=ChangeLog.md --color |
sed -E 'h;s/'"$_make_rex_escseq"'//g;s/^[^:]*:[0-9]+:[[:space:]]*//
/^#/d
/#D2164/d
g'

# array-element-length
Expand Down
2 changes: 1 addition & 1 deletion memo/D1779.func-source-and-lineno.sh
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ function ble/function#get-source-and-lineno {
declare -ft "$_ble_util_function_name"
builtin trap 'ble/function#get-source-and-lineno/.extract && return 0' DEBUG
"$_ble_util_function_name"
) 11>&- 11>&1 12>&- 12>&2
) 11>&- 11>&1 12>&- 12>&2 # disable=#D2164 (we give up bash-3.1)
fi
}

Expand Down
4 changes: 2 additions & 2 deletions memo/D1956.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ function test1 {
local fb=$prefix.b.pipe
rm -f "$fa" "$fb"
mkfifo "$fa" "$fb"
exec 36>&- 36<> "$fa"
exec 37>&- 37<> "$fb"
exec 36>&- 36<> "$fa" # disable=#D2164 (XXX--give up bash-3.1)
exec 37>&- 37<> "$fb" # disable=#D2164 (XXX--give up bash-3.1)
_test1_a=36
_test1_b=37

Expand Down
165 changes: 165 additions & 0 deletions note.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7075,6 +7075,171 @@ bash_tips
Done (実装ログ)
-------------------------------------------------------------------------------

2024-02-18

* util: bash-3.1 でプロンプトが表示されなくなっている [#D2164]

元々 #D2159 のテストの過程で見つけて確かに #2159 以降で顕在化した問題だが、
これは bash-3.1 のバグによって引き起こされていて実際にはもっと影響範囲が広
い様に思われるので独立した項目とする。

x bash-3.1 でプロンプトが表示されない。bash-3.2 では発生しない。何が起こっ
ているのだろうか? これは次の項目にある fd#add-cloexec を呼び出さない様に
しても変化しない。先ずはこれを修正したい。

プロンプトは恐らく表示されている。然し、stderr が正しい所に繋がっていない。
そもそも source ble.sh した直後に stderr に何か出力しても何も出ない。実際
に ls -l /proc/$$/fd で確認してみると /dev/null に繋がっている。

また、沢山の fd が既にこの時点でできている。うーん。ble/fd#add-cloexec を
完全に潰すと fd が増える問題は発生しなくなるがやはり /dev/null に繋がって
しまう問題は変わっていにない。他に気づくのは 3.1 だと 40, 41 が
/dev/null, /dev/tty に繋がっているという事。/dev/tty が開いているという事
は -t のテストに失敗しているという事だろうか?

調べていくとどうも最初から /dev/null に繋がっている。何故?
_ble_util_fd_null を初期化した時点で /dev/null に繋がっている様だ。

? ok: 一方で、そうだとしても最初のプロンプトの表示までは tui に繋げておくべ
きでは? と思ったが既にそういう実装になっている。

| 何故か関数の外ではちゃんとtty に繋がっていて、関数の中に入った途端に
| stderr は /dev/null に繋がっている様だ。謎だ。或いは 3.1 では関数内では必
| ず /dev/null に繋がるのだろうか? それとも ble.sh という特殊なスクリプトが
| そうさせている?
|
| うーん。調べると別に関数を抜けたら元に戻るというのは再現しなくなった。勘
| 違いかもしれない。それよりも nextfd を呼び出すと 2 が /dev/null に繋がっ
| た状態になってしまうという事が分かった。
|
| 更に ble/fd#is-open でも発生する事が分かった。もしかして redir に失敗する
| と関数の実行自体がキャンセルされる?
|
| うーん。辿って行ったらそもそも exec 33>&- で stream を閉じるのに失敗して
| いる。その所為で 33 が /dev/null に繋がった儘になり、また bash-3.1 の別の
| バグにより exec 33>&2 でも 33 が更新されずに /dev/null のままになる。それ
| が 2 に書き戻されて /dev/null に置き換わってしまうという事の様だ。
|
| 何故 33>&- で閉じないのか? うーん。一応 99>&33- 等としたら閉じはする。然
| しこれだと更にもう一つの fd 99 が必要になる。と思ったが 99 は置き換わって
| いない様なので実は既存の物でも良いのかもしれない。
|
| 然し前にテストした時にはちゃんと動いていた気がする。不思議だ。もっと単純
| な設定で問題が再現するのか確認する → ble.sh なしで再現する。もっと単純化
| しても発生する。というか 33>&- の形だと常に発生する。以前は bash-3.1 でも
| これを使えば 33>&- 33>&XX とする事ができるのではなかったか? 探してみると
| fd#alloc にコメントが残っていて #D0857 を参照している。然し其処を見ると
| 3.2 と書いてあるし、何だか別の問題について話している様に見える。但し、対
| 策としては一貫している。

まとめると、bash-3.1 では 33>&- 等としても file descriptor が閉じないバグが
あって、更に既知のバグである 33>redir で 33 が閉じていないと失敗する
(#D0857) というバグが組み合わさって面倒な事になっている。#D0857 の
workaround は exec 33>&- 33>redir とする事であったが、実はこれは 3.2 では動
いても 3.1 では動かないという事が判明してしまった。

fd を閉じようと思ったら move_fd 99>&33- を実行するしかない様だ。一方で、移
動先の 99 として何を選ぶのかは微妙である。3.1 だけでこの対策をするのだとし
たら、99>/dev/null として置いて、後はこれに対して redir を実行すれば良い様
に見える。何故なら実験した限りだと 99>&33- としても 99 に対するコピーは失敗
して単に 33 が閉じられるだけに見えるので。然し、これは他の version だと成功
してしまって 99 が置き換わってしまうので使えない方法である。

うーん。これは設計を改めて考え直す必要がある気がする。また、この微妙な振
る舞いは ble-0.3 の alloc/openat の実装にも影響があるのではないか。別項目
で議論するべきの気がする。

x fixed: bash-3.2 でエラーが出る様になった。と思ったが今まで出ていた
~/.bashrc のテスト用コードのエラーが見えていなかったのが見える様になった
だけの気がする。或いは今まで bash-3.2 で ~/.bashrc を実行していなかった。

x ok: この修正をしたら 3.2 で cloexec が効かなくなっている。うーん。修正前
は動いていた筈

→と思って結構遡ってみたが別に古い物でも cloexec はちゃんと働いていない様
だ。#D2158 でも 3.2 についてちゃんと動作を確認した様な事は書かれていない。
恐らく単にテストしていなかっただけ。また、様々の環境のテストでも msys1 以
外は 4.0 以降だったのだろうという気がする。

4.0 で動いているのは procfs があるからかもしれない。procfs がない場合でも
ちゃんと cloexec が動くかは現在の codebase でも確かめるべき → procfs を
使わない場合でもちゃんと 4.0 で cloexec は付加できている。つまり、cloexec
のアプローチの問題という訳ではない。

bash-3.2 に於ける振る舞いについて確認するべきか? 手元で bash-3.2 --norc
で確認する限りは 3.2 でも cloexec を付加する事は可能の様に思われる。何故
ble.sh では動かない? 試しに ble/fd#add-cloexec を色々の fd に適用してみた
が全く効果がない。

実際に ble/fd#add-cloexec の実装を抜き出して各箇所で振る舞いを見てみたが、
undo fd を見つけて exec する所までは良い。然し、undo fd にどうやら
cloexec がついていない様だ。 ls self で見ても残っている。手で clone した
時との違いは何だろうか。

うーん。改めて norc で試してみたが cloexec は付加されていない。付加されて
いる様な気がしたのは勘違いだろうか? そもそもコピー元の undo fd に cloexec
がついていない様に見える。不思議だ。

% と思ったが自分でやった限りは undo fd にちゃんと cloexec がついている。
% という事は .probe で見つけた fd が実は undo fd ではないという可能性? 然
% し、何れにしても undo fd から exec で移すと cloexec は消滅する様に見え
% る。
%
% $ exec 50>/dev/null
% $ { ls /proc/$$/fd; ls /proc/self/fd; exec 51>&10; } 50>/dev/tty
% $ ls /proc/$$/fd; ls /proc/self/fd
%
% 以下の様に mv で移したとしても cloexec は消えている。
%
% $ exec 50>/dev/null
% $ { ls /proc/$$/fd; ls /proc/self/fd; exec 51>&10-; exec 10>&51; } 50>/dev/tty
% $ ls /proc/$$/fd; ls /proc/self/fd
%
% と思ったがよく見ると別に undo fd (10) に cloexec はついていない。改めて
% 自分で確認する。うーん。やはり undo fd が bash-3.2 では子プロセスに継承
% されている。どうも cloexec がついていると思ったのは勘違いの様である。何
% 故こんなに勘違いするのかは分からない。

これは bash-3.2 特有なのだろうか? それとも 3.0 や 3.1 でも同じ? → 3.0
や 3.1 でも同じ振る舞いだった。つまり、3.0 や 3.1 では undo fd でも
cloexec がついていないので undo fd を clone する作戦は全く使えない。

[結論] bash-3.x では undo fd にも cloexec がついていないので、undo fd を
dup しても当然 cloexec はつかない。従って、この方針はそもそも使えない。

* done: m scan test を追加してコードを修正する必要がある

x reject: そもそもコマンドを実行する度にどんどん使っている fd の数が増えて
いく。overwrite が効いていないのだろうか。これは ble/fd#add-cloexec を実
行しない限りは問題にならない。一方で 3.1 に於ける ble/fd#add-cloexec の実
装について考える必要がある。

ble/fd#add-cloexec 対応は諦める。2つ前の調査の結果 bash-3.2 以下では
O_CLOEXEC に対応する事は不可能なので、そもそも fd#add-cloexec は実装しな
い事にした。ble/fd#add-cloexec を試みない限りは問題は発生しない様なので取
り敢えず気にしない事にする。

----

2024-02-21 msys1 を再びテストしようと思ったら再び bad fd のエラーが出る様に
なった。やはり fd の取り扱いを大きく書き換えてまたバグができたのだろうか。
先ず何処でエラーが発生しているか確認する。

どうも /dev/null に繋いだ筈の fd 30 が駄目の様だ。うーん。自分で直接 30 を
開いたりする時には問題は発生しない。bash-3.1 なら chat でも再現する。.close
で exec 30<&"$1"- を実行している所でエラーになっている。或いは、既に閉じて
いる fd について move を試みると発生する? →確かにエラーが発生する。これは
潰す必要がある。

然し、疑問は何故 bad fd のメッセージが行き先の 30 についてなのかという事。
30<&31- を実行しようとして 31 が存在しないのだったら 31 に対してエラーメッ
セージが発生するべきなのでは? 実際に bash-3.1 で手で実行すると 31 の方でエ
ラーメッセージになる。うーん。分かった。 exec 30<&"$v"- の形式だと fd $v が
開いていなかった時のエラーメッセージが 30 に対してになる。

取り敢えず対策はした。最新の bash でも似たようなエラーメッセージの問題はあ
るのだろうか → 何と現在の bash でも同じ問題がある。

2024-02-17

* msys1 をテストの為に久しぶりに使ってみたが色々と問題が発生する [#D2163]
Expand Down
Loading

0 comments on commit b593819

Please sign in to comment.