Bộ nhớ chỉ thực thi (XOM) cho tệp nhị phân AArch64

Theo mặc định, các phần mã thực thi cho tệp nhị phân hệ thống AArch64 được đánh dấu là chỉ thực thi (không đọc được) để giảm thiểu việc tấn công tái sử dụng mã đúng lúc. Mã kết hợp dữ liệu và mã với nhau và mã kiểm tra có chủ đích các phần này (mà không cần ánh xạ lại các phân đoạn bộ nhớ ở chế độ có thể đọc trước tiên) không còn hoạt động nữa. Các ứng dụng có SDK mục tiêu là 10 (API cấp 29 trở lên) sẽ bị ảnh hưởng nếu ứng dụng cố gắng đọc các phần mã của thư viện hệ thống hỗ trợ bộ nhớ chỉ thực thi (XOM) trong bộ nhớ mà không đánh dấu trước phần đó là có thể đọc được.

Để khai thác tối đa biện pháp giảm thiểu này, bạn cần có cả tính năng hỗ trợ phần cứng và nhân hệ điều hành. Nếu không có tính năng hỗ trợ này, biện pháp giảm thiểu có thể chỉ được thực thi một phần. Hạt nhân chung Android 4.9 chứa các bản vá thích hợp để hỗ trợ đầy đủ cho việc này trên các thiết bị ARMv8.2.

Triển khai

Tệp nhị phân AArch64 do trình biên dịch tạo ra giả định rằng mã và dữ liệu không bị lẫn lộn. Việc bật tính năng này không ảnh hưởng tiêu cực đến hiệu suất của thiết bị.

Đối với mã phải thực hiện hoạt động tự kiểm tra bộ nhớ có chủ ý trên các phân đoạn có thể thực thi, bạn nên gọi mprotect trên các phân đoạn mã yêu cầu kiểm tra để cho phép đọc được, sau đó xoá khả năng đọc được khi quá trình kiểm tra hoàn tất.
Việc triển khai này khiến các lượt đọc vào các phân đoạn bộ nhớ được đánh dấu là chỉ thực thi dẫn đến lỗi phân đoạn (SEGFAULT). Điều này có thể xảy ra do lỗi, lỗ hổng, dữ liệu bị trộn lẫn với mã (gộp dữ liệu cố định) hoặc hoạt động tự kiểm tra bộ nhớ có chủ ý.

Mức độ hỗ trợ và tác động của thiết bị

Các thiết bị có phần cứng hoặc hạt nhân cũ hơn (dưới 4.9) mà không có các bản vá bắt buộc có thể không hỗ trợ đầy đủ hoặc không được hưởng lợi từ tính năng này. Các thiết bị không hỗ trợ nhân hệ điều hành có thể không thực thi quyền truy cập của người dùng vào bộ nhớ chỉ có thể thực thi. Tuy nhiên, mã nhân hệ điều hành kiểm tra rõ ràng xem một trang có thể đọc được hay không vẫn có thể thực thi thuộc tính này, chẳng hạn như process_vm_readv().

Bạn phải đặt cờ hạt nhân CONFIG_ARM64_UAO trong hạt nhân để đảm bảo rằng hạt nhân tuân thủ các trang vùng người dùng được đánh dấu là chỉ thực thi. Các thiết bị ARMv8 cũ hơn hoặc thiết bị ARMv8.2 đã tắt tính năng Ghi đè quyền truy cập của người dùng (UAO) có thể không tận dụng được hết tính năng này và vẫn có thể đọc các trang chỉ thực thi bằng cách sử dụng các lệnh hệ thống.

Tái cấu trúc mã hiện có

Mã được chuyển từ AArch32 có thể chứa dữ liệu và mã được kết hợp, gây ra sự cố. Trong nhiều trường hợp, việc khắc phục các vấn đề này cũng đơn giản như việc di chuyển các hằng số sang phần .data trong tệp tập hợp.

Bạn có thể cần phải tái cấu trúc tập hợp viết tay để tách các hằng số được gộp cục bộ.

Ví dụ:

Tệp nhị phân do trình biên dịch Clang tạo ra sẽ không gặp vấn đề về dữ liệu được kết hợp trong mã. Nếu mã được tạo bằng bộ sưu tập trình biên dịch GNU (GCC) được đưa vào (từ thư viện tĩnh), hãy kiểm tra tệp nhị phân đầu ra để đảm bảo rằng các hằng số chưa được gộp vào các phần mã.

Nếu cần tự kiểm tra mã trên các phần mã có thể thực thi, trước tiên, hãy gọi mprotect để đánh dấu mã có thể đọc được. Sau khi thao tác hoàn tất, hãy gọi lại mprotect để đánh dấu tệp đó là không đọc được.

Bật XOM

Theo mặc định, chế độ chỉ thực thi được bật cho tất cả tệp nhị phân 64 bit trong hệ thống xây dựng.

Tắt XOM

Bạn có thể tắt chế độ chỉ thực thi ở cấp mô-đun, theo toàn bộ cây thư mục con hoặc trên toàn bộ bản dựng.

Bạn có thể tắt XOM cho các mô-đun riêng lẻ không thể được tái cấu trúc hoặc cần đọc mã thực thi của các mô-đun đó bằng cách đặt biến LOCAL_XOMxom thành false.

// Android.mk
LOCAL_XOM := false

// Android.bp
cc_binary { // or other module types
   ...
   xom: false,
}

Nếu bộ nhớ chỉ thực thi bị tắt trong thư viện tĩnh, thì hệ thống xây dựng sẽ áp dụng điều này cho tất cả các mô-đun phụ thuộc của thư viện tĩnh đó. Bạn có thể ghi đè điều này bằng cách sử dụng xom: true,.

Để tắt bộ nhớ chỉ thực thi trong một thư mục con cụ thể (ví dụ: foo/bar/), hãy truyền giá trị đó đến XOM_EXCLUDE_PATHS.

make -j XOM_EXCLUDE_PATHS=foo/bar

Ngoài ra, bạn có thể đặt biến PRODUCT_XOM_EXCLUDE_PATHS trong cấu hình sản phẩm.

Bạn có thể tắt tệp nhị phân chỉ thực thi trên toàn cầu bằng cách truyền ENABLE_XOM=false vào lệnh make.

make -j ENABLE_XOM=false

Xác nhận kết quả

Không có CTS hoặc kiểm thử xác minh nào cho bộ nhớ chỉ thực thi. Bạn có thể xác minh tệp nhị phân theo cách thủ công bằng readelf và kiểm tra các cờ phân đoạn.