Vấn đề là trong trường hợp nội dung của $xnó 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ế PATHbằng LD_LIBRARY_PATHhoặ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 $xmộ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--1biể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, zshvà ksh(không dashhay yash), nếu xlà:
x='a[0$(uname>&2)]'
Sau đó, việc mở rộng $((1-$x))hoặc $((1-x))khiến unamelệnh đó được thực thi (for zsh, acần phải là một biến mảng, nhưng người ta có thể sử dụng psvarví 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 bashhoặc zsh) mà còn phụ thuộc vào vỏ trong let, [/ test, declare/typeset/export..., return, break, continue, exit, printf, printnộ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,kshhoặ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.