UndefinedBehaviorSanitizer

UndefinedBehaviorSanitizer(UBSan)는 컴파일 시간을 계측하여 다양한 유형의 정의되지 않은 동작을 검사합니다. UBSan은 여러 정의되지 않은 동작 버그를 감지할 수 있고, Android는 다음을 지원합니다.

  • alignment
  • bool
  • bounds
  • enum
  • float-cast-overflow
  • float-divide-by-zero
  • integer-divide-by-zero
  • nonnull-attribute
  • null
  • return
  • returns-nonnull-attribute
  • shift-base
  • shift-exponent
  • signed-integer-overflow
  • unreachable
  • unsigned-integer-overflow
  • vla-bound

unsigned-integer-overflow의 경우 엄밀하게 말하면 정의되지 않은 동작은 아니지만, 잠재적인 integer-overflow 취약점을 제거하기 위해 새니타이저에 포함되어 미디어 서버 구성요소를 비롯한 많은 Android 모듈에서 사용됩니다.

구현

Android 빌드 시스템에서는 UBSan을 전역 또는 로컬에서 사용 설정할 수 있습니다. UBSan을 전역에서 사용 설정하려면 Android.mk에서 SANITIZE_TARGET을 설정합니다. UBSan을 모듈별 수준에서 사용 설정하려면 LOCAL_SANITIZE를 설정하고 Android.mk에서 찾고자 하는 정의되지 않은 동작을 지정합니다. 예를 들면 다음과 같습니다.

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)

동일한 청사진(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",
            ],
        },
    },

}

UBSan 단축키

Android에는 일련의 새니타이저를 동시에 사용 설정할 수 있는 두 가지 손쉬운 방법으로 integerdefault-ub를 제공합니다. integer는 integer-divide-by-zero, signed-integer-overflowunsigned-integer-overflow를 지원합니다. default-ub는 검사 시 bool, integer-divide-by-zero, return, returns-nonnull-attribute, shift-exponent, unreachable and vla-bound의 컴파일러 성능 문제를 최소화합니다. integer 새니타이저 클래스는 SANITIZE_TARGET 및 LOCAL_SANITIZE에서 사용할 수 있지만 default-ub는 SANITIZE_TARGET에서만 사용할 수 있습니다.

오류 신고 개선

Android의 기본 UBSan 구현은 정의되지 않은 동작이 발생할 때 지정된 함수를 호출합니다. 기본적으로 이 함수는 중단됩니다. 하지만 2016년 10월부터 Android UBSan은 발생한 정의되지 않은 동작의 유형, 파일 및 소스 코드 행 정보를 비롯한 더 세부적인 오류 신고 기능을 제공하는 런타임 라이브러리 옵션을 제공합니다. 정수 검사가 포함된 오류 신고 기능을 사용하려면 다음을 Android.mk 파일에 추가합니다.

LOCAL_SANITIZE:=integer
LOCAL_SANITIZE_DIAG:=integer

LOCAL_SANITIZE 값은 빌드하는 동안 새니타이저를 사용 설정합니다. LOCAL_SANITIZE_DIAG는 지정된 새니타이저의 진단 모드를 사용 설정합니다. LOCAL_SANITIZE 및 LOCAL_SANITIZE_DIAG를 서로 다른 값으로 설정할 수 있지만, LOCAL_SANITIZE의 검사만 사용 설정됩니다. 검사가 LOCAL_SANITIZE에 지정되지 않았지만 LOCAL_SANITIZE_DIAG에 지정되어 있으면 검사는 사용 설정되지 않으며 진단 메시지도 제공되지 않습니다.

다음은 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')

정수 오버플로 제거

의도하지 않은 정수 오버플로는 메모리 액세스 또는 메모리 할당과 관련된 변수에서 메모리 손상 또는 정보 공개 취약성 문제를 일으킬 수 있습니다. 이 문제를 해결하기 위해 Clang의 UndefinedBehaviorSanitizer(UBSan) 부호 있는 정수 및 부호 없는 정수 오버플로 새니타이저를 추가해 Android 7.0에서 미디어 프레임워크를 강화했습니다. Android 9에서는 더 많은 구성요소를 포함하도록 UBSan을 확장하고 UBSan에 대한 빌드 시스템 지원을 개선했습니다.

오버플로가 발생한 경우 프로세스를 안전하게 취소하기 위해 오버플로가 발생할 수 있는 산술 연산/명령에 대한 검사를 추가하도록 설계되었습니다. 이러한 새니타이저는 메모리 손상 및 정보 공개 취약점의 전체 클래스를 완화할 수 있는데, 이러한 취약점의 근본 원인은 원래 Stagefright와 같은 정수 오버플로입니다.

예시 및 소스

정수 오버플로 제거(IntSan)는 컴파일러에서 제공하며 연산 오버플로를 감지하기 위해 컴파일하는 동안 바이너리에 계측을 추가합니다. 또한 플랫폼 전체의 다양한 구성요소에서 기본적으로 사용 설정되어 있습니다(예: /platform/external/libnl/Android.bp).

구현

IntSan은 UBSan의 부호 있는 정수 및 부호 없는 정수 오버플로 새니타이저를 사용합니다. 이러한 완화는 모듈 수준별로 사용 설정됩니다. Android의 중요한 구성요소를 안전하게 유지하는 데 도움이 되므로 사용 중지하면 안 됩니다.

추가 구성요소에 대해 정수 오버플로 제거를 사용 설정하는 것이 좋습니다. 이상적인 방식은 권한 있는 네이티브 코드나 신뢰하지 않는 사용자 입력을 파싱하는 네이티브 코드입니다. 새니타이저와 관련된 사소한 성능 오버헤드가 있는데, 코드의 사용량과 산술 연산의 배포에 따라 달라집니다. 약간의 오버헤드 비율을 예상하고 성능이 문제가 되는지 테스트합니다.

Makefile에서 IntSan 지원

Makefile에서 IntSan을 사용하려면 다음을 추가합니다.

LOCAL_SANITIZE := integer_overflow
    # Optional features
    LOCAL_SANITIZE_DIAG := integer_overflow
    LOCAL_SANITIZE_BLOCKLIST := modulename_BLOCKLIST.txt
  • LOCAL_SANITIZE는 쉼표로 구분된 새니타이저 목록을 가져옵니다. 개별의 부호 있는 정수 및 부호 없는 정수 오버플로 새니타이저에 대해 기본 BLOCKLIST를 사용하여 미리 패키징된 옵션 집합이 되는 integer_overflow를 사용합니다.
  • LOCAL_SANITIZE_DIAG는 새니타이저에 대해 진단 모드를 켭니다. 진단 모드는 오버플로에 대해 취소되지 않으며 완화의 보안 이점을 완전히 무효화하므로 테스트 중에만 사용합니다. 자세한 내용은 문제 해결을 참고하세요.
  • LOCAL_SANITIZE_BLOCKLIST를 사용하면 BLOCKLIST 파일을 지정하여 함수와 소스 파일이 제거되지 않도록 방지할 수 있습니다. 자세한 내용은 문제 해결을 참고하세요.

더 세부적으로 제어하려면 다음 플래그 중 하나 또는 둘 다를 사용하여 새니타이저를 개별적으로 사용 설정합니다.

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

청사진 파일에서 IntSan 지원

청사진 파일(예: /platform/external/libnl/Android.bp)에서 정수 오버플로 제거를 사용 설정하려면 다음을 추가합니다.

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

Makefile과 마찬가지로 integer_overflow 속성은 개별의 부호 있는 정수 및 부호 없는 정수 오버플로 새니타이저에 대해 기본 BLOCKLIST를 사용해 미리 패키징된 옵션 집합입니다.

속성의 diag 집합은 새니타이저에 대해 진단 모드를 사용 설정합니다. 테스트 중에만 진단 모드를 사용합니다. 진단 모드는 오버플로에 대해 취소되지 않으며, 사용자 빌드에서 완화의 보안 이점을 완전히 무효화합니다. 자세한 내용은 문제 해결을 참고하세요.

BLOCKLIST 속성은 개발자가 함수 및 소스 파일이 제거되지 않게 방지하도록 허용하는 BLOCKLIST 파일의 사양을 허용합니다. 자세한 내용은 문제 해결을 참고하세요.

새니타이저를 개별적으로 사용 설정하려면 다음을 사용합니다.

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

문제해결

새 구성요소에서 정수 오버플로 제거를 사용 설정하거나 정수 오버플로 제거가 있는 플랫폼 라이브러리를 사용하는 경우, 취소를 일으키는 무해한 정수 오버플로와 관련된 몇 가지 문제가 발생할 수 있습니다. 무해한 오버플로가 드러날 수 있도록 제거가 사용 설정된 상태에서 구성요소를 테스트해야 합니다.

사용자 빌드에서 제거로 인해 발생한 취소를 찾으려면 UBSan에서 감지한 오버플로를 나타내는 다음과 같은 취소 메시지를 사용해 SIGABRT 충돌을 검색합니다.

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

스택 추적에는 취소를 일으킨 함수가 포함되어야 하지만 인라인 함수에서 발생하는 오버플로는 스택 추적에서 명확하게 표시되지 않을 수 있습니다.

근본 원인을 더 쉽게 확인하려면 라이브러리에서 진단을 사용 설정하여 취소를 트리거하고 오류를 재현해 봅니다. 진단을 사용 설정하면 프로세스가 중단되지 않고 대신 계속 실행됩니다. 취소하지 않으면 각 버그를 수정한 후 다시 컴파일할 필요 없이 특정 실행 경로에서 무해한 오버플로의 수를 최대화하는데 도움이 됩니다. 진단은 취소를 일으킨 행 번호와 소스 파일이 포함된 다음과 같은 오류 메시지를 생성합니다.

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')

문제가 있는 산술 연산이 발견되면 오버플로가 무해하고 의도된 것인지 확인합니다(예: 보안에 영향을 미치지 않음). 새니타이저 취소는 다음과 같이 처리할 수 있습니다.

  • 코드를 리팩터링하여 오버플로 방지()
  • Clang의 __builtin_*_overflow 함수를 통해 명시적으로 오버플로()
  • no_sanitize 속성을 지정하여 함수에서 제거 사용 중지()
  • BLOCKLIST 파일을 통해 함수 또는 소스 파일의 제거 사용 중지()

가능한 한 가장 세분화된 솔루션을 사용해야 합니다. 예를 들어 다수의 산술 연산과 오버플로 연산 하나가 있는 큰 함수에는 BLOCKLIST된 전체 함수가 아니라 리팩터링된 단일 연산이 있어야 합니다.

무해한 오버플로가 발생할 수 있는 일반적인 패턴은 다음과 같습니다.

  • 부호 있는 형식으로 변환되기 전에 부호 없는 오버플로가 발생하는 암시적 변환()
  • 삭제 시 루프 색인을 줄이는 연결된 목록 삭제()
  • 실제 최댓값을 지정하는 대신 부호 없는 유형을 -1로 할당()
  • 조건에서 부호 없는 정수를 줄이는 루프(, )

개발자는 제거를 사용 중지하기 전에 새니타이저가 의도하지 않은 부작용이나 보안에 미치는 영향 없이 실제로 무해한 오버플로를 감지하는지 확인하는 것이 좋습니다.

IntSan 사용 중지

BLOCKLIST 또는 함수 속성을 사용해 IntSan을 사용 중지할 수 있습니다. 코드 리팩터링이 적절하지 않거나 성능 오버헤드가 문제가 되는 경우에만 드물게 사용 중지합니다.

함수 속성BLOCKLIST 파일 형식으로 IntSan을 사용 중지하는 방법에 대한 자세한 내용은 Clang 업스트림 문서를 참고하세요. 다른 새니타이저에 영향을 미치지 않으려면 섹션 이름을 사용해 타겟 새니타이저를 지정하여 특정 새니타이저로 BLOCKLIST의 범위를 지정해야 합니다.

유효성 검사

현재 정수 오버플로 제거에 대한 CTS 테스트는 없습니다. 대신 IntSan을 사용 설정하거나 사용 중지한 상태에서 CTS 테스트를 통과하는지 확인해 기기에 영향을 미치는지 확인할 수 있습니다.

범위 제거

BoundsSanitizer(BoundSan)는 바이너리에 계측을 추가하여 배열 액세스 주변에 범위 검사를 삽입합니다. 이러한 검사는 비교 검사가 가능하도록 배열 크기가 런타임 시에 알려지며 컴파일러가 컴파일 시간에 액세스가 안전할 것임을 증명할 수 없는 경우에 추가됩니다. Android 10은 블루투스 및 코덱에서 BoundSan을 배포합니다. BoundSan은 컴파일러에 의해 제공되며 플랫폼 곳곳의 다양한 구성요소에서 기본으로 사용 설정됩니다.

구현

BoundSan은 UBSan의 bounds sanitizer를 사용합니다. 이러한 완화는 모듈별 수준에서 사용 설정됩니다. 이는 Android의 중요 구성요소를 안전하게 유지할 수 있게 도와주며 사용 중지하면 안 됩니다.

추가 구성요소에는 BoundSan을 사용 설정하는 것이 좋습니다. 이상적인 후보는 권한이 있는 네이티브 코드나 신뢰할 수 없는 사용자 입력을 파싱하는 복합 네이티브 코드입니다. BoundSan 사용 설정과 관련된 성능 오버헤드는 안전한 것으로 증명할 수 없는 배열 액세스 수에 종속됩니다. 평균에 대한 약간의 오버헤드 비율을 예상해야 하며, 성능에 대한 우려가 있는 경우 테스트하세요.

청사진 파일에 BoundSan 사용 설정

BoundSan을 청사진 파일에 사용 설정하려면 바이너리 및 라이브러리 모듈의 misc_undefined 처리 속성에 "bounds"를 추가해야 합니다.

    sanitize: {
       misc_undefined: ["bounds"],
       diag: {
          misc_undefined: ["bounds"],
       },
       BLOCKLIST: "modulename_BLOCKLIST.txt",
diag

diag 속성은 새니타이저에 진단 모드를 사용 설정합니다. 진단 모드는 테스트 도중에만 사용하세요. 진단 모드는 완화의 보안 이점을 무색하게 하고 더 높은 성능 오버헤드를 수반하는 오버플로우에 대해 중단되지 않으므로 프로덕션 빌드에 사용하지 않는 것이 좋습니다.

BLOCKLIST

BLOCKLIST 속성은 개발자가 사용 가능한 BLOCKLIST 파일을 지정하여 함수 및 소스 파일이 처리되지 않도록 합니다. 성능에 대한 우려가 있고 타겟팅된 파일/함수의 기여가 상당한 경우에만 이 속성을 사용하세요. 배열 액세스가 안전한지 확인하려면 이러한 파일/함수를 수동으로 감사하세요. 자세한 내용은 문제해결을 참조하세요.

makefile에 BoundSan 사용 설정

BoundSan을 makefile에 사용 설정하려면 "bounds"를 바이너리 및 라이브러리 모듈의 LOCAL_SANITIZE 변수에 추가해야 합니다.

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

LOCAL_SANITIZE는 쉼표로 구분된 새니타이저 목록을 수락합니다.

LOCAL_SANITIZE_DIAG는 진단 모드를 켭니다. 진단 모드는 테스트 도중에만 사용하세요. 진단 모드는 완화의 보안 이점을 무색하게 하고 더 높은 성능 오버헤드를 수반하는 오버플로우에 대해 중단되지 않으므로 프로덕션 빌드에 사용하지 않는 것이 좋습니다.

LOCAL_SANITIZE_BLOCKLIST는 개발자가 사용 가능한 BLOCKLIST 파일을 지정하여 함수 및 소스 파일이 처리되지 않도록 합니다. 성능에 대한 우려가 있고 타겟팅된 파일/함수의 기여가 상당한 경우에만 이 속성을 사용하세요. 배열 액세스가 안전한지 확인하려면 이러한 파일/함수를 수동으로 감사하세요. 자세한 내용은 문제해결을 참조하세요.

BoundSan 사용 중지

BLOCKLIST 또는 함수 속성으로 함수 및 소스 파일에서 BoundSan을 사용 중지할 수 있습니다. BoundSan은 사용 설정 상태를 유지하는 것이 좋으므로 함수나 파일이 대량의 성능 오버헤드를 생성하고 소스가 수동으로 검토된 경우에만 사용 중지하세요.

함수 속성BLOCKLIST 파일 형식으로 BoundSan을 사용 중지하는 자세한 방법은 Clang LLVM 문서를 참고하세요. 다른 새니타이저가 영향을 받지 않도록 하려면 타겟 새니타이저를 지정하는 섹션 이름을 사용하여 BLOCKLIST 범위를 특정 새니타이저로 한정하세요.

유효성 검사

BoundSan 전용 CTS 테스트는 없습니다. 대신 CTS 테스트가 BoundSan 사용 여부와 상관없이 통과되도록 하여 기기에 대한 영향이 없는지 확인하세요.

문제해결

BoundSan을 사용 설정한 후에는 구성요소를 꼼꼼히 테스트하여 이전에 감지되지 않은 모든 범위 밖의 액세스가 해결되었는지 확인하세요.

BoundSan 오류는 다음과 같은 Tombstone 중단 메시지를 포함하므로 쉽게 식별 가능합니다.

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

진단 모드에서 실행할 때는 소스 파일, 행 번호 및 색인 값이 logcat에 출력됩니다. 기본적으로 이 모드는 중단 메시지를 반환하지 않습니다. logcat을 검토하여 오류가 없는지 확인하세요.

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