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

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

Ở mức cao, MTE gắn thẻ mỗi phân bổ/giải phóng bộ nhớ với siêu dữ liệu bổ sung. Nó gán một thẻ cho một vị trí bộ nhớ, sau đó có thể được liên kết với các con trỏ tham chiếu đến vị trí bộ nhớ đó. Trong thời gian chạy, CPU kiểm tra xem con trỏ và thẻ siêu dữ liệu có khớp với mỗi lần tải và lưu trữ hay không.

Trong Android 12, bộ cấp phát bộ nhớ vùng nhớ khối nhân và vùng người dùng có thể tăng cường mỗi lần phân bổ bằng siêu dữ liệu. Điều này giúp phát hiện lỗi use-after-free và lỗi tràn bộ đệm, đây là những lỗi phổ biến nhất gây ra lỗi an toàn bộ nhớ trong cơ sở mã của chúng tôi.

Chế độ hoạt động MTE

MTE có ba 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 hóa để phát hiện lỗi chính xác theo 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 có thể chấp nhận được chi phí hiệu suất cao hơn. Khi được bật, MTE SYNC hoạt động như một biện pháp giảm thiểu bảo mật. Khi thẻ không khớp, bộ xử lý sẽ hủy bỏ việc thực thi ngay lập tức và kết thúc quá trình bằng SIGSEGV (mã SEGV_MTESERR ) cùng thông tin đầy đủ về quyền truy cập bộ nhớ và địa chỉ lỗi.

Chúng tôi khuyên bạn nên sử dụng chế độ này trong quá trình thử nghiệm để thay thế cho HWASan/KASAN hoặc trong quá trình sản xuất khi quy trình mục tiêu có bề mặt dễ bị tấn công. Ngoài ra, khi chế độ ASYNC chỉ ra sự hiện diện của lỗi, bạn có thể nhận đượ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 việc thực thi sang chế độ SYNC.

Khi chạy ở chế độ ĐỒNG BỘ, bộ cấp phát Android ghi lại dấu vết ngăn xếp cho tất cả các hoạt động phân bổ và giải phóng, đồng thời sử dụng chúng để cung cấp các báo cáo lỗi tốt hơn bao gồm giải thích về lỗi bộ nhớ, chẳng hạn như hết thời gian sử dụng hoặc tràn bộ đệm và ngăn xếp dấu vết của các sự kiện bộ nhớ có liên quan. Những báo cáo như vậy cung cấp nhiều thông tin theo ngữ cảnh hơn và giúp việc theo dõi và sửa lỗi dễ dàng hơn.

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

Chế độ này được tối ưu hóa để đạt hiệu suất cao hơn độ chính xác của báo cáo lỗi và có thể được sử dụng làm tính năng phát hiện chi phí thấp đối với các lỗi an toàn bộ nhớ.
Khi thẻ không khớp, bộ xử lý tiếp tục thực thi cho đến khi mục nhập kernel gần nhất (ví dụ: ngắt hệ thống hoặc bộ đếm thời gian), tại đó nó kết thúc quá trình bằng SIGSEGV (mã SEGV_MTEAERR ) mà không ghi lại địa chỉ lỗi hoặc quyền truy cập bộ nhớ.
Chúng tôi khuyên bạn nên sử dụng chế độ này trong quá trình sản xuất trên các cơ sở mã đã được kiểm tra kỹ lưỡng, nơi mật độ lỗi an toàn bộ nhớ được biết là thấp, điều này đạt được bằng cách sử dụng chế độ SYNC 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 không đối xứng cung cấp khả năng kiểm tra đồng bộ khi đọc bộ nhớ và kiểm tra ghi bộ nhớ không đồng bộ, với hiệu suất tương tự như chế độ ASYNC. Trong hầu hết các trường hợp, chế độ này là một cải tiến so với chế độ ASYNC và chúng tôi khuyên 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ả bên dưới đề cập đến chế độ Bất đối xứng. Thay vào đó, hệ điều hành có thể được 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 mức MTE ưu tiên dành riêng cho CPU" để 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 kích hoạt MTE cho các tiến trình và ứng dụng hệ thống. MTE bị tắt theo mặc định, trừ khi một trong các tùy chọn bên dưới được đặt cho một quy trình cụ thể (xem MTE được bật cho thành phần nào bên dưới ).

Kích hoạt MTE bằng hệ thống xây dựng

Là một thuộc tính trong toàn quy trình, MTE được kiểm soát bởi 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 đây 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. Cài đặt này bị bỏ qua trên các thư viện hoặc bất kỳ mục tiêu nào không thể thực thi cũng như không thể kiểm tra.

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

Chế độ MTE Cài đặ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 Cài đặt
Asynchronous MTE LOCAL_SANITIZE := memtag_heap
Synchronous MTE LOCAL_SANITIZE := memtag_heap
LOCAL_SANITIZE_DIAG := memtag_heap

2. Kích hoạt MTE trên thư mục con trong cây nguồn bằng biến sản phẩm:

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 Cài đặ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 Cài đặ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

Kích hoạt MTE bằng thuộc tính hệ thống

Cài đặt bản dựng ở trên có thể được ghi đè trong thời gian chạy bằng cách đặt 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 sử dụng MTE không đồng bộ, hãy sử dụng adb shell setprop arm64.memtag.process.ping async .

Kích hoạt MTE bằng biến môi trường

Một cách nữa để ghi đè cài đặt bản dựng là xác định biến môi trường: 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ì biến đó sẽ được ưu tiên.

Kích hoạt MTE cho các ứ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 sử dụng MTE có thể làm như vậy bằng cách đặt android:memtagMode trong thẻ <application> hoặc <process> trong AndroidManifest.xml .

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

Khi được đặt trên thẻ <application> , thuộc tính này sẽ ảnh hưởng đến tất cả các quy trình được ứng dụng sử dụng và có thể bị ghi đè đối với các quy trình riêng lẻ bằng cách đặt thẻ <process> .

Để thử nghiệm, các thay đổi về tính tương thích có thể được sử dụng để đặt giá trị mặc định của thuộc tính memtagMode cho ứng dụng 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 những thứ này trong System > Advanced > Developer options > App Compatibility Changes trong menu cài đặt chung. Đặt NATIVE_MEMTAG_ASYNC hoặc NATIVE_MEMTAG_SYNC sẽ bật MTE cho một ứng dụng cụ thể.
Ngoài ra, điều này có thể được thiết lập bằng lệnh 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

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

Chúng tôi thực sự khuyên 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

Giống như bất kỳ biến nào trong hệ thống xây dựng, SANITIZE_TARGET có thể được sử dụng làm biến môi trường hoặc cài đặt make (ví dụ: trong tệp product.mk ).
Xin lưu ý rằng điều này sẽ bật MTE cho tất cả các 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ể được bật theo hướng dẫn ở trên .

Định cấu hình mức MTE ưu tiên dành riêng cho CPU

Trên một số CPU, hiệu suất của MTE ở chế độ ASYMM hoặc thậm chí SYNC có thể tương tự như ASYNC. Điều này đáng giá khi kích hoạt kiểm tra chặt chẽ hơn trên các CPU đó khi yêu cầu chế độ kiểm tra ít nghiêm ngặt hơn, để đạt được lợi ích phát hiện lỗi của kiểm tra chặt chẽ hơn mà không làm giảm hiệu suất.
Theo mặc định, các tiến trình được định cấu hình để chạy ở chế độ ASYNC sẽ chạy ở chế độ ASYNC trên tất cả các CPU. Để định cấu hình hạt nhân chạy các tiến trình này trong chế độ SYNC trên các CPU cụ thể, đồng bộ hóa giá trị phải được ghi vào mục nhập sysfs /sys/devices/system/cpu/cpu<N>/mte_tcf_preferred khi khởi động. Điều này có thể được thực hiện 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, có thể thêm phần 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

Tombstone từ các quy trình ở chế độ ASYNC đang chạy ở chế độ SYNC sẽ chứa dấu vết ngăn xếp chính xác về 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ỉ khả dụng nếu quy trình đượ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.
Vô hiệu hóa việc khởi tạo bộ nhớ trong malloc và tránh thay đổi thẻ bộ nhớ trừ khi cần thiết để đảm bảo tính chính xác.

int mallopt(M_MEMTAG_TUNING, level)

level là:

  • M_MEMTAG_TUNING_BUFFER_OVERFLOW
  • M_MEMTAG_TUNING_UAF

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

  • Cài đặt mặc định là M_MEMTAG_TUNING_BUFFER_OVERFLOW .
  • M_MEMTAG_TUNING_BUFFER_OVERFLOW - cho phép phát hiện xác định lỗi tràn bộ đệm tuyến tính và lỗi tràn bộ đệm bằng cách gán các giá trị thẻ riêng biệt cho các phân bổ liền kề. Chế độ này có cơ hội phát hiện các lỗi không sử dụng được giảm một chút vì chỉ có một nửa giá trị thẻ có thể có cho mỗi 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 liên kết 16 byte) và có thể bỏ sót các lỗi tràn nhỏ ngay cả trong chế độ này. Sự tràn như vậy không thể là nguyên nhân gây ra hỏng bộ nhớ vì bộ nhớ trong một hạt không bao giờ được sử dụng cho nhiều lần phân bổ.
  • M_MEMTAG_TUNING_UAF - cho phép các thẻ ngẫu nhiên độc lập có xác suất thống nhất ~ 93% phát hiện cả lỗi không gian (tràn bộ đệm) và lỗi tạm thời (sử dụng sau khi 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 biết những điều sau:

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

MTE trong hạt nhân

Để bật KASAN được tăng tốc MTE cho kernel, hãy định cấu hình kernel 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 với Android 12-5.10 .
Điều này có thể được kiểm soát khi 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] - có thu thập dấu vết ngăn xếp hay không (mặc định: on )
    • Bộ sưu tập dấu vết ngăn xếp cũng yêu cầu stack_depot_disable=off .
  • kasan.fault=[report|panic] - chỉ in báo cáo hay còn hoảng loạn kernel (mặc định: report ). Bất kể tùy chọn này, việc kiểm tra thẻ sẽ bị vô hiệu hóa sau lỗi được báo cáo đầu tiên.

Chúng tôi thực sự khuyên bạn nên sử dụng chế độ SYNC trong quá trình phát triển, phát triển và thử nghiệm. Tùy chọn này phải được bật 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 với hệ thống xây dựng . Ở chế độ này, các lỗi được phát hiện sớm trong 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.

Chúng tôi thực sự khuyên bạn nên sử dụng chế độ ASYNC trong sản xuất. Điều này cung cấp một công cụ có chi phí thấp để phát hiện sự hiện diện của các lỗi an toàn bộ nhớ trong một quy trình cũng như tăng cường khả năng bảo vệ chuyên sâu. Sau khi phát hiện thấy lỗi, nhà phát triển có thể tận dụng 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.

Chúng tôi thực sự khuyên bạn nên định cấu hình mức MTE ưu tiên dành riêng cho CPU cho SoC. Chế độ bất đối xứng thường có các đặc tính hiệu suất tương tự như ASYNC và hầu như luôn được ưu tiên hơn. Các lõi nhỏ theo thứ tự thường hiển thị hiệu suất tương tự ở cả ba chế độ và có thể được định cấu hình để ưu tiên SYNC.

Các nhà phát triển nên kiểm tra sự hiện diện của sự cố bằng cách kiểm tra /data/tombstones , logcat hoặc bằng cách giám sát đường dẫn DropboxManager của nhà cung cấp để phát hiện lỗi của 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 .

Các 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à hoạt động như một lớp bảo vệ chuyên sâu bổ sung. Những thành phần này là:

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

Các mục tiêu này được lựa 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 thứ gì đó mà miền SELinux unprivileged_app không có)
  • Xử lý đầu vào không đáng tin cậy ( Quy tắc hai )
  • Làm chậm hiệu suất có thể chấp nhận được (sự chậm lại không tạo ra độ trễ hiển thị cho người dùng)

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