UndefinedBehaviorSanitizer

UndefinedBehaviorSanitizer (UBSan) thực hiện công cụ thời gian biên dịch để kiểm tra các loại hành vi không xác định. Trong khi UBsan có khả năng phát hiện nhiều lỗi hành vi không xác định , Android hỗ trợ:

  • căn chỉnh
  • bool
  • giới hạn
  • liệt kê
  • float-cast-tràn
  • float-divide-by-zero
  • số nguyên chia cho số không
  • thuộc tính nonnull
  • vô giá trị
  • trở lại
  • trả về-nonnull-thuộc tính
  • cơ sở thay đổi
  • shift-số mũ
  • ký-số nguyên-tràn
  • không thể truy cập
  • unsigned-integer-tràn
  • ràng buộc vla

tràn số nguyên không dấu, mặc dù không phải là hành vi không xác định về mặt kỹ thuật, được bao gồm trong trình khử trùng và được sử dụng trong nhiều mô-đun Android, bao gồm cả các thành phần máy chủ phương tiện, để loại bỏ mọi lỗ hổng tràn số nguyên tiềm ẩn.

Thực hiện

Trong hệ thống xây dựng Android, bạn có thể bật UBsan trên toàn cầu hoặc cục bộ. Để bật UBSan trên toàn cầu, hãy đặt SANITIZE_TARGET trong Android.mk. Để bật UBSan ở cấp độ mỗi mô-đun, hãy đặt LOCAL_SANITIZE và chỉ định các hành vi không xác định mà bạn muốn tìm kiếm trong Android.mk. Ví dụ:

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_CFLAGS := -std=c11 -Wall -Werror -O0

LOCAL_SRC_FILES:= sanitizer-status.c

LOCAL_MODULE:= sanitizer-status

LOCAL_SANITIZE := alignment bounds null unreachable integer
LOCAL_SANITIZE_DIAG := alignment bounds null unreachable integer

include $(BUILD_EXECUTABLE)

Và cấu hình bản thiết kế tương đương (Android.bp):

cc_binary {

    cflags: [
        "-std=c11",
        "-Wall",
        "-Werror",
        "-O0",
    ],

    srcs: ["sanitizer-status.c"],

    name: "sanitizer-status",

    sanitize: {
        misc_undefined: [
            "alignment",
            "bounds",
            "null",
            "unreachable",
            "integer",
        ],
        diag: {
            misc_undefined: [
                "alignment",
                "bounds",
                "null",
                "unreachable",
                "integer",
            ],
        },
    },

}

phím tắt UBsan

Android cũng có hai phím tắt, integerdefault-ub , để kích hoạt một bộ trình khử trùng cùng một lúc. số nguyên cho phép integer-divide-by-zero , signed-integer-overflowunsigned-integer-overflow . default-ub cho phép kiểm tra có vấn đề về hiệu suất trình biên dịch tối thiểu: bool, integer-divide-by-zero, return, returns-nonnull-attribute, shift-exponent, unreachable and vla-bound Lớp khử số nguyên có thể được sử dụng với SANITIZE_TARGET và LOCAL_SANITIZE, trong khi default-ub chỉ có thể được sử dụng với SANITIZE_TARGET.

Báo cáo lỗi tốt hơn

Việc triển khai UBsan mặc định của Android gọi một chức năng được chỉ định khi gặp phải hành vi không xác định. Theo mặc định, chức năng này bị hủy bỏ. Tuy nhiên, bắt đầu từ tháng 10 năm 2016, UBsan trên Android có một thư viện thời gian chạy tùy chọn cung cấp báo cáo lỗi chi tiết hơn, bao gồm loại hành vi không xác định gặp phải, thông tin về tệp và dòng mã nguồn. Để bật tính năng báo cáo lỗi này bằng cách kiểm tra số nguyên, hãy thêm phần sau vào tệp Android.mk:

LOCAL_SANITIZE:=integer
LOCAL_SANITIZE_DIAG:=integer

Giá trị LOCAL_SANITIZE kích hoạt trình khử trùng trong quá trình xây dựng. LOCAL_SANITIZE_DIAG bật chế độ chẩn đoán cho chất khử trùng được chỉ định. Có thể đặt LOCAL_SANITIZE và LOCAL_SANITIZE_DIAG thành các giá trị khác nhau nhưng chỉ những kiểm tra đó trong LOCAL_SANITIZE mới được bật. Nếu một kiểm tra không được chỉ định trong LOCAL_SANITIZE, nhưng được chỉ định trong LOCAL_SANITIZE_DIAG, kiểm tra không được bật và thông báo chẩn đoán không được cung cấp.

Dưới đây là một ví dụ về thông tin được cung cấp bởi thư viện thời gian chạy UBsan:

pixel-xl:/ # sanitizer-status ubsan
sanitizer-status/sanitizer-status.c:53:6: runtime error: unsigned integer overflow: 18446744073709551615 + 1 cannot be represented in type 'size_t' (aka 'unsigned long')

Vệ sinh tràn số nguyên

Tràn số nguyên ngoài ý muốn có thể gây ra lỗi bộ nhớ hoặc lỗ hổng tiết lộ thông tin trong các biến liên quan đến truy cập bộ nhớ hoặc cấp phát bộ nhớ. Để giải quyết vấn đề này, chúng tôi đã thêm bộ khử trùng tràn số nguyên không dấu và không dấu UndefinedBehaviorSanitizer (UBSan) của Clang để củng cố khung phương tiện trong Android 7.0. Trong Android 9, chúng tôi đã mở rộng UBsan để bao gồm nhiều thành phần hơn và hỗ trợ hệ thống xây dựng được cải thiện cho nó.

Điều này được thiết kế để thêm kiểm tra xung quanh các hoạt động/hướng dẫn số học—có thể bị tràn—để hủy bỏ một quy trình một cách an toàn nếu xảy ra tràn. Các trình khử trùng này có thể giảm thiểu toàn bộ lớp lỗ hổng hỏng hóc bộ nhớ và tiết lộ thông tin trong đó nguyên nhân gốc rễ là do tràn số nguyên, chẳng hạn như lỗ hổng Stagefright ban đầu.

Ví dụ và nguồn

Integer Overflow Sanitization (IntSan) được cung cấp bởi trình biên dịch và thêm công cụ đo đạc vào tệp nhị phân trong thời gian biên dịch để phát hiện tràn số học. Nó được bật theo mặc định trong các thành phần khác nhau trong nền tảng, chẳng hạn như /platform/external/libnl/Android.bp .

Thực hiện

IntSan sử dụng bộ khử tràn số nguyên có dấu và không dấu của UBsan. Giảm thiểu này được kích hoạt ở cấp độ mỗi mô-đun. Nó giúp giữ an toàn cho các thành phần quan trọng của Android và không nên bị vô hiệu hóa.

Chúng tôi đặc biệt khuyến khích bạn bật Khử trùng tràn số nguyên cho các thành phần bổ sung. Các ứng cử viên lý tưởng là mã gốc có đặc quyền hoặc mã gốc phân tích cú pháp đầu vào của người dùng không đáng tin cậy. Có một chi phí hoạt động nhỏ liên quan đến trình khử trùng phụ thuộc vào việc sử dụng mã và mức độ phổ biến của các phép toán số học. Mong đợi một tỷ lệ phần trăm chi phí nhỏ và kiểm tra xem hiệu suất có phải là vấn đề đáng lo ngại hay không.

Hỗ trợ IntSan trong makefiles

Để bật IntSan trong tệp thực hiện, hãy thêm:

LOCAL_SANITIZE := integer_overflow
    # Optional features
    LOCAL_SANITIZE_DIAG := integer_overflow
    LOCAL_SANITIZE_BLOCKLIST := modulename_BLOCKLIST.txt
  • LOCAL_SANITIZE lấy một danh sách các trình khử trùng được phân tách bằng dấu phẩy, với integer_overflow là một bộ tùy chọn được đóng gói sẵn cho các trình khử trùng tràn số nguyên đã ký và không dấu riêng lẻ với BLOCKLIST mặc định .
  • LOCAL_SANITIZE_DIAG bật chế độ chẩn đoán cho chất khử trùng. Chỉ sử dụng chế độ chẩn đoán trong quá trình thử nghiệm vì chế độ này sẽ không hủy bỏ khi tràn, phủ nhận hoàn toàn lợi thế bảo mật của giảm thiểu. Xem Khắc phục sự cố để biết thêm chi tiết.
  • LOCAL_SANITIZE_BLOCKLIST cho phép bạn chỉ định tệp BLOCKLIST để ngăn không cho chức năng và tệp nguồn bị khử trùng. Xem Khắc phục sự cố để biết thêm chi tiết.

Nếu bạn muốn kiểm soát chi tiết hơn, hãy bật riêng từng trình khử trùng bằng một hoặc cả hai cờ:

LOCAL_SANITIZE := signed-integer-overflow, unsigned-integer-overflow
    LOCAL_SANITIZE_DIAG := signed-integer-overflow, unsigned-integer-overflow

Hỗ trợ IntSan trong các tệp bản thiết kế

Để bật khử trùng tràn số nguyên trong tệp bản thiết kế, chẳng hạn như /platform/external/libnl/Android.bp , hãy thêm:

   sanitize: {
          integer_overflow: true,
          diag: {
              integer_overflow: true,
          },
          BLOCKLIST: "modulename_BLOCKLIST.txt",
       },

Cũng giống như tạo tệp, thuộc tính integer_overflow là một tập hợp các tùy chọn được đóng gói sẵn cho các trình khử trùng tràn số nguyên có dấu và không dấu riêng lẻ với BLOCKLIST mặc định .

Bộ diag tính chẩn đoán kích hoạt chế độ chẩn đoán cho chất khử trùng. Chỉ sử dụng chế độ chẩn đoán trong quá trình thử nghiệm. Chế độ chẩn đoán không hủy bỏ khi tràn, điều này phủ nhận hoàn toàn lợi thế bảo mật của việc giảm thiểu trong các bản dựng của người dùng. Xem Khắc phục sự cố để biết thêm chi tiết.

Thuộc tính BLOCKLIST cho phép đặc tả tệp BLOCKLIST cho phép nhà phát triển ngăn không cho chức năng và tệp nguồn bị khử trùng. Xem Khắc phục sự cố để biết thêm chi tiết.

Để kích hoạt các chất khử trùng riêng lẻ, hãy sử dụng:

   sanitize: {
          misc_undefined: ["signed-integer-overflow", "unsigned-integer-overflow"],
          diag: {
              misc_undefined: ["signed-integer-overflow",
                               "unsigned-integer-overflow",],
          },
          BLOCKLIST: "modulename_BLOCKLIST.txt",
       },

Xử lý sự cố

Nếu bạn đang kích hoạt tính năng khử trùng số nguyên tràn trong các thành phần mới hoặc dựa vào các thư viện nền tảng đã có tính năng khử trùng lỗi tràn số nguyên, thì bạn có thể gặp phải một số vấn đề về lỗi tràn số nguyên lành tính gây ra việc hủy bỏ. Bạn nên kiểm tra các thành phần đã bật tính năng khử trùng để đảm bảo có thể xuất hiện tình trạng tràn lành tính.

Để tìm, hủy bỏ do quá trình vệ sinh trong bản dựng của người dùng gây ra, hãy tìm kiếm các sự cố SIGABRT với thông báo Hủy bỏ cho biết lỗi tràn do UBsan bắt được, chẳng hạn như:

pid: ###, tid: ###, name: Binder:###  >>> /system/bin/surfaceflinger <<<
    signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
    Abort message: 'ubsan: sub-overflow'

Theo dõi ngăn xếp phải bao gồm chức năng gây ra hủy bỏ, tuy nhiên, lỗi tràn xảy ra trong các hàm nội tuyến có thể không hiển thị trong theo dõi ngăn xếp.

Để xác định nguyên nhân gốc dễ dàng hơn, hãy bật chẩn đoán trong thư viện kích hoạt hủy bỏ và cố gắng tạo lại lỗi. Khi bật chẩn đoán, quy trình sẽ không bị hủy bỏ và thay vào đó sẽ tiếp tục chạy. Việc không hủy bỏ giúp tối đa hóa số lần tràn lành tính trong một đường dẫn thực thi cụ thể mà không cần phải biên dịch lại sau khi sửa từng lỗi. Chẩn đoán tạo ra một thông báo lỗi bao gồm số dòng và tệp nguồn gây ra việc hủy bỏ:

frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp:2188:32: runtime error: unsigned integer overflow: 0 - 1 cannot be represented in type 'size_t' (aka 'unsigned long')

Sau khi xác định được hoạt động số học có vấn đề, hãy đảm bảo rằng lỗi tràn là lành tính và có chủ đích (ví dụ: không có ý nghĩa bảo mật). Bạn có thể giải quyết việc phá thai bằng cách:

  • Tái cấu trúc mã để tránh tràn ( ví dụ )
  • Tràn một cách rõ ràng thông qua các chức năng __builtin_*_overflow của Clang ( ví dụ )
  • Vô hiệu hóa vệ sinh trong chức năng bằng cách chỉ định thuộc tính no_sanitize ( ví dụ )
  • Vô hiệu hóa chức năng hoặc tệp nguồn thông qua tệp BLOCKLIST ( ví dụ )

Bạn nên sử dụng giải pháp chi tiết nhất có thể. Ví dụ: một hàm lớn có nhiều phép tính số học và một phép toán tràn duy nhất nên cấu trúc lại một phép toán đơn lẻ thay vì BLOCKLIST toàn bộ hàm.

Các mẫu phổ biến có thể dẫn đến tràn lành tính bao gồm:

  • Truyền ngầm trong đó xảy ra tràn không dấu trước khi được truyền sang loại đã ký ( ví dụ )
  • Xóa danh sách được liên kết làm giảm chỉ số vòng lặp khi xóa ( ví dụ )
  • Chỉ định loại không dấu cho -1 thay vì chỉ định giá trị tối đa thực tế ( ví dụ )
  • Các vòng lặp giảm một số nguyên không dấu trong điều kiện ( ví dụ , ví dụ )

Các nhà phát triển nên đảm bảo rằng các trường hợp mà trình khử trùng phát hiện thấy tràn là thực sự lành tính, không có tác dụng phụ ngoài ý muốn hoặc ý nghĩa bảo mật trước khi tắt tính năng khử trùng.

Vô hiệu hóa IntSan

Bạn có thể tắt IntSan bằng BLOCKLIST hoặc thuộc tính chức năng. Vô hiệu hóa một cách tiết kiệm và chỉ khi tái cấu trúc mã là không hợp lý hoặc nếu có vấn đề về chi phí hoạt động.

Xem tài liệu Clang ngược dòng để biết thêm thông tin về cách tắt IntSan bằng các thuộc tính chức năngđịnh dạng tệp BLOCKLIST . BLOCKLISTing nên được đặt trong phạm vi chất khử trùng cụ thể bằng cách sử dụng tên phần chỉ định chất khử trùng mục tiêu để tránh ảnh hưởng đến chất khử trùng khác.

Thẩm định

Hiện tại, không có thử nghiệm CTS cụ thể cho Vệ sinh tràn số nguyên. Thay vào đó, hãy đảm bảo rằng các kiểm tra CTS vượt qua dù có hoặc không bật IntSan để xác minh rằng nó không ảnh hưởng đến thiết bị.

Vệ sinh giới hạn

BoundsSanitizer (BoundSan) thêm thiết bị vào các tệp nhị phân để chèn kiểm tra giới hạn xung quanh các truy cập mảng. Các kiểm tra này được thêm vào nếu trình biên dịch không thể chứng minh tại thời điểm biên dịch rằng quyền truy cập sẽ an toàn và nếu kích thước của mảng sẽ được biết trong thời gian chạy, để có thể kiểm tra lại. Android 10 triển khai BoundSan trong Bluetooth và codec. BoundSan được cung cấp bởi trình biên dịch và được bật theo mặc định trong các thành phần khác nhau trên toàn bộ nền tảng.

Thực hiện

BoundSan sử dụng chất khử trùng giới hạn của UBsan . Giảm thiểu này được kích hoạt ở cấp độ mỗi mô-đun. Nó giúp giữ an toàn cho các thành phần quan trọng của Android và không nên bị vô hiệu hóa.

Chúng tôi đặc biệt khuyến khích bạn bật BoundSan cho các thành phần bổ sung. Các ứng cử viên lý tưởng là mã gốc đặc quyền hoặc mã gốc phức tạp phân tích cú pháp đầu vào của người dùng không đáng tin cậy. Chi phí hoạt động liên quan đến việc bật BoundSan phụ thuộc vào số lần truy cập mảng không thể chứng minh là an toàn. Mong đợi một tỷ lệ phần trăm chi phí nhỏ trung bình và kiểm tra xem hiệu suất có phải là vấn đề đáng lo ngại hay không.

Kích hoạt BoundSan trong các tệp bản thiết kế

BoundSan có thể được kích hoạt trong các tệp bản thiết kế bằng cách thêm "bounds" vào thuộc tính khử trùng misc_undefined cho các mô-đun thư viện và nhị phân:

    sanitize: {
       misc_undefined: ["bounds"],
       diag: {
          misc_undefined: ["bounds"],
       },
       BLOCKLIST: "modulename_BLOCKLIST.txt",
chẩn đoán

Thuộc tính diag kích hoạt chế độ chẩn đoán cho chất khử trùng. Chỉ sử dụng chế độ chẩn đoán trong quá trình thử nghiệm. Chế độ chẩn đoán không hủy bỏ khi tràn, điều này làm mất đi lợi thế bảo mật của quá trình giảm thiểu và mang lại chi phí hoạt động cao hơn, vì vậy, chế độ này không được khuyến nghị cho các bản dựng sản xuất.

DANH SÁCH CHẶN

Thuộc tính BLOCKLIST cho phép đặc điểm kỹ thuật của tệp BLOCKLIST mà nhà phát triển có thể sử dụng để ngăn không cho các chức năng và tệp nguồn bị làm sạch. Chỉ sử dụng thuộc tính này nếu hiệu suất là mối quan tâm và các tệp/chức năng được nhắm mục tiêu đóng góp đáng kể. Kiểm tra thủ công các tệp/hàm này để đảm bảo rằng các truy cập mảng được an toàn. Xem Khắc phục sự cố để biết thêm chi tiết.

Kích hoạt BoundSan trong makefiles

BoundSan có thể được kích hoạt trong tệp tạo tệp bằng cách thêm "bounds" vào biến LOCAL_SANITIZE cho mô-đun nhị phân và thư viện:

    LOCAL_SANITIZE := bounds
    # Optional features
    LOCAL_SANITIZE_DIAG := bounds
    LOCAL_SANITIZE_BLOCKLIST := modulename_BLOCKLIST.txt

LOCAL_SANITIZE chấp nhận danh sách chất khử trùng được phân tách bằng dấu phẩy.

LOCAL_SANITIZE_DIAG bật chế độ chẩn đoán. Chỉ sử dụng chế độ chẩn đoán trong quá trình thử nghiệm. Chế độ chẩn đoán không hủy bỏ khi tràn, điều này làm mất đi lợi thế bảo mật của quá trình giảm thiểu và mang lại chi phí hoạt động cao hơn, vì vậy, chế độ này không được khuyến nghị cho các bản dựng sản xuất.

LOCAL_SANITIZE_BLOCKLIST cho phép đặc điểm kỹ thuật của tệp BLOCKLIST cho phép nhà phát triển ngăn các chức năng và tệp nguồn khỏi bị khử trùng. Chỉ sử dụng thuộc tính này nếu hiệu suất là mối quan tâm và các tệp/chức năng được nhắm mục tiêu đóng góp đáng kể. Kiểm tra thủ công các tệp/hàm này để đảm bảo rằng các truy cập mảng được an toàn. Xem Khắc phục sự cố để biết thêm chi tiết.

Vô hiệu hóa BoundSan

Bạn có thể tắt BoundSan trong các hàm và tệp nguồn bằng BLOCKLIST hoặc thuộc tính hàm. Tốt nhất là luôn bật BoundSan, vì vậy chỉ tắt nó nếu chức năng hoặc tệp đang tạo ra một lượng lớn chi phí hoạt động và nguồn đã được xem xét thủ công.

Để biết thêm thông tin về cách tắt BoundSan bằng các thuộc tính chức năngđịnh dạng tệp BLOCKLIST , hãy tham khảo tài liệu Clang LLVM. Phạm vi DANH SÁCH CHẶN cho chất khử trùng cụ thể bằng cách sử dụng tên phần chỉ định chất khử trùng mục tiêu để tránh ảnh hưởng đến chất khử trùng khác.

Thẩm định

Không có thử nghiệm CTS dành riêng cho BoundSan. Thay vào đó, hãy đảm bảo rằng các thử nghiệm CTS vượt qua dù có hoặc không bật BoundSan để xác minh rằng nó không ảnh hưởng đến thiết bị.

Xử lý sự cố

Kiểm tra kỹ lưỡng các thành phần sau khi bật BoundSan để đảm bảo rằng mọi truy cập ngoài giới hạn chưa bị phát hiện trước đó đều được xử lý.

Có thể dễ dàng xác định lỗi BoundSan vì chúng bao gồm thông báo hủy bỏ Tombstone sau:

    pid: ###, tid: ###, name: Binder:###  >>> /system/bin/foobar <<<
    signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
    Abort message: 'ubsan: out-of-bounds'

Khi chạy ở chế độ chẩn đoán, tệp nguồn, số dòng và giá trị chỉ mục được in thành logcat . Theo mặc định, chế độ này không đưa ra thông báo hủy bỏ. Xem lại logcat để kiểm tra bất kỳ lỗi nào.

    external/foo/bar.c:293:13: runtime error: index -1 out of bounds for type 'int [24]'