Tiện ích gắn thẻ bộ nhớ Arm

Arm v9 giới thiệu Arm Memory Tiện ích gắn thẻ (MTE), một phương thức triển khai phần cứng của bộ nhớ được gắn thẻ.

Ở cấp độ cao, MTE gắn thẻ cho mỗi quá trình phân bổ/giải phóng bộ nhớ bằng siêu dữ liệu bổ sung. Thao tác này chỉ định một thẻ cho một vị trí bộ nhớ, sau đó có thể liên kết với con trỏ tham chiếu đến vị trí bộ nhớ đó. Trong thời gian chạy, CPU sẽ kiểm tra để đảm bảo con trỏ và thẻ siêu dữ liệu khớp với nhau trong mỗi lần tải và lưu trữ.

Trong Android 12, trình phân bổ bộ nhớ vùng nhớ khối xếp cho nhân và không gian người dùng có thể tăng cường từng lượt phân bổ có siêu dữ liệu. Điều này giúp phát hiện việc hết hạn sử dụng và lỗi tràn vùng đệm – nguồn lỗi phổ biến nhất gây ra lỗi về an toàn bộ nhớ trong cơ sở mã của chúng tôi.

Các chế độ hoạt động của MTE

MTE có 3 chế độ hoạt động:

  • Chế độ đồng bộ (SYNC)
  • Chế độ không đồng bộ (ASYNC)
  • Chế độ bất đối xứng (ASYMM)

Chế độ đồng bộ (SYNC)

Chế độ này được tối ưu hoá cho độ chính xác của việc phát hiện lỗi so với hiệu suất và có thể được sử dụng làm công cụ phát hiện lỗi chính xác khi chi phí hiệu suất cao hơn có thể chấp nhận được. Khi được bật, chế độ SYNC của MTE hoạt động như một giải pháp giảm thiểu bảo mật. Khi thẻ không khớp, bộ xử lý huỷ thực thi ngay lập tức và chấm dứt quá trình bằng SIGSEGV (mã SEGV_MTESERR) và thông tin đầy đủ về quyền truy cập bộ nhớ cũng như địa chỉ có lỗi.

Bạn nên sử dụng chế độ này trong quá trình thử nghiệm thay cho HWASan/KASAN hoặc trong phiên bản chính thức khi quy trình mục tiêu biểu thị một lỗ hổng bề mặt tấn công. Ngoài ra, khi chế độ ASYNC cho biết sự hiện diện của lỗi, có thể lấy được báo cáo lỗi chính xác bằng cách sử dụng API thời gian chạy để chuyển đổi thực thi sang chế độ SYNC.

Khi chạy ở chế độ SYNC, trình phân bổ Android sẽ ghi lại dấu vết ngăn xếp cho tất cả và phân bổ, phân bổ và sử dụng chúng để cung cấp báo cáo lỗi tốt hơn có giải thích một kỷ niệm lỗi, chẳng hạn như use-after-free hoặcbuffer-overflow, và dấu vết ngăn xếp của các sự kiện kỷ niệm liên quan. Những báo cáo đó cung cấp nhiều thông tin theo ngữ cảnh hơn và giúp theo dõi và khắc phục lỗi dễ dàng hơn.

Chế độ không đồng bộ (ASYNC)

Chế độ này được tối ưu hoá để nâng cao hiệu suất hơn là độ chính xác của báo cáo lỗi và có thể được dùng làm công cụ phát hiện mức hao tổn thấp đối với các lỗi về an toàn bộ nhớ.
Khi thẻ không khớp, bộ xử lý sẽ tiếp tục thực thi cho đến khi giá trị gần nhất mục nhập nhân hệ điều hành (ví dụ: ngắt lệnh gọi hệ thống hoặc bộ tính giờ), khi chấm dứt quy trình với SIGSEGV (mã SEGV_MTEAERR) mà không có ghi lại địa chỉ lỗi hoặc quyền truy cập bộ nhớ.
Bạn nên sử dụng chế độ này trong phiên bản chính thức trên các cơ sở mã được kiểm thử tốt, trong đó mật độ lỗi về độ an toàn của bộ nhớ được xác định là thấp, đạt được bằng cách sử dụng chế độ SYNC (đồng bộ) trong quá trình thử nghiệm.

Chế độ bất đối xứng (ASYMM)

Một tính năng bổ sung trong Arm v8.7-A, chế độ MTE bất đối xứng cung cấp tính năng kiểm tra số lần đọc bộ nhớ cũng như kiểm tra không đồng bộ việc ghi bộ nhớ, có hiệu suất tương tự như hiệu suất của chế độ ASYNC. Trong hầu hết các trường hợp, là một điểm cải tiến so với chế độ ASYNC và bạn nên sử dụng chế độ này thay vì ASYNC bất cứ khi nào có sẵn.

Vì lý do này, không có API nào được mô tả dưới đây đề cập đến API . Thay vào đó, hệ điều hành có thể được định cấu hình để luôn sử dụng chế độ Bất đối xứng khi Yêu cầu không đồng bộ. Vui lòng tham khảo phần "Định cấu hình dành riêng cho CPU cấp độ MTE ưu tiên" để biết thêm thông tin.

MTE trong không gian người dùng

Các phần sau đây mô tả cách bật MTE cho các quy trình hệ thống và ứng dụng. Theo mặc định, MTE bị tắt, trừ phi một trong các tuỳ chọn bên dưới là thiết lập cho một quy trình cụ thể (xem những thành phần được bật MTE trong bên dưới).

Bật MTE bằng hệ thống xây dựng

Là một thuộc tính trên toàn quy trình, MTE được kiểm soát bởi chế độ cài đặt thời gian xây dựng của tệp thực thi chính. Các tùy chọn sau cho phép thay đổi cài đặt này cho các tệp thực thi riêng lẻ hoặc cho toàn bộ thư mục con trong cây nguồn. Chiến lược phát hành đĩa đơn bị bỏ qua trên thư viện hoặc bất kỳ mục tiêu nào không thực thi được hoặc thử nghiệm.

1. Bật MTE trong Android.bp (ví dụ), cho một dự án cụ thể:

Chế độ MTE Xem xét
MTE không đồng bộ
  sanitize: {
  memtag_heap: true,
  }
MTE đồng bộ
  sanitize: {
  memtag_heap: true,
  diag: {
  memtag_heap: true,
  },
  }

hoặc trong Android.mk:

Chế độ MTE Xem xét
Asynchronous MTE LOCAL_SANITIZE := memtag_heap
Synchronous MTE LOCAL_SANITIZE := memtag_heap
LOCAL_SANITIZE_DIAG := memtag_heap

2. Bật MTE trên một thư mục con trong cây nguồn bằng cách dùng một sản phẩm biến:

Chế độ MTE Bao gồm danh sách Loại trừ danh sách
không đồng bộ PRODUCT_MEMTAG_HEAP_ASYNC_INCLUDE_PATHS MEMTAG_HEAP_ASYNC_INCLUDE_PATHS PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS MEMTAG_HEAP_EXCLUDE_PATHS
đồng bộ hóa PRODUCT_MEMTAG_HEAP_SYNC_INCLUDE_PATHS MEMTAG_HEAP_SYNC_INCLUDE_PATHS

hoặc

Chế độ MTE Xem xét
MTE không đồng bộ MEMTAG_HEAP_ASYNC_INCLUDE_PATHS
MTE đồng bộ MEMTAG_HEAP_SYNC_INCLUDE_PATHS

hoặc bằng cách chỉ định đường dẫn loại trừ của tệp thực thi:

Chế độ MTE Xem xét
MTE không đồng bộ PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS MEMTAG_HEAP_EXCLUDE_PATHS
MTE đồng bộ

Ví dụ: (cách sử dụng tương tự như PRODUCT_CFI_INCLUDE_PATHS)

  PRODUCT_MEMTAG_HEAP_SYNC_INCLUDE_PATHS=vendor/$(vendor)
  PRODUCT_MEMTAG_HEAP_EXCLUDE_PATHS=vendor/$(vendor)/projectA \
                                    vendor/$(vendor)/projectB

Bật MTE bằng các thuộc tính hệ thống

Bạn có thể ghi đè các chế độ cài đặt bản dựng ở trên trong thời gian chạy bằng cách đặt giá trị thuộc tính hệ thống sau:

arm64.memtag.process.<basename> = (off|sync|async)

Trong đó basename là tên cơ sở của tệp thực thi.

Ví dụ: để đặt /system/bin/ping hoặc /data/local/tmp/ping để dùng MTE không đồng bộ, hãy dùng adb shell setprop arm64.memtag.process.ping async.

Bật MTE bằng một biến môi trường

Một cách khác để ghi đè chế độ cài đặt bản dựng là xác định môi trường biến: MEMTAG_OPTIONS=(off|sync|async) Nếu cả biến môi trường và thuộc tính hệ thống đều được xác định, thì thuộc tính sẽ được ưu tiên hơn.

Bật MTE cho ứng dụng

Nếu không được chỉ định, MTE sẽ bị tắt theo mặc định nhưng các ứng dụng muốn dùng MTE có thể làm như vậy bằng cách đặt android:memtagMode trong <application> hoặc Thẻ <process> trong AndroidManifest.xml.

android:memtagMode=(off|default|sync|async)

Khi được đặt trên thẻ <application>, ảnh hưởng đến tất cả quy trình mà ứng dụng sử dụng và có thể bị ghi đè cho từng quy trình riêng lẻ bằng cách đặt Thẻ <process>.

Đối với thử nghiệm, khả năng tương thích có thể được sử dụng để đặt giá trị mặc định của memtagMode cho một ứng dụng có không chỉ định bất kỳ giá trị nào trong tệp kê khai (hoặc chỉ định default).
Bạn có thể tìm thấy các cài đặt này trong System > Advanced > Developer options > App Compatibility Changes trong trình đơn cài đặt chung. Chế độ cài đặt NATIVE_MEMTAG_ASYNC hoặc NATIVE_MEMTAG_SYNC bật MTE cho một ứng dụng cụ thể.
Ngoài ra, bạn có thể thiết lập giá trị này bằng am như sau:

$ adb shell am compat enable NATIVE_MEMTAG_[A]SYNC my.app.name

Xây dựng hình ảnh hệ thống MTE

Bạn nên bật MTE trên tất cả tệp nhị phân gốc trong quá trình phát triển và nhắc nhở. Điều này giúp sớm phát hiện các lỗi về độ an toàn của bộ nhớ và cung cấp thông tin thực tế phạm vi người dùng, nếu được bật trong các bản dựng kiểm thử.

Bạn nên bật MTE ở chế độ Đồng bộ trên tất cả các tệp nhị phân gốc trong quá trình phát triển

SANITIZE_TARGET=memtag_heap SANITIZE_TARGET_DIAG=memtag_heap m

Tương tự như với bất kỳ biến nào trong hệ thống xây dựng, SANITIZE_TARGET có thể là được dùng làm biến môi trường hoặc chế độ cài đặt make (ví dụ: trong một tệp product.mk).
Xin lưu ý rằng thao tác này sẽ bật MTE cho mọi quy trình gốc, nhưng không bật cho các ứng dụng (được phân nhánh từ zygote64) mà MTE có thể bật theo hướng dẫn ở trên.

Định cấu hình cấp MTE ưu tiên theo từng CPU

Trên một số CPU, hiệu suất của MTE trong các chế độ ASYMM hoặc thậm chí là SYNC có thể tương tự như của ASYNC. Điều này khiến việc bật kiểm tra nghiêm ngặt hơn trên các CPU đó khi chế độ kiểm tra ít nghiêm ngặt hơn được yêu cầu, trong có được lợi ích phát hiện lỗi của các lần kiểm tra nghiêm ngặt hơn mà không cần nhược điểm về hiệu suất.
Theo mặc định, các quy trình được định cấu hình để chạy ở chế độ ASYNC sẽ chạy ở chế độ ASYNC trên tất cả CPU. Cách định cấu hình nhân hệ điều hành để chạy các quy trình này ở chế độ SYNC trên CPU cụ thể, đồng bộ hoá giá trị phải được ghi vào sysfs mục /sys/devices/system/cpu/cpu<N>/mte_tcf_preferred lúc khởi động bất cứ lúc nào. Bạn có thể thực hiện việc này bằng tập lệnh init. Ví dụ: để định cấu hình CPU 0-1 để chạy các quy trình chế độ ASYNC ở chế độ SYNC và CPU 2-3 để sử dụng chạy ở chế độ ASYMM, bạn có thể thêm đoạn mã sau vào mệnh đề init của tập lệnh init của nhà cung cấp:

  write /sys/devices/system/cpu/cpu0/mte_tcf_preferred sync
  write /sys/devices/system/cpu/cpu1/mte_tcf_preferred sync
  write /sys/devices/system/cpu/cpu2/mte_tcf_preferred asymm
  write /sys/devices/system/cpu/cpu3/mte_tcf_preferred asymm

Tombstones từ các quy trình ở chế độ ASYNC chạy ở chế độ SYNC sẽ chứa dấu vết ngăn xếp chính xác của vị trí lỗi bộ nhớ. Tuy nhiên, chúng sẽ không bao gồm dấu vết ngăn xếp phân bổ hoặc giải phóng. Các dấu vết ngăn xếp này chỉ sẽ có nếu quy trình này được định cấu hình để chạy ở chế độ SYNC.

int mallopt(M_THREAD_DISABLE_MEM_INIT, level)

trong đó level là 0 hoặc 1.
Tắt tính năng khởi chạy bộ nhớ trong Malloc và tránh thay đổi thẻ bộ nhớ trừ phi cần thiết để đảm bảo tính chính xác.

int mallopt(M_MEMTAG_TUNING, level)

trong đó level là:

  • M_MEMTAG_TUNING_BUFFER_OVERFLOW
  • M_MEMTAG_TUNING_UAF

Chọn chiến lược phân bổ thẻ.

  • Chế độ cài đặt mặc định là M_MEMTAG_TUNING_BUFFER_OVERFLOW.
  • M_MEMTAG_TUNING_BUFFER_OVERFLOW - cho phép mang tính xác định có thể phát hiện lỗi tràn vùng đệm tuyến tính và lỗi tràn bằng cách chỉ định thẻ riêng biệt cho các lượt phân bổ liền kề. Chế độ này làm giảm một chút cơ hội phát hiện các lỗi use-after-free vì chỉ một nửa số giá trị thẻ có thể có là có sẵn cho từng vị trí bộ nhớ. Xin lưu ý rằng MTE không thể phát hiện tràn trong cùng một hạt thẻ (đoạn được căn chỉnh 16 byte) và có thể bỏ lỡ các phần nhỏ tràn ngay cả ở chế độ này. Việc tràn như vậy không phải là nguyên nhân của bộ nhớ vì bộ nhớ trong một hạt không bao giờ được dùng cho nhiều phân bổ.
  • M_MEMTAG_TUNING_UAF – cho phép các thẻ ngẫu nhiên độc lập để xác suất phát hiện đồng nhất ~93% cả về không gian (tràn vùng đệm) và lỗi tạm thời (sử dụng sau lỗi miễn phí).

Ngoài các API được mô tả ở trên, người dùng có kinh nghiệm có thể muốn được nhận biết những điều sau:

  • Việc đặt thanh ghi phần cứng PSTATE.TCO có thể tạm thời chặn kiểm tra thẻ (ví dụ). Ví dụ: khi sao chép một dải bộ nhớ có nội dung thẻ không xác định hoặc giải quyết điểm tắc nghẽn hiệu suất trong một vòng lặp nóng.
  • Khi sử dụng M_HEAP_TAGGING_LEVEL_SYNC, trình xử lý sự cố hệ thống cung cấp thêm thông tin như dấu vết ngăn xếp phân bổ và giải phóng. Chức năng này cần quyền truy cập vào các bit của thẻ và được bật bằng cách truyền SA_EXPOSE_TAGBITS gắn cờ khi thiết lập trình xử lý tín hiệu. Bất kỳ chương trình nào tự đặt tín hiệu trình xử lý và uỷ quyền các sự cố không xác định cho hệ thống, bạn nên thực hiện .

MTE trong nhân

Để bật KASAN được tăng tốc MTE cho nhân, hãy định cấu hình nhân bằng CONFIG_KASAN=y, CONFIG_KASAN_HW_TAGS=y. Các cấu hình này được bật theo mặc định trên nhân GKI, bắt đầu bằng Android 12-5.10.
Bạn có thể kiểm soát việc này tại thời điểm khởi động bằng cách sử dụng các đối số dòng lệnh sau:

  • kasan=[on|off] – bật hoặc tắt KASAN (mặc định: on)
  • kasan.mode=[sync|async] – chọn giữa chế độ đồng bộ và không đồng bộ (mặc định: sync)
  • kasan.stacktrace=[on|off] – liệu có thu thập hay không dấu vết ngăn xếp (mặc định: on)
    • thu thập dấu vết ngăn xếp cũng đòi hỏi stack_depot_disable=off.
  • kasan.fault=[report|panic] – liệu có chỉ in báo cáo hay không, hoặc cũng có thể gây hoảng loạn hạt nhân (mặc định: report). Bất kể điều này , tính năng kiểm tra thẻ sẽ bị tắt sau lỗi được báo cáo đầu tiên.

Bạn nên sử dụng chế độ SYNC trong quá trình khởi tạo, phát triển và kiểm thử. Bạn phải bật tuỳ chọn này trên toàn cầu cho tất cả các quy trình sử dụng biến môi trường hoặc bằng hệ thống xây dựng. Ở chế độ này, lỗi được phát hiện ở đầu quá trình phát triển, cơ sở mã được ổn định nhanh hơn và tránh được chi phí phát hiện lỗi sau này trong quá trình sản xuất.

Bạn nên sử dụng chế độ ASYNC trong bản phát hành công khai. Điều này dẫn đến để phát hiện sự hiện diện của lỗi an toàn bộ nhớ trong một quy trình cũng như phòng thủ theo chiều sâu. Khi phát hiện lỗi, nhà phát triển có thể tận dụng các API thời gian chạy để chuyển sang chế độ SYNC và nhận dấu vết ngăn xếp chính xác từ một nhóm người dùng được lấy mẫu.

Bạn nên định cấu hình cấp MTE ưu tiên theo từng CPU cho SoC. Chế độ Asymm thường có các đặc điểm hiệu suất tương tự như ASYNC, và hầu như luôn được ưa chuộng hơn. Các lõi nhỏ theo thứ tự thường cho thấy tương tự nhau hiệu suất ở cả ba chế độ và có thể được định cấu hình để ưu tiên SYNC.

Nhà phát triển nên kiểm tra xem có sự cố hay không bằng cách kiểm tra /data/tombstones, logcat hoặc bằng cách theo dõi nhà cung cấp DropboxManager quy trình báo lỗi cho người dùng cuối. Để biết thêm thông tin về cách gỡ lỗi mã gốc Android, hãy xem thông tin tại đây.

Thành phần nền tảng hỗ trợ MTE

Trong Android 12, một số thành phần hệ thống quan trọng về bảo mật sử dụng MTE ASYNC để phát hiện sự cố của người dùng cuối và đóng vai trò như một lớp bổ sung phòng thủ theo chiều sâu. Những thành phần này bao gồm:

  • Các trình nền và tiện ích mạng (ngoại trừ netd)
  • Bluetooth, SecureElement, HAL NFC và các ứng dụng hệ thống
  • statsd trình nền
  • system_server
  • zygote64 (để cho phép ứng dụng chọn sử dụng MTE)

Các mục tiêu này được chọn dựa trên các tiêu chí sau:

  • Một quy trình đặc quyền (được định nghĩa là một quy trình có quyền truy cập vào một nội dung nào đó miền SELinux không có quyền này)
  • Quy trình thông tin đầu vào không đáng tin cậy (Quy tắc trên 2)
  • Hiệu suất giảm tốc có thể chấp nhận (việc chậm lại không khiến người dùng thấy rõ độ trễ)

Chúng tôi khuyến khích các nhà cung cấp hỗ trợ MTE trong quá trình sản xuất để có thêm nhiều thành phần, theo các tiêu chí nêu trên. Trong quá trình phát triển, bạn nên kiểm thử các thành phần này bằng chế độ SYNC, để phát hiện lỗi dễ dàng khắc phục và đánh giá Chế độ ASYNC sẽ ảnh hưởng đến hiệu suất của chúng.
Trong tương lai, Android dự định mở rộng danh sách các thành phần hệ thống mà MTE bật, được hướng dẫn theo các đặc điểm hiệu suất của các thiết kế phần cứng sắp tới.