Development record
rm-rf-root-or-home is bypassed by an IFS named through an expansion: with I=I, `export ${I}FS=x; rm -rf ${U:-x/x}` allows and hands rm "" and / on bash 3.2, /bin/sh, dash and bash 5.3, and so do `eval "I${F:-F}S=x"`, `declare|typeset|readonly|local ${I}FS=x`, `read -r ${I}FS`, `printf -v ${I}FS x` and `: $((IFS=1))`. namesIFS matched the literal text IFS only, and the tokenizer steps over arithmetic.
issueresolvedmajoriss-2609300651115290
rm-rf-root-or-home is bypassed by an IFS named through an expansion: with I=I, export ${I}FS=x; rm -rf ${U:-x/x} allows and hands rm "" and / on bash 3.2, /bin/sh, dash and bash 5.3, and so do eval "I${F:-F}S=x", declare|typeset|readonly|local ${I}FS=x, read -r ${I}FS, printf -v ${I}FS x and : $((IFS=1)). namesIFS matched the literal text IFS only, and the tokenizer steps over arithmetic.
Grounds
- pursued: every form the review named, and the arithmetic, let, getopts, mapfile and wait -p siblings, now blocks while IFS= read -r f and IFS=, read -ra arr with a quoted operand stay allowed; a name built some way the guard still does not read (a sourced file, a nameref set before the line) would show it wrong, and 17-guard.md names those as residuals.
Frontmatter
| id | iss-2609300651115290 |
|---|---|
| type | issue |
| lifecycle | resolved |
| severity | major |
.abcd/work/issues/resolved/iss-2609300651115290-rm-rf-root-or-home-is-bypassed-by-an-ifs.md
open on GitHub ↗ · commit history ↗
mentions2
- iss-2609300726507446Structural closure of iss-2609300651115290's class: an IFS assigned through a name built from an expansion still passed the guard in contexts the per-context reading never reached. I=I; : $[${I}FS=1], a[${I}FS=1]=x, : ${a[${I}FS=1]}, declare -i n; n=${I}FS=1, (( ${I}FS++ )), : ${X:${I}FS=1} and x=${I}FS=1; : $((x)) each set IFS in bash 3.2, /bin/sh and bash 5.3, which then hand rm "" and / for rm -rf ${U:-1/1}; all were allowed. Rounds 1 and 3 each listed the contexts that can set IFS and each re-verify found another, so the fix is one fail-closed rule, not another context.
- iss-2609300812525892Round 5 of iss-2609300651115290's class, a regression the round-4 fix (84ad332c5) opened: the IFS rule keyed on the bytes written beside an expansion, but bash sets the variable an assignment target's VALUE names, so a target written as expansions alone names IFS with no I, F or S byte (reverify4-guardSet finding 1). a=I; b=FS; (( ${a}${b} = 1 )) and x=$a$b; (( $x = 1 )) set IFS to 1 on bash 3.2 and 5.3, and an unquoted default delete after them then hands rm the root; 282908797 blocked both, b993017e8 allows them. Siblings: (( ${x^^} = 1 )), (( ${x@P} = 1 )), a spaced operator in a string arithmetic reads (declare -i n; n="$y = 1"), a substring offset, ${!x:=1}, and a nameref (declare -n r=$x; r=1), which no round read.