addressSanitizer (ASan) là một công cụ dựa trên trình biên dịch nhanh để phát hiện lỗi bộ nhớ trong mã gốc.
ASan phát hiện:
- Tràn/tràn bộ đệm đống và ngăn xếp
- Sử dụng heap sau khi miễn phí
- Ngăn xếp sử dụng phạm vi bên ngoài
- Miễn phí gấp đôi/miễn phí hoang dã
ASan chạy trên cả ARM 32 bit và 64 bit, cộng với x86 và x86-64. Chi phí sử dụng CPU của ASan là khoảng 2 lần, chi phí sử dụng kích thước mã nằm trong khoảng từ 50% đến 2x và chi phí bộ nhớ lớn (phụ thuộc vào kiểu phân bổ của bạn, nhưng theo thứ tự 2x).
Android 10 và nhánh chính AOSP trên AArch64 hỗ trợ ASan (HWASan) được tăng tốc phần cứng , một công cụ tương tự với chi phí RAM thấp hơn và phạm vi lỗi được phát hiện lớn hơn. HWASan phát hiện việc sử dụng ngăn xếp sau khi quay trở lại, bên cạnh các lỗi được ASan phát hiện.
HWASan có chi phí sử dụng CPU và kích thước mã tương tự, nhưng chi phí sử dụng RAM nhỏ hơn nhiều (15%). HWASan là không xác định. Chỉ có 256 giá trị thẻ có thể có, do đó có xác suất cố định là 0,4% thiếu bất kỳ lỗi nào. HWASan không có các vùng màu đỏ có kích thước giới hạn của ASan để phát hiện tràn và cách ly dung lượng giới hạn để phát hiện hết lần sử dụng, do đó, đối với HWASan mức độ tràn lớn như thế nào hoặc bộ nhớ đã được giải phóng bao lâu không quan trọng. Điều này làm cho HWASan tốt hơn ASan. Bạn có thể đọc thêm về thiết kế của HWASan hoặc về cách sử dụng HWASan trên Android .
ASan phát hiện lỗi tràn ngăn xếp/tràn toàn cục bên cạnh lỗi tràn heap và hoạt động nhanh chóng với chi phí bộ nhớ tối thiểu.
Tài liệu này mô tả cách xây dựng và chạy các phần/toàn bộ Android bằng ASan. Nếu bạn đang xây dựng ứng dụng SDK/NDK bằng ASan, thay vào đó hãy xem Trình dọn dẹp địa chỉ .
Vệ sinh các tệp thực thi riêng lẻ bằng ASan
Thêm LOCAL_SANITIZE:=address
hoặc sanitize: { address: true }
vào quy tắc xây dựng cho tệp thực thi. Bạn có thể tìm kiếm mã cho các ví dụ hiện có hoặc tìm các chất khử trùng có sẵn khác.
Khi phát hiện thấy lỗi, ASan sẽ in một báo cáo chi tiết ra cả đầu ra tiêu chuẩn và logcat
, sau đó làm hỏng quy trình.
Vệ sinh thư viện dùng chung với ASan
Do cách thức hoạt động của ASan, thư viện được xây dựng bằng ASan chỉ có thể được sử dụng bởi tệp thực thi được xây dựng bằng ASan.
Để vệ sinh một thư viện dùng chung được sử dụng trong nhiều tệp thực thi, không phải tất cả đều được xây dựng bằng ASan, bạn cần có hai bản sao của thư viện. Cách được đề xuất để thực hiện việc này là thêm phần sau vào Android.mk
cho mô-đun được đề cập:
LOCAL_SANITIZE:=address LOCAL_MODULE_RELATIVE_PATH := asan
Điều này đặt thư viện vào /system/lib/asan
thay vì /system/lib
. Sau đó, chạy tệp thực thi của bạn với:
LD_LIBRARY_PATH=/system/lib/asan
Đối với trình nền hệ thống, hãy thêm phần sau vào phần thích hợp của /init.rc
hoặc /init.$device$.rc
.
setenv LD_LIBRARY_PATH /system/lib/asan
Xác minh rằng quy trình đang sử dụng các thư viện từ /system/lib/asan
khi có mặt bằng cách đọc /proc/$PID/maps
. Nếu không, bạn có thể cần phải tắt SELinux:
adb root
adb shell setenforce 0
# restart the process with adb shell kill $PID # if it is a system service, or may be adb shell stop; adb shell start.
Dấu vết ngăn xếp tốt hơn
ASan sử dụng trình gỡ bỏ nhanh, dựa trên con trỏ khung để ghi lại dấu vết ngăn xếp cho mọi sự kiện cấp phát và giải phóng bộ nhớ trong chương trình. Hầu hết Android được xây dựng mà không có con trỏ khung. Kết quả là bạn thường chỉ nhận được một hoặc hai khung hình có ý nghĩa. Để khắc phục điều này, hãy xây dựng lại thư viện bằng ASan (được khuyến nghị!) hoặc bằng:
LOCAL_CFLAGS:=-fno-omit-frame-pointer LOCAL_ARM_MODE:=arm
Hoặc đặt ASAN_OPTIONS=fast_unwind_on_malloc=0
trong môi trường quy trình. Cái sau có thể rất tốn CPU, tùy thuộc vào tải.
Biểu tượng hóa
Ban đầu, báo cáo ASan chứa các tham chiếu đến độ lệch trong tệp nhị phân và thư viện dùng chung. Có hai cách để lấy thông tin về tệp nguồn và dòng:
- Đảm bảo rằng tệp nhị phân
llvm-symbolizer
có trong/system/bin
.llvm-symbolizer
được xây dựng từ các nguồn trongthird_party/llvm/tools/llvm-symbolizer
. - Lọc báo cáo thông qua tập lệnh
external/compiler-rt/lib/asan/scripts/symbolize.py
.
Cách tiếp cận thứ hai có thể cung cấp nhiều dữ liệu hơn (nghĩa là vị trí file:line
) do có sẵn các thư viện được ký hiệu trên máy chủ.
ASan trong ứng dụng
ASan không thể nhìn vào mã Java, nhưng nó có thể phát hiện lỗi trong thư viện JNI. Để làm được điều đó, bạn cần xây dựng tệp thực thi bằng ASan, trong trường hợp này là /system/bin/app_process( 32|64 )
. Điều này cho phép ASan chạy đồng thời tất cả các ứng dụng trên thiết bị. Đây là một tải nặng nhưng một thiết bị có RAM 2 GB sẽ có thể xử lý việc này.
Thêm LOCAL_SANITIZE:=address
vào quy tắc xây dựng app_process
trong frameworks/base/cmds/app_process
. Bây giờ hãy bỏ qua mục tiêu app_process__asan
trong cùng một tệp (nếu nó vẫn ở đó vào thời điểm bạn đọc nội dung này).
Chỉnh sửa phần service zygote
của system/core/rootdir/init.zygote( 32|64 ).rc
thích hợp để thêm các dòng sau vào khối các dòng thụt lề có chứa class main
, cũng được thụt lề với cùng một lượng:
setenv LD_LIBRARY_PATH /system/lib/asan:/system/lib setenv ASAN_OPTIONS allow_user_segv_handler=true
Xây dựng, đồng bộ hóa adb, khởi động flash fastboot và khởi động lại.
Sử dụng thuộc tính bọc
Cách tiếp cận trong phần trước đưa ASan vào mọi ứng dụng trong hệ thống (thực ra là vào mọi hậu duệ của quy trình Zygote). Có thể chỉ chạy một (hoặc một số) ứng dụng với ASan, đánh đổi một số chi phí bộ nhớ để khởi động ứng dụng chậm hơn.
Điều này có thể được thực hiện bằng cách khởi động ứng dụng của bạn bằng wrap.
tài sản. Ví dụ sau chạy ứng dụng Gmail trong ASan:
adb root
adb shell setenforce 0 # disable SELinux
adb shell setprop wrap.com.google.android.gm "asanwrapper"
Trong ngữ cảnh này, asanwrapper
viết lại /system/bin/app_process
thành /system/bin/asan/app_process
, được xây dựng bằng ASan. Nó cũng thêm /system/lib/asan
vào đầu đường dẫn tìm kiếm thư viện động. Bằng cách này, các thư viện được thiết kế bởi ASan từ /system/lib/asan
được ưu tiên hơn các thư viện thông thường trong /system/lib
khi chạy với asanwrapper
.
Nếu tìm thấy lỗi, ứng dụng sẽ gặp sự cố và báo cáo sẽ được in vào nhật ký.
VỆ SINH_TARGET
Android 7.0 trở lên bao gồm hỗ trợ xây dựng toàn bộ nền tảng Android với ASan cùng một lúc. (Nếu bạn đang xây dựng một bản phát hành cao hơn Android 9, HWASan là lựa chọn tốt hơn.)
Chạy các lệnh sau trong cùng một cây xây dựng.
make -j42
SANITIZE_TARGET=address make -j42
Ở chế độ này, userdata.img
chứa các thư viện bổ sung và cũng phải được flash vào thiết bị. Sử dụng dòng lệnh sau:
fastboot flash userdata && fastboot flashall
Điều này xây dựng hai bộ thư viện dùng chung: normal trong /system/lib
(lệnh gọi make đầu tiên) và ASan-instrumented trong /data/asan/lib
(lệnh gọi make thứ hai). Các tệp thực thi từ bản dựng thứ hai sẽ ghi đè lên các tệp từ bản dựng đầu tiên. Các tệp thực thi được thiết kế bằng công cụ ASan có đường dẫn tìm kiếm thư viện khác bao gồm /data/asan/lib
trước /system/lib
thông qua việc sử dụng /system/bin/linker_asan
trong PT_INTERP
.
Hệ thống xây dựng sẽ chặn các thư mục đối tượng trung gian khi giá trị $SANITIZE_TARGET
thay đổi. Điều này buộc phải xây dựng lại tất cả các mục tiêu trong khi vẫn bảo toàn các tệp nhị phân đã cài đặt trong /system/lib
.
Một số mục tiêu không thể được xây dựng bằng ASan:
- Các tệp thực thi được liên kết tĩnh
-
LOCAL_CLANG:=false
-
LOCAL_SANITIZE:=false
không được xác nhận choSANITIZE_TARGET=address
Các tệp thực thi như thế này bị bỏ qua trong bản dựng SANITIZE_TARGET
và phiên bản từ lệnh gọi make đầu tiên được để lại trong /system/bin
.
Những thư viện như thế này được xây dựng mà không có ASan. Chúng có thể chứa một số mã ASan từ các thư viện tĩnh mà chúng phụ thuộc vào.