AddressSanitizer

AddressSanitizer (ASan) là một công cụ dựa trên trình biên dịch giúp phát hiện lỗi bộ nhớ trong mã gốc.

ASan phát hiện:

  • Chặn tràn (overflow)/chặn trống (underflow) cho ngăn xếp (stack) và bộ nhớ khối xếp (heap)
  • Sử dụng bộ nhớ khối xếp sau khi giải phóng
  • Sử dụng ngăn xếp bên ngoài phạm vi
  • Giải phóng gấp đôi/Giải phóng đối tượng không phải bộ nhớ khối xếp

ASan chạy trên cả ARM 32-bit và 64-bit, cùng với x86 và x86-64. Mức hao tổn CPU của ASan khoảng 2x, mức hao tổn kích thước mã nằm trong khoảng từ 50% đến 2x và mức hao tổn bộ nhớ lớn (phụ thuộc vào mô hình 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ợ AddressSanitizer (HWASan) được phần cứng hỗ trợ, một công cụ tương tự có mức hao tổn RAM thấp hơn và một loạt các lỗi phát hiện được. HWASan phát hiện việc sử dụng ngăn xếp sau khi trả về, ngoài các lỗi do ASan phát hiện.

HWASan có mức hao tổn CPU và kích thước mã tương tự, nhưng mức hao tổn RAM nhỏ hơn nhiều (15%). HWASan là không xác định. Chỉ có 256 giá trị thẻ có thể có, vì vậy chỉ có 0,4% không đổi xác suất bỏ lỡ bất kỳ lỗi nào. HWASan không có vùng đỏ có kích thước giới hạn của ASan đối với phát hiện tình trạng tràn và cách ly dung lượng giới hạn để phát hiện các trường hợp sử dụng sau khi không dùng nữa, vì vậy, việc HWASan lớn đến mức nào hoặc cách bộ nhớ đã được giải quyết. Việc này giúp HWASan tốt hơn ASan. Bạn có thể đọc thêm về chính sách thiết kế của HWASan hoặc về việc sử dụng HWASan trên Android.

ASan phát hiện lỗi ngăn xếp/chung ngoài hiện tượng tràn vùng nhớ khối xếp, đồng thời hoạt động nhanh với mức hao tổn bộ nhớ ở mức tối thiểu.

Tài liệu này mô tả cách tạo và chạy các phần/tất cả các phần của Android bằng ASan. Nếu bạn đang xây dựng một ứng dụng SDK/NDK bằng ASan, hãy xem Address Sanitizer thay thế.

Dọn dẹp từng tệp thực thi 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 lỗi, ASan sẽ in một báo cáo chi tiết theo cả tiêu chuẩn xuất và logcat, sau đó gây ra sự cố quá trình này.

Dọn dẹp các thư viện dùng chung bằng ASan

Do cách thức hoạt động của ASan, chỉ một thư viện được xây dựng bằng ASan mới dùng được tệp thực thi được xây dựng bằng ASan.

Để dọn dẹp một thư viện dùng chung được dùng trong nhiều tệp thực thi, không phải tất cả được tạo bằng ASan, nên bạn cần có 2 bản sao của thư viện. Chiến lược phát hành đĩa đơn bạn nên thêm đoạn mã sau vào Android.mk cho mô-đun đang được đề cập:

LOCAL_SANITIZE:=address
LOCAL_MODULE_RELATIVE_PATH := asan

Thao tác này sẽ đặ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 đoạn mã 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 này đang sử dụng thư viện của /system/lib/asan khi hiển thị bằng cách đọc /proc/$PID/maps. Nếu không, có thể bạn phải để vô hiệu hoá 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 chính xác hơn

ASan sử dụng bộ tháo dỡ (unwinder) nhanh dựa trên con trỏ khung để ghi một ngăn xếp theo dõi mọi sự kiện phân bổ và giải phóng bộ nhớ trong chương trình. Thường gặp nhất Android được tạo mà không cần con trỏ khung. Do đó, bạn thường nhận được chỉ một hoặc hai khung có ý nghĩa. Để khắc phục vấn đề này, hãy tạo lại thư viện bằng ASan (nên dùng!) hoặc với:

LOCAL_CFLAGS:=-fno-omit-frame-pointer
LOCAL_ARM_MODE:=arm

Hoặc đặt ASAN_OPTIONS=fast_unwind_on_malloc=0 trong quá trình này môi trường. Chính sách sau có thể dùng rất nhiều CPU, tuỳ thuộc vào tải.

Biểu tượng

Ban đầu, báo cáo ASan chứa các tệp tham chiếu đến độ lệch trong tệp nhị phân và được dùng chung thư viện. Có hai cách để lấy thông tin về dòng và tệp nguồn:

  • Đảm bảo rằng tệp nhị phân llvm-symbolizer có trong /system/bin. llvm-symbolizer được tạo từ các nguồn bằng third_party/llvm/tools/llvm-symbolizer.
  • Lọc báo cáo thông qua external/compiler-rt/lib/asan/scripts/symbolize.py tập lệnh.

Cách tiếp cận thứ hai có thể cung cấp thêm dữ liệu (tức là file:line vị trí) vì khả năng sử dụng thư viện được biểu tượng hoá trên máy chủ lưu trữ.

ASan trong ứng dụng

ASan không xem được mã Java nhưng có thể phát hiện lỗi trong JNI thư viện. Do đó, bạn cần tạo tệp thực thi bằng ASan, trong đó trường hợp này là /system/bin/app_process(32|64). Chiến dịch này bật ASan trong tất cả ứng dụng trên thiết bị cùng một lúc, đây là một tải nặng nhưng thiết bị có RAM 2 GB sẽ có thể xử lý tải trọng này.

Thêm LOCAL_SANITIZE:=address vào quy tắc bản dựng app_process trong frameworks/base/cmds/app_process. Bỏ qua mục tiêu app_process__asan hiện đang ở trong cùng một tệp (nếu là ở đó vào thời điểm bạn đọc thông báo này).

Chỉnh sửa phần service zygote của tệp 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 được thụt lề chứa class main, cũng thụt lề có cùng số lượng:

    setenv LD_LIBRARY_PATH /system/lib/asan:/system/lib
    setenv ASAN_OPTIONS allow_user_segv_handler=true

Tạo, đồng bộ hoá adb, khởi động nhanh flash và khởi động lại.

Sử dụng thuộc tính bao bọc

Phương pháp trong phần trước đặt ASan vào mỗi ứng dụng trong hệ thống (trên thực tế, vào mọi con cháu của người Zygote quá trình). Bạn chỉ có thể chạy một (hoặc nhiều) ứng dụng bằng ASan, giao dịch một số mức hao tổn bộ nhớ để khởi động ứng dụng chậm hơn.

Bạn có thể thực hiện việc này bằng cách khởi động ứng dụng bằng thuộc tính wrap.. Ví dụ sau đây 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 trường hợp này, asanwrapper sẽ viết lại /system/bin/app_process vào /system/bin/asan/app_process, được xây dựng bằng ASan. Thao tác này cũng thêm /system/lib/asan vào đầu đường dẫn tìm kiếm thư viện động. Theo cách này, công cụ được đo lường bằng ASan thư viện từ /system/lib/asan được ưu tiên hơn thư viện thông thường trong /system/lib khi chạy bằng 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 ra nhật ký.

Hàm SANITIZE_TARGET

Android 7.0 trở lên có hỗ trợ xây dựng toàn bộ nền tảng Android nhờ ASan cùng lúc. (Nếu bạn đang xây dựng một bản phát hành cao hơn Android 9, thì HWASan là lựa chọn phù hợp hơn.)

Chạy các lệnh sau trong cùng một cây bản dựng.

make -j42
SANITIZE_TARGET=address make -j42

Ở chế độ này, userdata.img chứa thêm thư viện và phải được được cài đặt ROM cho thiết bị. Sử dụng dòng lệnh sau:

fastboot flash userdata && fastboot flashall

Thao tác này sẽ tạo hai nhóm thư viện dùng chung: bình thường trong /system/lib (lệnh gọi thực hiện đầu tiên) và được đo lường bằng ASan trong /data/asan/lib (lệnh gọi thực hiện thứ hai). Các tệp thực thi từ bản dựng thứ hai sẽ ghi đè các bản dựng từ bản dựng đầu tiên. Được đo lường bằng ASan các tệp thực thi sẽ nhận được đường dẫn tìm kiếm thư viện khác nhau 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 chặn các thư mục đối tượng trung gian khi Giá trị $SANITIZE_TARGET đã thay đổi. Thao tác này buộc tạo lại tất cả trong khi vẫn giữ lại các tệp nhị phân đã cài đặt trong /system/lib.

Không thể tạo một số mục tiêu bằng ASan:

  • Các tệp thực thi được liên kết tĩnh
  • LOCAL_CLANG:=false mục tiêu
  • LOCAL_SANITIZE:=false không phải là ASan cho SANITIZE_TARGET=address

Các tệp thực thi như thế này sẽ bị bỏ qua trong bản dựng SANITIZE_TARGET và phiên bản của lệnh gọi make đầu tiên được để lại trong /system/bin.

Các thư viện như thế này được xây dựng mà không cần ASan. Họ có thể chứa một số ASan từ các thư viện tĩnh mà chúng phụ thuộc.

Tài liệu hỗ trợ