Kiểm soát tính toàn vẹn của luồng

Tính đến năm 2016, khoảng 86% lỗ hổng trên Android có liên quan đến an toàn bộ nhớ. Hầu hết các lỗ hổng đều bị kẻ tấn công khai thác bằng cách thay đổi luồng điều khiển thông thường của ứng dụng để thực hiện các hoạt động độc hại tùy ý với tất cả các đặc quyền của ứng dụng bị khai thác. Tính toàn vẹn của luồng điều khiển (CFI) là một cơ chế bảo mật không cho phép thay đổi biểu đồ luồng điều khiển ban đầu của tệp nhị phân được biên dịch, khiến việc thực hiện các cuộc tấn công như vậy trở nên khó khăn hơn đáng kể.

Trong Android 8.1, chúng tôi đã kích hoạt triển khai CFI của LLVM trong ngăn xếp phương tiện. Trong Android 9, chúng tôi đã kích hoạt CFI trong nhiều thành phần hơn và cả kernel. CFI hệ thống được bật theo mặc định nhưng bạn cần bật CFI kernel.

CFI của LLVM yêu cầu biên dịch bằng Tối ưu hóa thời gian liên kết (LTO) . LTO bảo toàn biểu diễn mã bit LLVM của các tệp đối tượng cho đến thời gian liên kết, điều này cho phép trình biên dịch đưa ra lý do chính xác hơn về những gì có thể thực hiện tối ưu hóa. Việc kích hoạt LTO sẽ giảm kích thước của tệp nhị phân cuối cùng và cải thiện hiệu suất nhưng làm tăng thời gian biên dịch. Khi thử nghiệm trên Android, sự kết hợp giữa LTO và CFI dẫn đến chi phí không đáng kể đối với kích thước và hiệu suất mã; trong một số trường hợp cả hai đều được cải thiện.

Để biết thêm chi tiết kỹ thuật về CFI và cách xử lý các kiểm tra kiểm soát chuyển tiếp khác, hãy xem tài liệu thiết kế LLVM .

Ví dụ và nguồn

CFI được trình biên dịch cung cấp và thêm công cụ đo lường vào tệp nhị phân trong thời gian biên dịch. Chúng tôi hỗ trợ CFI trong chuỗi công cụ Clang và hệ thống xây dựng Android trong AOSP.

CFI được bật theo mặc định cho các thiết bị Arm64 cho tập hợp các thành phần trong /platform/build/target/product/cfi-common.mk . Nó cũng được kích hoạt trực tiếp trong một tập hợp các tệp makefiles/blueprint của các thành phần đa phương tiện, chẳng hạn như /platform/frameworks/av/media/libmedia/Android.bp/platform/frameworks/av/cmds/stagefright/Android.mk .

Triển khai hệ thống CFI

CFI được bật theo mặc định nếu bạn sử dụng Clang và hệ thống xây dựng Android. Vì CFI giúp giữ an toàn cho người dùng Android nên bạn không nên tắt nó.

Trên thực tế, chúng tôi thực sự khuyến khích bạn kích hoạt CFI cho các thành phần bổ sung. Ứng cử viên lý tưởng là mã gốc đặc quyền hoặc mã gốc xử lý thông tin đầu vào không đáng tin cậy của người dùng. Nếu bạn đang sử dụng clang và hệ thống xây dựng Android, bạn có thể bật CFI trong các thành phần mới bằng cách thêm một vài dòng vào tệp makefile hoặc tệp bản thiết kế của mình.

Hỗ trợ CFI trong tệp tạo tệp

Để bật CFI trong tệp tạo, chẳng hạn như /platform/frameworks/av/cmds/stagefright/Android.mk , hãy thêm:

LOCAL_SANITIZE := cfi
# Optional features
LOCAL_SANITIZE_DIAG := cfi
LOCAL_SANITIZE_BLACKLIST := cfi_blacklist.txt
  • LOCAL_SANITIZE chỉ định CFI làm chất khử trùng trong quá trình xây dựng.
  • LOCAL_SANITIZE_DIAG bật chế độ chẩn đoán cho CFI. Chế độ chẩn đoán in ra thông tin gỡ lỗi bổ sung trong logcat khi gặp sự cố, thông tin này rất hữu ích khi phát triển và thử nghiệm các bản dựng của bạn. Tuy nhiên, hãy đảm bảo xóa chế độ chẩn đoán trên các bản dựng sản phẩm.
  • LOCAL_SANITIZE_BLACKLIST cho phép các thành phần vô hiệu hóa có chọn lọc công cụ CFI cho các chức năng hoặc tệp nguồn riêng lẻ. Bạn có thể sử dụng danh sách đen như biện pháp cuối cùng để khắc phục mọi sự cố mà người dùng gặp phải có thể tồn tại. Để biết thêm chi tiết, xem Tắt CFI .

Hỗ trợ CFI trong các tập tin kế hoạch chi tiết

Để bật CFI trong tệp kế hoạch chi tiết, chẳng hạn như /platform/frameworks/av/media/libmedia/Android.bp , hãy thêm:

   sanitize: {
        cfi: true,
        diag: {
            cfi: true,
        },
        blacklist: "cfi_blacklist.txt",
    },

Xử lý sự cố

Nếu bạn đang bật CFI trong các thành phần mới, bạn có thể gặp một số vấn đề với lỗi không khớp loại chức nănglỗi không khớp loại mã lắp ráp .

Lỗi không khớp loại chức năng xảy ra do CFI hạn chế các cuộc gọi gián tiếp chỉ chuyển sang các chức năng có cùng loại động với loại tĩnh được sử dụng trong cuộc gọi. CFI hạn chế các lệnh gọi hàm thành viên ảo và không ảo chỉ chuyển đến các đối tượng là lớp dẫn xuất của loại tĩnh của đối tượng được sử dụng để thực hiện cuộc gọi. Điều này có nghĩa là khi bạn có mã vi phạm một trong hai giả định này, công cụ đo lường mà CFI thêm vào sẽ bị hủy bỏ. Ví dụ: dấu vết ngăn xếp hiển thị SIGABRT và logcat chứa một dòng về tính toàn vẹn của luồng điều khiển đang tìm kiếm sự không khớp.

Để khắc phục điều này, hãy đảm bảo rằng hàm được gọi có cùng loại đã được khai báo tĩnh. Dưới đây là hai ví dụ CL:

Một vấn đề khác có thể xảy ra là cố gắng kích hoạt CFI trong mã chứa lệnh gọi gián tiếp tới tập hợp. Vì mã hợp ngữ không được gõ nên điều này dẫn đến kiểu không khớp.

Để khắc phục điều này, hãy tạo trình bao bọc mã gốc cho mỗi lệnh gọi hợp ngữ và cung cấp cho trình bao bọc cùng chữ ký hàm giống như con trỏ gọi. Sau đó, trình bao bọc có thể gọi trực tiếp mã lắp ráp. Vì các nhánh trực tiếp không được CFI đo lường (chúng không thể được chỉ định lại trong thời gian chạy và do đó không gây ra rủi ro bảo mật), điều này sẽ khắc phục được sự cố.

Nếu có quá nhiều hàm hợp ngữ và không thể sửa hết chúng, bạn cũng có thể đưa vào danh sách đen tất cả các hàm chứa lệnh gọi gián tiếp tới hợp ngữ. Điều này không được khuyến khích vì nó vô hiệu hóa việc kiểm tra CFI trên các chức năng này, do đó mở ra bề mặt tấn công.

Vô hiệu hóa CFI

Chúng tôi không quan sát thấy bất kỳ chi phí hoạt động nào, vì vậy bạn không cần phải tắt CFI. Tuy nhiên, nếu có tác động đối với người dùng, bạn có thể vô hiệu hóa CFI một cách có chọn lọc cho các chức năng riêng lẻ hoặc tệp nguồn bằng cách cung cấp tệp danh sách cấm khử trùng tại thời điểm biên dịch. Danh sách đen hướng dẫn trình biên dịch vô hiệu hóa thiết bị CFI ở các vị trí được chỉ định.

Hệ thống xây dựng Android cung cấp hỗ trợ cho danh sách cấm theo từng thành phần (cho phép bạn chọn tệp nguồn hoặc các chức năng riêng lẻ sẽ không nhận được thiết bị CFI) cho cả Make và Soong. Để biết thêm chi tiết về định dạng của tệp danh sách đen, hãy xem tài liệu Clang ngược dòng .

Thẩm định

Hiện tại, không có bài kiểm tra CTS cụ thể nào cho CFI. Thay vào đó, hãy đảm bảo rằng các bài kiểm tra CTS vượt qua khi bật hoặc không bật CFI để xác minh rằng CFI không ảnh hưởng đến thiết bị.