O problema ocorre nos casos em que o conteúdo de $x
não foi higienizado e contém dados que podem estar sob o controle de um invasor nos casos em que o código do shell pode acabar sendo usado em um contexto de escalação de privilégios (por exemplo, um script invocado por um setuid aplicação, um script sudoers ou usado para processar dados fora da rede (CGI, gancho DHCP ...) direta ou indiretamente).
E se:
x='(PATH=2)'
Então:
x=$((1-$x)))
tem o efeito colateral de definir PATH
como 2
(um caminho relativo que pode muito bem estar sob controle do invasor). Você pode substituir PATH
por LD_LIBRARY_PATH
ou IFS
... O mesmo acontece com x=$((1-x))
bash, zsh ou ksh (não traço nem yash, que aceitam apenas constantes numéricas nas variáveis).
Observe que:
x=$((1-$x))
não funcionará corretamente para valores negativos de $x
alguns shells que implementam o --
operador ( opcional conforme POSIX) (decremento) (como acontece x=-1
, isso significa pedir ao shell para avaliar a 1--1
expressão aritmética). "$((1-x))"
não tem o problema, pois x
é expandido como parte da avaliação aritmética (não antes).
Em bash
, zsh
e ksh
(não dash
ou yash
), se x
é:
x='a[0$(uname>&2)]'
Em seguida, a expansão de $((1-$x))
ou $((1-x))
faz com que esse uname
comando seja executado (pois zsh
, a
precisa ser uma variável de matriz, mas pode-se usar, psvar
por exemplo, para isso).
Em resumo, não se deve usar uninitialised ou não higienizado dados externos em expressões aritméticas em conchas (nota que a avaliação aritmética pode ser feito por $((...))
(aka $[...]
em bash
ou zsh
), mas também em função do shell no let
, [
/ test
, declare/typeset/export...
, return
, break
, continue
, exit
, printf
, print
builtins, índices de matriz ((..))
e [[...]]
construções para citar alguns).
Para verificar se uma variável contém um número inteiro decimal literal, você pode usar POSIXly:
case $var in
("" | - | *[!0123456789-]* | ?*-*) echo >&2 not a valid number; exit 1;;
esac
Lembre-se de que [0-9]
em alguns locais corresponde a mais de 0123456789. [[:digit:]]
deve estar OK, mas eu não apostaria nisso.
Lembre-se também de que números com zeros à esquerda são tratados como octais em alguns contextos ( 010
às vezes 10, às vezes 8) e tenha cuidado para que a verificação acima permita números que são potencialmente maiores que o número máximo máximo suportado pelo seu sistema (ou qualquer aplicativo que você desejar) use esse número inteiro em; bash, por exemplo, trata 18446744073709551616 como 0, pois é 2 64 ). Portanto, você pode querer adicionar verificações extras nessa declaração de caso acima, como:
(0?* | -0?*)
echo >&2 'Only decimal numbers without leading 0 accepted'; exit 1;;
(-??????????* | [!-]?????????*)
echo >&2 'Only numbers from -999999999 to 999999999 supported'; exit 1;;
Exemplos:
$ 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
Mais leitura em:
x='P=3'; : $(($x + 5))
irá definirP
a 8, masx='P=3'; : $((x + 5))
irá definirP
a3
(emzsh
,ksh
oubash
). "O mesmo acontece com$((x + 1))
..." não está correto agora; ele irá definirPATH
para2
, como antigamente.