Vấn đề là trong trường hợp nội dung của $x
nó không được vệ sinh và chứa dữ liệu có khả năng nằm dưới sự kiểm soát của kẻ tấn công trong trường hợp mã shell có thể sẽ được sử dụng trong bối cảnh leo thang đặc quyền (ví dụ: tập lệnh được gọi bởi setuid ứng dụng, tập lệnh sudoers hoặc được sử dụng để xử lý dữ liệu ngoài mạng (CGI, DHCP hook ...) trực tiếp hoặc gián tiếp).
Nếu:
x='(PATH=2)'
Sau đó:
x=$((1-$x)))
có tác dụng phụ của việc thiết PATH
để 2
(một đường dẫn tương đối mà rất có thể là dưới sự kiểm soát của những kẻ tấn công). Bạn có thể thay thế PATH
bằng LD_LIBRARY_PATH
hoặc IFS
... Điều tương tự xảy ra với x=$((1-x))
bash, zsh hoặc ksh (không phải dấu gạch ngang cũng như yash chỉ chấp nhận các hằng số trong các biến ở đó).
Lưu ý rằng:
x=$((1-$x))
sẽ không hoạt động chính xác cho các giá trị âm của $x
một số shell triển khai toán tử (tùy chọn theo POSIX) --
(giảm dần) x=-1
, như có nghĩa là yêu cầu shell đánh giá 1--1
biểu thức số học). "$((1-x))"
không có vấn đề như x
được mở rộng như là một phần của (không phải trước đây) việc đánh giá số học.
Trong bash
, zsh
và ksh
(không dash
hay yash
), nếu x
là:
x='a[0$(uname>&2)]'
Sau đó, việc mở rộng $((1-$x))
hoặc $((1-x))
khiến uname
lệnh đó được thực thi (for zsh
, a
cần phải là một biến mảng, nhưng người ta có thể sử dụng psvar
ví dụ cho điều đó).
Nói tóm lại, người ta không nên sử dụng uninitialised hoặc không vệ sinh dữ liệu bên ngoài trong các biểu thức số học trong vỏ (lưu ý rằng đánh giá số học có thể được thực hiện bằng $((...))
(aka $[...]
trong bash
hoặc zsh
) mà còn phụ thuộc vào vỏ trong let
, [
/ test
, declare/typeset/export...
, return
, break
, continue
, exit
, printf
, print
nội trang, chỉ mục mảng ((..))
và [[...]]
cấu trúc để đặt tên cho một số).
Để kiểm tra xem một biến có chứa số nguyên thập phân bằng chữ hay không, bạn có thể sử dụng POSIXly:
case $var in
("" | - | *[!0123456789-]* | ?*-*) echo >&2 not a valid number; exit 1;;
esac
Coi chừng rằng [0-9]
ở một số địa phương khớp với hơn 0123456789. [[:digit:]]
sẽ ổn thôi nhưng tôi sẽ không đặt cược vào nó.
Cũng cần nhớ rằng các số có số 0 đứng đầu được coi là số bát phân trong một số ngữ cảnh ( 010
đôi khi là 10, đôi khi là 8) và hãy cẩn thận rằng kiểm tra ở trên sẽ cho phép các số có khả năng lớn hơn số nguyên tối đa được hệ thống của bạn hỗ trợ (hoặc bất kỳ ứng dụng nào bạn sẽ sử dụng số nguyên đó trong; bash ví dụ xử lý 18446744073709551616 là 0 như 2 64 ). Vì vậy, bạn có thể muốn thêm kiểm tra bổ sung trong trường hợp đó ở trên như:
(0?* | -0?*)
echo >&2 'Only decimal numbers without leading 0 accepted'; exit 1;;
(-??????????* | [!-]?????????*)
echo >&2 'Only numbers from -999999999 to 999999999 supported'; exit 1;;
Ví dụ:
$ 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
Đọc thêm tại:
x='P=3'; : $(($x + 5))
sẽ thiết lậpP
đến 8, nhưngx='P=3'; : $((x + 5))
sẽ thiết lậpP
để3
(trongzsh
,ksh
hoặcbash
). "Điều tương tự xảy ra với$((x + 1))
..." hiện không chính xác; nó sẽ thiết lậpPATH
để2
, như thuở xưa.