UndefinedBehaviorSanitizer (UBSan) thực hiện hoạt động đo lường tại thời điểm biên dịch để kiểm tra nhiều loại hành vi không xác định. Mặc dù UBSan có thể phát hiện nhiều lỗi hành vi không xác định, nhưng Android hỗ trợ:
- căn chỉnh
- bool
- giới hạn
- enum
- float-cast-overflow
- float-divide-by-zero
- integer-divide-by-zero
- nonnull-attribute
- null
- câu lệnh trả về
- returns-nonnull-attribute
- shift-base
- shift-exponent
- signed-integer-overflow
- không truy cập được
- unsigned-integer-overflow
- vla-bound
unsigned-integer-overflow, mặc dù không phải là hành vi không xác định về mặt kỹ thuật, nhưng được đưa vào trình dọn dẹp và được sử dụng trong nhiều mô-đun Android, bao gồm cả các thành phần mediaserver, để loại bỏ mọi lỗ hổng tiềm ẩn liên quan đến tràn số nguyên.
Triển khai
Trong hệ thống xây dựng Android, bạn có thể bật UBSan trên toàn cục hoặc cục bộ. Để bật UBSan trên toàn cục, hãy đặt SANITIZE_TARGET trong Android.mk. Để bật UBSan ở cấp mô-đun, hãy đặt LOCAL_SANITIZE và chỉ định các hành vi chưa xác định mà bạn muốn tìm trong Android.mk. Ví dụ:
LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_CFLAGS := -std=c11 -Wall -Werror -O0 LOCAL_SRC_FILES:= sanitizer-status.c LOCAL_MODULE:= sanitizer-status LOCAL_SANITIZE := alignment bounds null unreachable integer LOCAL_SANITIZE_DIAG := alignment bounds null unreachable integer include $(BUILD_EXECUTABLE)
Và cấu hình bản thiết kế tương đương (Android.bp):
cc_binary { cflags: [ "-std=c11", "-Wall", "-Werror", "-O0", ], srcs: ["sanitizer-status.c"], name: "sanitizer-status", sanitize: { misc_undefined: [ "alignment", "bounds", "null", "unreachable", "integer", ], diag: { misc_undefined: [ "alignment", "bounds", "null", "unreachable", "integer", ], }, }, }
Lối tắt UBSan
Android cũng có hai lối tắt, integer
và default-ub
, để bật một bộ trình dọn dẹp cùng một lúc. integer bật integer-divide-by-zero
, signed-integer-overflow
và unsigned-integer-overflow
.
default-ub
cho phép các hoạt động kiểm tra có ít vấn đề về hiệu suất của trình biên dịch nhất: bool, integer-divide-by-zero, return,
returns-nonnull-attribute, shift-exponent, unreachable and vla-bound
. Bạn có thể sử dụng lớp trình dọn dẹp số nguyên với SANITIZE_TARGET và LOCAL_SANITIZE, trong khi chỉ có thể sử dụng default-ub với SANITIZE_TARGET.
Báo cáo lỗi hiệu quả hơn
Việc triển khai UBSan mặc định của Android sẽ gọi một hàm được chỉ định khi gặp hành vi không xác định. Theo mặc định, hàm này là huỷ. Tuy nhiên, kể từ tháng 10 năm 2016, UBSan trên Android có một thư viện thời gian chạy không bắt buộc cung cấp báo cáo lỗi chi tiết hơn, bao gồm cả loại hành vi không xác định gặp phải, thông tin về tệp và dòng mã nguồn. Để bật tính năng báo cáo lỗi này bằng các bước kiểm tra số nguyên, hãy thêm nội dung sau vào tệp Android.mk:
LOCAL_SANITIZE:=integer LOCAL_SANITIZE_DIAG:=integer
Giá trị LOCAL_SANITIZE bật trình dọn dẹp trong quá trình xây dựng. LOCAL_SANITIZE_DIAG bật chế độ chẩn đoán cho trình dọn dẹp được chỉ định. Bạn có thể đặt LOCAL_SANITIZE và LOCAL_SANITIZE_DIAG thành các giá trị khác nhau, nhưng chỉ những bước kiểm tra đó trong LOCAL_SANITIZE mới được bật. Nếu bạn không chỉ định một quy trình kiểm tra trong LOCAL_SANITIZE, nhưng lại chỉ định trong LOCAL_SANITIZE_DIAG, thì quy trình kiểm tra đó sẽ không được bật và thông báo chẩn đoán sẽ không được đưa ra.
Dưới đây là ví dụ về thông tin do thư viện thời gian chạy UBSan cung cấp:
pixel-xl:/ # sanitizer-status ubsan sanitizer-status/sanitizer-status.c:53:6: runtime error: unsigned integer overflow: 18446744073709551615 + 1 cannot be represented in type 'size_t' (aka 'unsigned long')
Dọn dẹp tràn số nguyên
Tình trạng tràn số nguyên ngoài ý muốn có thể gây ra sự cố hỏng bộ nhớ hoặc các lỗ hổng tiết lộ thông tin trong các biến liên kết với hoạt động truy cập bộ nhớ hoặc phân bổ bộ nhớ. Để chống lại vấn đề này, chúng tôi đã thêm trình dọn dẹp tràn số nguyên đã ký và chưa ký UndefinedBehaviorSanitizer (UBSan) của Clang để củng cố khung nội dung nghe nhìn trong Android 7.0. Trong Android 9, chúng tôi đã mở rộng UBSan để bao gồm nhiều thành phần hơn và cải thiện khả năng hỗ trợ hệ thống xây dựng cho UBSan.
Tính năng này được thiết kế để thêm các bước kiểm tra xung quanh các toán tử / lệnh – có thể làm tràn – để huỷ một quy trình một cách an toàn nếu xảy ra tình trạng tràn. Các trình dọn dẹp này có thể giảm thiểu toàn bộ lớp lỗ hổng rò rỉ thông tin và hỏng bộ nhớ, trong đó nguyên nhân gốc rễ là tràn số nguyên, chẳng hạn như lỗ hổng Stagefright ban đầu.
Ví dụ và nguồn
Trình biên dịch cung cấp tính năng Xoá lỗi tràn số nguyên (IntSan) và thêm khả năng đo lường vào tệp nhị phân trong thời gian biên dịch để phát hiện lỗi tràn số học. Tính năng này được bật theo mặc định trong nhiều thành phần trên nền tảng, ví dụ: /platform/external/libnl/Android.bp
.
Triển khai
IntSan sử dụng trình dọn dẹp tràn số nguyên đã ký và chưa ký của UBSan. Tính năng giảm thiểu này được bật ở cấp mô-đun. Tính năng này giúp bảo mật các thành phần quan trọng của Android và không được tắt.
Bạn nên bật tính năng Xoá lỗi tràn số nguyên cho các thành phần khác. Các ứng cử viên lý tưởng là mã gốc đặc quyền hoặc mã gốc phân tích cú pháp dữ liệu đầu vào không đáng tin cậy của người dùng. Có một mức hao tổn hiệu suất nhỏ liên quan đến trình dọn dẹp phụ thuộc vào mức sử dụng mã và mức độ phổ biến của các phép tính số học. Dự kiến mức hao tổn nhỏ và kiểm tra xem hiệu suất có phải là vấn đề cần quan tâm hay không.
Hỗ trợ IntSan trong tệp makefile
Để bật IntSan trong tệp makefile, hãy thêm:
LOCAL_SANITIZE := integer_overflow # Optional features LOCAL_SANITIZE_DIAG := integer_overflow LOCAL_SANITIZE_BLOCKLIST := modulename_BLOCKLIST.txt
LOCAL_SANITIZE
lấy một danh sách trình dọn dẹp được phân tách bằng dấu phẩy, trong đóinteger_overflow
là một nhóm các tuỳ chọn được đóng gói sẵn cho các trình dọn dẹp tràn số nguyên đã ký và chưa ký riêng lẻ với danh sách chặn mặc định.LOCAL_SANITIZE_DIAG
bật chế độ chẩn đoán cho trình dọn dẹp. Chỉ sử dụng chế độ chẩn đoán trong quá trình kiểm thử vì chế độ này sẽ không huỷ khi tràn, hoàn toàn phủ nhận lợi thế bảo mật của biện pháp giảm thiểu. Hãy xem phần Khắc phục sự cố để biết thêm thông tin chi tiết.LOCAL_SANITIZE_BLOCKLIST
cho phép bạn chỉ định tệp DANH SÁCH CHẶN để ngăn các hàm và tệp nguồn bị dọn dẹp. Hãy xem phần Khắc phục sự cố để biết thêm thông tin chi tiết.
Nếu bạn muốn kiểm soát chi tiết hơn, hãy bật trình dọn dẹp riêng lẻ bằng một hoặc cả hai cờ:
LOCAL_SANITIZE := signed-integer-overflow, unsigned-integer-overflow LOCAL_SANITIZE_DIAG := signed-integer-overflow, unsigned-integer-overflow
Hỗ trợ IntSan trong tệp bản thiết kế
Để bật tính năng dọn dẹp tràn số nguyên trong tệp bản thiết kế, chẳng hạn như /platform/external/libnl/Android.bp
, hãy thêm:
sanitize: { integer_overflow: true, diag: { integer_overflow: true, }, BLOCKLIST: "modulename_BLOCKLIST.txt", },
Cũng giống như tệp make, thuộc tính integer_overflow
là một nhóm tuỳ chọn được đóng gói sẵn cho các trình dọn dẹp tràn số nguyên đã ký và chưa ký riêng lẻ có BLOCKLIST mặc định.
Tập hợp thuộc tính diag
cho phép chế độ chẩn đoán cho trình dọn dẹp. Chỉ sử dụng chế độ chẩn đoán trong quá trình kiểm thử. Chế độ chẩn đoán không huỷ khi tràn bộ nhớ, điều này hoàn toàn phủ nhận lợi thế bảo mật của biện pháp giảm thiểu trong bản dựng người dùng. Hãy xem phần Khắc phục sự cố để biết thêm thông tin chi tiết.
Thuộc tính BLOCKLIST
cho phép chỉ định tệp DANH SÁCH CHẶN để nhà phát triển ngăn việc dọn dẹp các hàm và tệp nguồn. Hãy xem phần Khắc phục sự cố để biết thêm thông tin chi tiết.
Để bật từng trình dọn dẹp, hãy sử dụng:
sanitize: { misc_undefined: ["signed-integer-overflow", "unsigned-integer-overflow"], diag: { misc_undefined: ["signed-integer-overflow", "unsigned-integer-overflow",], }, BLOCKLIST: "modulename_BLOCKLIST.txt", },
Khắc phục sự cố
Nếu đang bật tính năng dọn dẹp tràn số nguyên trong các thành phần mới hoặc dựa vào các thư viện nền tảng đã có tính năng dọn dẹp tràn số nguyên, bạn có thể gặp một số vấn đề với các trường hợp tràn số nguyên lành tính gây ra tình trạng huỷ. Bạn nên kiểm thử các thành phần đã bật tính năng dọn dẹp để đảm bảo có thể hiển thị các trường hợp tràn lành tính.
Để tìm các trường hợp huỷ do quá trình dọn dẹp trong bản dựng người dùng, hãy tìm các sự cố SIGABRT
có thông báo Huỷ cho biết tình trạng tràn bộ nhớ do UBSan phát hiện, chẳng hạn như:
pid: ###, tid: ###, name: Binder:### >>> /system/bin/surfaceflinger <<< signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr -------- Abort message: 'ubsan: sub-overflow'
Dấu vết ngăn xếp phải bao gồm hàm gây ra lỗi huỷ, tuy nhiên, tình trạng tràn xảy ra trong các hàm nội tuyến có thể không rõ ràng trong dấu vết ngăn xếp.
Để dễ dàng xác định nguyên nhân gốc rễ hơn, hãy bật tính năng chẩn đoán trong thư viện để kích hoạt thao tác huỷ và cố gắng tái hiện lỗi. Khi bạn bật tính năng chẩn đoán, quy trình sẽ không bị huỷ mà sẽ tiếp tục chạy. Việc không huỷ giúp tối đa hoá số lượng lỗi tràn lành tính trong một đường dẫn thực thi cụ thể mà không cần biên dịch lại sau khi khắc phục từng lỗi. Công cụ chẩn đoán sẽ tạo một thông báo lỗi bao gồm số dòng và tệp nguồn gây ra lỗi huỷ:
frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp:2188:32: runtime error: unsigned integer overflow: 0 - 1 cannot be represented in type 'size_t' (aka 'unsigned long')
Sau khi xác định được phép tính có vấn đề, hãy đảm bảo rằng tình trạng tràn bộ nhớ là vô hại và có chủ ý (ví dụ: không ảnh hưởng đến bảo mật). Bạn có thể giải quyết việc trình dọn dẹp bị huỷ bằng cách:
- Tái cấu trúc mã để tránh tình trạng tràn bộ nhớ (ví dụ)
- Vượt quá giới hạn một cách rõ ràng thông qua các hàm __builtin_*_overflow của Clang (ví dụ)
- Tắt tính năng dọn dẹp trong hàm bằng cách chỉ định thuộc tính
no_sanitize
(ví dụ) - Tắt tính năng dọn dẹp một hàm hoặc tệp nguồn thông qua tệp DANH SÁCH CHẶN (ví dụ)
Bạn nên sử dụng giải pháp chi tiết nhất có thể. Ví dụ: một hàm lớn có nhiều phép tính số học và một phép tính tràn duy nhất phải được tái cấu trúc thay vì toàn bộ hàm bị đưa vào DANH SÁCH CHẶN.
Sau đây là một số mẫu phổ biến có thể dẫn đến tình trạng tràn lành tính:
- Chuyển đổi ngầm ẩn xảy ra khi tràn không ký trước khi chuyển đổi sang loại đã ký (ví dụ)
- Thao tác xoá danh sách liên kết làm giảm chỉ mục vòng lặp khi xoá (ví dụ)
- Chỉ định loại không dấu cho -1 thay vì chỉ định giá trị tối đa thực tế (ví dụ)
- Vòng lặp giảm một số nguyên chưa ký trong điều kiện (ví dụ, ví dụ)
Nhà phát triển nên đảm bảo rằng các trường hợp trình dọn dẹp phát hiện tình trạng tràn thực sự là vô hại, không có tác dụng phụ ngoài ý muốn hoặc ảnh hưởng đến tính bảo mật trước khi tắt tính năng dọn dẹp.
Tắt IntSan
Bạn có thể tắt IntSan bằng DANH SÁCH CHẶN hoặc thuộc tính hàm. Chỉ tắt khi việc tái cấu trúc mã là không hợp lý hoặc nếu có hao tổn hiệu suất gây ra vấn đề.
Hãy xem tài liệu về Clang ngược dòng để biết thêm thông tin về cách tắt IntSan bằng các thuộc tính hàm và định dạng tệp DANH SÁCH CHẶN. Bạn nên đặt danh sách chặn trong phạm vi trình dọn dẹp cụ thể bằng cách sử dụng tên mục chỉ định trình dọn dẹp mục tiêu để tránh ảnh hưởng đến các trình dọn dẹp khác.
Xác nhận kết quả
Hiện tại, không có quy trình kiểm thử CTS nào dành riêng cho việc dọn dẹp tràn số nguyên. Thay vào đó, hãy đảm bảo rằng các kiểm thử CTS đã vượt qua dù có bật hay không bật IntSan để xác minh rằng tính năng này không ảnh hưởng đến thiết bị.
Dọn dẹp giới hạn
BoundsSanitizer (BoundSan) thêm khả năng đo lường vào tệp nhị phân để chèn các hoạt động kiểm tra giới hạn xung quanh các quyền truy cập vào mảng. Các bước kiểm tra này được thêm vào nếu trình biên dịch không thể chứng minh tại thời điểm biên dịch rằng quyền truy cập sẽ an toàn và nếu kích thước của mảng sẽ được biết tại thời gian chạy để có thể kiểm tra. Android 10 triển khai BoundSan trong Bluetooth và bộ mã hoá và giải mã. BoundSan do trình biên dịch cung cấp và được bật theo mặc định trong nhiều thành phần trên nền tảng.
Triển khai
BoundSan sử dụng trình dọn dẹp giới hạn của UBSan. Phương pháp giảm thiểu này được bật ở cấp mô-đun. Tính năng này giúp bảo mật các thành phần quan trọng của Android và không được tắt.
Bạn nên bật BoundSan cho các thành phần khác. Các ứng cử viên lý tưởng là mã gốc đặc quyền hoặc mã gốc phức tạp phân tích cú pháp dữ liệu đầu vào không đáng tin cậy của người dùng. Mức hao tổn hiệu suất liên quan đến việc bật BoundSan phụ thuộc vào số lượng lượt truy cập vào mảng không thể chứng minh là an toàn. Dự kiến mức hao tổn trung bình nhỏ và kiểm tra xem hiệu suất có phải là vấn đề cần quan tâm hay không.
Bật BoundSan trong tệp bản thiết kế
Bạn có thể bật BoundSan trong các tệp bản thiết kế bằng cách thêm "bounds"
vào thuộc tính dọn dẹp misc_undefined
cho các mô-đun nhị phân và thư viện:
sanitize: { misc_undefined: ["bounds"], diag: { misc_undefined: ["bounds"], }, BLOCKLIST: "modulename_BLOCKLIST.txt",
diag
Thuộc tính diag
bật chế độ chẩn đoán cho trình dọn dẹp.
Chỉ sử dụng chế độ chẩn đoán trong quá trình kiểm thử. Chế độ Chẩn đoán không huỷ khi xảy ra tình trạng tràn bộ nhớ, điều này làm mất đi lợi thế bảo mật của biện pháp giảm thiểu và gây ra mức hao tổn hiệu suất cao hơn, vì vậy, bạn không nên sử dụng chế độ này cho các bản dựng chính thức.
DANH SÁCH CHẶN
Thuộc tính BLOCKLIST
cho phép chỉ định tệp DANH SÁCH CHẶN mà nhà phát triển có thể sử dụng để ngăn các hàm và tệp nguồn bị dọn dẹp. Chỉ sử dụng thuộc tính này nếu bạn quan tâm đến hiệu suất và các tệp/hàm được nhắm mục tiêu đóng góp đáng kể. Kiểm tra thủ công các tệp/hàm này để đảm bảo rằng các quyền truy cập vào mảng là an toàn. Hãy xem phần Khắc phục sự cố để biết thêm thông tin chi tiết.
Bật BoundSan trong tệp makefile
Bạn có thể bật BoundSan trong tệp makefile bằng cách thêm "bounds"
vào biến LOCAL_SANITIZE
cho các mô-đun nhị phân và thư viện:
LOCAL_SANITIZE := bounds # Optional features LOCAL_SANITIZE_DIAG := bounds LOCAL_SANITIZE_BLOCKLIST := modulename_BLOCKLIST.txt
LOCAL_SANITIZE
chấp nhận danh sách trình dọn dẹp được phân tách bằng dấu phẩy.
LOCAL_SANITIZE_DIAG
bật chế độ chẩn đoán. Chỉ sử dụng chế độ chẩn đoán trong quá trình kiểm thử. Chế độ Chẩn đoán không huỷ khi tràn bộ nhớ, điều này làm mất đi lợi thế bảo mật của biện pháp giảm thiểu và gây hao tổn hiệu suất cao hơn, vì vậy, bạn không nên dùng chế độ này cho các bản dựng chính thức.
LOCAL_SANITIZE_BLOCKLIST
cho phép chỉ định tệp DANH SÁCH CHẶN để cho phép nhà phát triển ngăn việc dọn dẹp các hàm và tệp nguồn. Chỉ sử dụng thuộc tính này nếu bạn quan tâm đến hiệu suất và các tệp/hàm được nhắm mục tiêu đóng góp đáng kể. Kiểm tra thủ công các tệp/hàm này để đảm bảo rằng các quyền truy cập vào mảng là an toàn. Hãy xem phần Khắc phục sự cố để biết thêm thông tin chi tiết.
Tắt BoundSan
Bạn có thể tắt BoundSan trong các hàm và tệp nguồn bằng DANH SÁCH CHẶN hoặc thuộc tính hàm. Tốt nhất là bạn nên bật BoundSan, vì vậy, chỉ tắt tính năng này nếu hàm hoặc tệp đang tạo ra nhiều hao tổn hiệu suất và nguồn đã được xem xét theo cách thủ công.
Để biết thêm thông tin về cách tắt BoundSan bằng các thuộc tính hàm và định dạng tệp DANH SÁCH CHẶN, hãy tham khảo tài liệu về Clang LLVM. Giới hạn DANH SÁCH CHẶN cho trình dọn dẹp cụ thể bằng cách sử dụng tên phần chỉ định trình dọn dẹp mục tiêu để tránh ảnh hưởng đến các trình dọn dẹp khác.
Xác nhận kết quả
Không có quy trình kiểm thử CTS dành riêng cho BoundSan. Thay vào đó, hãy đảm bảo rằng các kiểm thử CTS đạt yêu cầu dù có bật hay không bật BoundSan để xác minh rằng tính năng này không ảnh hưởng đến thiết bị.
Khắc phục sự cố
Kiểm thử kỹ các thành phần sau khi bật BoundSan để đảm bảo mọi quyền truy cập ngoài phạm vi trước đây chưa được phát hiện đều được xử lý.
Bạn có thể dễ dàng xác định lỗi BoundSan vì các lỗi này bao gồm thông báo huỷ bia mộ sau:
pid: ###, tid: ###, name: Binder:### >>> /system/bin/foobar <<< signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr -------- Abort message: 'ubsan: out-of-bounds'
Khi chạy ở chế độ chẩn đoán, tệp nguồn, số dòng và giá trị chỉ mục sẽ được in vào logcat
. Theo mặc định, chế độ này không gửi thông báo huỷ. Xem lại logcat
để kiểm tra xem có lỗi nào không.
external/foo/bar.c:293:13: runtime error: index -1 out of bounds for type 'int [24]'