Scudo

Scudo là một trình phân bổ bộ nhớ chế độ người dùng động hoặc trình phân bổ vùng nhớ khối xếp, được thiết kế để có khả năng chống lại các lỗ hổng liên quan đến vùng nhớ khối xếp (chẳng hạn như tràn bộ đệm dựa trên vùng nhớ khối xếp, sử dụng sau khi giải phónggiải phóng hai lần) trong khi vẫn duy trì hiệu suất. Nó cung cấp các nguyên tắc phân bổ và huỷ phân bổ C tiêu chuẩn (chẳng hạn như malloc và free), cũng như các nguyên tắc C++ (chẳng hạn như new và delete).

Scudo là một giải pháp giảm thiểu hơn là một trình phát hiện lỗi bộ nhớ hoàn chỉnh như AddressSanitizer (ASan).

Kể từ bản phát hành Android 11, scudo được dùng cho tất cả mã gốc (ngoại trừ trên các thiết bị có bộ nhớ thấp, nơi jemalloc vẫn được dùng). Trong thời gian chạy, tất cả các hoạt động phân bổ và huỷ phân bổ vùng nhớ heap gốc đều được Scudo xử lý cho tất cả các tệp thực thi và các phần phụ thuộc thư viện của chúng, đồng thời quy trình sẽ bị huỷ nếu phát hiện thấy hành vi tham nhũng hoặc hành vi đáng ngờ trong vùng nhớ heap.

Scudo là nguồn mở và là một phần của dự án compiler-rt của LLVM. Bạn có thể xem tài liệu tại https://llvm.org/docs/ScudoHardenedAllocator.html. Thời gian chạy Scudo được phân phối trong bộ công cụ Android và hỗ trợ đã được thêm vào Soong và Make để cho phép dễ dàng bật trình phân bổ trong tệp nhị phân.

Bạn có thể bật hoặc tắt biện pháp giảm thiểu bổ sung trong trình phân bổ bằng cách sử dụng các lựa chọn được mô tả bên dưới.

Tuỳ chỉnh

Bạn có thể xác định một số tham số của trình phân bổ theo từng quy trình thông qua một số cách:

  • Tĩnh: Xác định một hàm __scudo_default_options trong chương trình trả về chuỗi lựa chọn cần phân tích cú pháp. Hàm này phải có nguyên mẫu sau: extern "C" const char *__scudo_default_options().
  • Động: Sử dụng biến môi trường SCUDO_OPTIONS chứa chuỗi lựa chọn cần phân tích cú pháp. Các lựa chọn được xác định theo cách này sẽ ghi đè mọi định nghĩa được thực hiện thông qua __scudo_default_options.

Bạn có thể chọn trong các phương án sau đây.

Lựa chọn Mặc định là 64 bit Mặc định 32 bit Mô tả
QuarantineSizeKb 256 64 Kích thước (tính bằng KB) của vùng cách ly dùng để trì hoãn việc giải phóng thực tế các khối. Giá trị thấp hơn có thể giảm mức sử dụng bộ nhớ nhưng làm giảm hiệu quả của việc giảm thiểu; giá trị âm sẽ quay lại giá trị mặc định. Việc đặt cả chế độ này và ThreadLocalQuarantineSizeKb thành 0 sẽ vô hiệu hoá hoàn toàn chế độ cách ly.
QuarantineChunksUpToSize 2048 512 Kích thước (tính bằng byte) mà các khối có thể được cách ly.
ThreadLocalQuarantineSizeKb 64 16 Kích thước (tính bằng KB) của bộ nhớ đệm trên mỗi luồng dùng để giảm tải quy trình cách ly toàn cầu. Giá trị thấp hơn có thể giảm mức sử dụng bộ nhớ nhưng có thể làm tăng mức tranh chấp trên vùng cách ly toàn cầu. Việc đặt cả thông số này và QuarantineSizeKb thành 0 sẽ vô hiệu hoá hoàn toàn tính năng cách ly.
DeallocationTypeMismatch false false Cho phép báo cáo lỗi trên malloc/delete, new/free, new/delete[]
DeleteSizeMismatch true true Cho phép báo cáo lỗi về sự không khớp giữa kích thước của các mục mới và mục bị xoá.
ZeroContents false false Cho phép nội dung có 0 khối khi phân bổ và huỷ phân bổ.
allocator_may_return_null false false Chỉ định rằng trình phân bổ có thể trả về giá trị rỗng khi xảy ra lỗi có thể khôi phục, thay vì kết thúc quy trình.
hard_rss_limit_mb 0 0 Khi RSS của quy trình đạt đến giới hạn này, quy trình sẽ chấm dứt.
soft_rss_limit_mb 0 0 Khi RSS của quy trình đạt đến giới hạn này, các hoạt động phân bổ tiếp theo sẽ không thành công hoặc trả về null (tuỳ thuộc vào giá trị của allocator_may_return_null), cho đến khi RSS giảm xuống để cho phép phân bổ mới.
allocator_release_to_os_interval_ms 5000 Không áp dụng Chỉ ảnh hưởng đến trình phân bổ 64 bit. Nếu được đặt, hãy cố gắng giải phóng bộ nhớ không dùng đến cho hệ điều hành, nhưng không thường xuyên hơn khoảng thời gian này (tính bằng mili giây). Nếu giá trị là số âm, thì bộ nhớ sẽ không được giải phóng cho hệ điều hành.
abort_on_error true true Nếu được đặt, công cụ sẽ gọi abort() thay vì _exit() sau khi in thông báo lỗi.

Xác nhận kết quả

Hiện tại, không có kiểm thử CTS nào dành riêng cho Scudo. Thay vào đó, hãy đảm bảo rằng các kiểm thử CTS vượt qua khi Scudo được bật hoặc không được bật cho một tệp nhị phân nhất định để xác minh rằng tệp nhị phân đó không ảnh hưởng đến thiết bị.

Khắc phục sự cố

Nếu phát hiện thấy vấn đề không thể khôi phục, trình phân bổ sẽ hiển thị thông báo lỗi cho trình mô tả lỗi tiêu chuẩn rồi kết thúc quy trình. Các dấu vết ngăn xếp dẫn đến việc chấm dứt sẽ được thêm vào nhật ký hệ thống. Đầu ra thường bắt đầu bằng Scudo ERROR:, theo sau là một bản tóm tắt ngắn gọn về vấn đề cùng với mọi con trỏ.

Dưới đây là danh sách các thông báo lỗi hiện tại và nguyên nhân có thể gây ra lỗi:

  • corrupted chunk header: Không xác minh được tổng kiểm của tiêu đề khối. Điều này có thể là do một trong hai nguyên nhân: tiêu đề đã bị ghi đè (một phần hoặc toàn bộ) hoặc con trỏ được truyền đến hàm không phải là một khối.
  • race on chunk header: Hai luồng khác nhau đang cố gắng thao tác cùng lúc trên cùng một tiêu đề. Điều này thường là triệu chứng của tình trạng xung đột dữ liệu hoặc thiếu khoá chung khi thực hiện các thao tác trên khối đó.
  • invalid chunk state: Đoạn không ở trạng thái dự kiến cho một thao tác nhất định, ví dụ: đoạn không được phân bổ khi cố gắng giải phóng hoặc không được cách ly khi cố gắng tái chế. Lỗi giải phóng gấp đôi là nguyên nhân thường gặp gây ra lỗi này.
  • misaligned pointer: Các yêu cầu cơ bản về việc căn chỉnh được thực thi nghiêm ngặt: 8 byte trên nền tảng 32 bit và 16 byte trên nền tảng 64 bit. Nếu một con trỏ được truyền đến các hàm của chúng tôi không phù hợp với những con trỏ đó, thì con trỏ được truyền đến một trong các hàm sẽ không được căn chỉnh.
  • allocation type mismatch: Khi lựa chọn này được bật, một hàm giải phóng được gọi trên một đoạn phải khớp với loại hàm đã được gọi để phân bổ đoạn đó. Loại lỗi không khớp này có thể gây ra các vấn đề về bảo mật.
  • invalid sized delete: Khi toán tử xoá có kích thước C++14 được dùng và chế độ kiểm tra không bắt buộc được bật, sẽ có sự không khớp giữa kích thước được truyền khi huỷ phân bổ một đoạn và kích thước được yêu cầu khi phân bổ đoạn đó. Đây thường là vấn đề về trình biên dịch hoặc nhầm lẫn về kiểu trên đối tượng đang được huỷ cấp phát.
  • RSS limit exhausted: Bạn đã vượt quá số lượng RSS tối đa (không bắt buộc).

Nếu đang gỡ lỗi sự cố trong chính hệ điều hành, bạn có thể sử dụng bản dựng hệ điều hành HWASan. Nếu đang gỡ lỗi sự cố trong một ứng dụng, bạn cũng có thể sử dụng bản dựng ứng dụng HWASan.