Il problema si verifica nei casi in cui il contenuto di $x
non è stato disinfettato e contiene dati che potrebbero essere potenzialmente sotto il controllo di un utente malintenzionato nel caso in cui il codice shell possa finire per essere utilizzato in un contesto di escalation di privilegi (ad esempio uno script invocato da un setuid applicazione, uno script sudoers o utilizzato per elaborare dati fuori rete (CGI, hook DHCP ...) direttamente o indirettamente).
Se:
x='(PATH=2)'
Poi:
x=$((1-$x)))
ha l'effetto collaterale dell'impostazione PATH
su 2
(un percorso relativo che potrebbe benissimo essere sotto il controllo dell'attaccante). Puoi sostituirlo PATH
con LD_LIBRARY_PATH
o IFS
... Lo stesso succede con x=$((1-x))
bash, zsh o ksh (non trattino né yash che accettano solo costanti numeriche nelle variabili lì).
Nota che:
x=$((1-$x))
non funzionerà correttamente per valori negativi $x
in alcune shell che implementano l' --
operatore ( opzionale secondo POSIX) (decremento) (come con x=-1
, ciò significa chiedere alla shell di valutare l' 1--1
espressione aritmetica). "$((1-x))"
non ha il problema poiché x
viene espanso come parte della valutazione aritmetica (non prima).
In bash
, zsh
e ksh
(no dash
o yash
), se x
è:
x='a[0$(uname>&2)]'
Quindi l'espansione $((1-$x))
o $((1-x))
causa l' uname
esecuzione di quel comando (per zsh
, a
deve essere una variabile array, ma si può usare psvar
per esempio per quello).
In sintesi, non si dovrebbe usare non inizializzate o non sterilizzate dati esterni in espressioni aritmetiche in gusci (si noti che la valutazione aritmetica può essere fatto da $((...))
(aka $[...]
in bash
o zsh
), ma anche a seconda della shell in let
, [
/ test
, declare/typeset/export...
, return
, break
, continue
, exit
, printf
, print
builtin, indici di array ((..))
e [[...]]
costrutti per citarne alcuni).
Per verificare che una variabile contenga un numero intero decimale letterale, è possibile utilizzare POSIXly:
case $var in
("" | - | *[!0123456789-]* | ?*-*) echo >&2 not a valid number; exit 1;;
esac
Attenzione che [0-9]
in alcune versioni locali più di 0123456789. [[:digit:]]
dovrebbe essere OK ma non ci scommetterei.
Ricorda inoltre che i numeri con zeri iniziali sono trattati come ottali in alcuni contesti (a 010
volte 10, a volte 8) e fai attenzione che il controllo sopra consentirà di passare attraverso numeri potenzialmente più grandi dell'intero massimo supportato dal tuo sistema (o qualunque applicazione tu voglia usa quel numero intero in; bash ad esempio considera 18446744073709551616 come 0 dato che è 2 64 ). Quindi potresti voler aggiungere ulteriori controlli nell'affermazione del caso sopra come:
(0?* | -0?*)
echo >&2 'Only decimal numbers without leading 0 accepted'; exit 1;;
(-??????????* | [!-]?????????*)
echo >&2 'Only numbers from -999999999 to 999999999 supported'; exit 1;;
Esempi:
$ export 'x=psvar[0$(uname>&2)]'
$ ksh93 -c 'echo "$((x))"'
Linux
ksh93: psvar: parameter not set
$ ksh93 -c '[ x -lt 2 ]'
Linux
ksh93: [: psvar: parameter not set
$ bash -c 'echo "$((x))"'
Linux
0
$ bash -c '[[ $x -lt 2 ]]'
Linux
$ bash -c 'typeset -i a; export a="$x"'
Linux
$ bash -c 'typeset -a a=([x]=1)'
Linux
$ bash -c '[ -v "$x" ]'
Linux
$ mksh -c '[[ $x -lt 2 ]]'
Linux
$ zsh -c 'echo "$((x))"'
Linux
0
$ zsh -c 'printf %d $x'
Linux
0
$ zsh -c 'integer x'
Linux
$ zsh -c 'exit $x'
Linux
Altre letture a:
x='P=3'; : $(($x + 5))
sarà impostatoP
su 8, max='P=3'; : $((x + 5))
sarà impostatoP
su3
(inzsh
,ksh
obash
). "Lo stesso accade con$((x + 1))
..." non è corretto ora; sarà impostatoPATH
su2
, come un tempo.