Scudo

Scudo는 동적 사용자 모드 메모리 할당자 또는 할당자로, 힙 관련 취약점(예: 힙 기반 버퍼 오버플로, use after free, double free)으로부터 보호하고 성능을 유지하기 위해 설계되었습니다. 표준 C 할당 및 할당 해제 프리미티브(예: malloc 및 free)와 C++ 프리미티브(예: new 및 delete)도 제공합니다.

Scudo는 AddressSanitizer(ASan)와 같은 전문적인 메모리 오류 감지기보다 완화에 더 중점을 둔 할당자입니다.

Android 11 출시부터는 모든 네이티브 코드에 scudo가 사용됩니다(jemalloc이 여전히 사용되는 메모리 용량이 낮은 기기 제외). 런타임 시 모든 네이티브 힙 할당 및 할당 해제가 Scudo에서 모든 실행 파일 및 파일의 라이브러리 종속 항목에 제공되며 힙에서 손상이나 의심스러운 동작이 감지되면 프로세스가 중단됩니다.

Android 10에서 scudo는 .mk 파일의 LOCAL_SANITIZE := scudo 옵션이나 .bp 파일의 sanitize: { scudo: true, }를 설정하여 바이너리별로 사용 설정해야 했습니다.

Scudo는 오픈소스이며 LLVM의 compiler-rt 프로젝트의 일부입니다. 관련 문서는 https://llvm.org/docs/ScudoHardenedAllocator.html에서 확인할 수 있습니다. Scudo 런타임은 Android 도구 모음의 일부로 제공되며 Soong 및 Make가 지원되어 바이너리에서 Scudo를 쉽게 사용 설정할 수 있습니다.

아래에 설명된 옵션을 사용하여 Scudo 내에서 추가 완화를 사용 설정하거나 사용 중지할 수 있습니다.

맞춤설정

할당자의 일부 매개변수는 다음과 같은 여러 방법을 통해 프로세스별로 정의할 수 있습니다.

  • 정적: 파싱할 옵션 문자열을 반환하는 프로그램의 __scudo_default_options 함수를 정의합니다. 이 함수에는 extern "C" const char *__scudo_default_options() 프로토타입이 있어야 합니다.
  • 동적: 파싱할 옵션 문자열을 포함하는 SCUDO_OPTIONS 환경 변수를 사용합니다. 이 방식으로 정의된 옵션은 __scudo_default_options를 통해 만들어진 모든 정의보다 우선합니다.

사용할 수 있는 옵션은 다음과 같습니다.

옵션 64비트 기본값 32비트 기본값 설명
QuarantineSizeKb 256 64 청크의 실제 할당 해제를 지연하는 데 사용되는 스팸 격리 저장소의 크기(KB)입니다. 값이 낮을수록 메모리 사용량은 줄지만 완화 효과가 감소할 수 있습니다. 음수 값은 기본값으로 대체됩니다. 이 값과 ThreadLocalQuarantineSizeKb를 0으로 설정하면 스팸 격리 저장소가 완전히 사용 중지됩니다.
QuarantineChunksUpToSize 2048 512 청크를 격리할 수 있는 최대 크기(바이트)입니다.
ThreadLocalQuarantineSizeKb 64 16 전역 스팸 격리 저장소를 오프로드하는 데 사용하는 스레드당 캐시 크기(KB)입니다. 값이 낮을수록 메모리 사용량은 줄지만 전역 스팸 격리 저장소에서의 경합이 증가할 수 있습니다. 이 값과 QuarantineSizeKb를 0으로 설정하면 스팸 격리 저장소가 완전히 사용 중지됩니다.
DeallocationTypeMismatch false false malloc/delete, new/free, new/delete[] 오류 신고를 사용 설정합니다.
DeleteSizeMismatch true true new와 delete의 크기가 일치하지 않는 경우 오류 신고를 사용 설정합니다.
ZeroContents false false 할당 및 할당 해제 시 0개의 청크 콘텐츠를 사용 설정합니다.
allocator_may_return_null false false 복구가 가능한 오류가 발생할 때 프로세스를 종료하는 대신 할당자가 null을 반환할 수 있도록 지정합니다.
hard_rss_limit_mb 0 0 프로세스의 RSS가 이 한도에 도달하면 프로세스가 종료됩니다.
soft_rss_limit_mb 0 0 프로세스의 RSS가 이 한도에 도달하면 다시 새로운 할당을 허용할 때까지 allocator_may_return_null의 값에 따라 추가 할당이 실패하거나 null을 반환합니다.
allocator_release_to_os_interval_ms N/A 5000 64비트 할당자에만 영향을 줍니다. 이 옵션을 설정하면 사용되지 않는 메모리를 OS로 릴리스하려고 시도하지만 이 간격(밀리초)보다 더 짧은 간격으로 시도하지는 않습니다. 값이 음수이면 메모리가 OS로 릴리스되지 않습니다.
abort_on_error true true 이 옵션을 설정하면 도구에서 오류 메시지를 인쇄한 후 _exit() 대신 abort()를 호출합니다.

유효성 검사

현재 Scudo 전용 CTS 테스트는 없습니다. 그 대신 CTS 테스트가 특정 바이너리에 Scudo를 사용하거나 사용하지 않고 통과하도록 하여 Scudo가 기기에 영향을 주지 않음을 검증합니다.

문제 해결

복구할 수 없는 문제가 감지되면 할당자는 표준 오류 설명자에 오류 메시지를 표시한 다음 프로세스를 종료합니다. 종료를 유발하는 스택 트레이스가 시스템 로그에 추가됩니다. 출력은 일반적으로 Scudo ERROR:로 시작되며 이어서 포인터와 함께 문제가 간략하게 요약된 내용이 나옵니다.

다음은 현재 오류 메시지와 그 잠재적 원인의 목록입니다.

  • corrupted chunk header: 청크 헤더의 체크섬 검증에 실패했습니다. 헤더가 부분적으로 또는 완전히 덮어쓰였거나 함수에 전달된 포인터가 청크가 아니기 때문일 수 있습니다.
  • race on chunk header: 두 개의 다른 스레드가 같은 헤더를 동시에 조작하려고 합니다. 이는 대개 청크에서 연산을 실행할 때 경합 상태이거나 전반적으로 잠금이 없다는 것을 의미합니다.
  • invalid chunk state: 특정 연산에서 청크가 예상되는 상태가 아닙니다. 예를 들어 청크를 해제하려고 할 때 할당되지 않거나, 청크를 재활용하려고 할 때 격리되지 않습니다. double free가 이 오류의 일반적인 원인입니다.
  • misaligned pointer: 기본 정렬 요구사항이 강력하게 적용됩니다. 32비트 플랫폼에서는 8바이트, 64비트 플랫폼에서는 16바이트입니다. 함수에 전달된 포인터가 요구사항에 맞지 않으면 함수 중 하나에 전달된 포인터가 정렬되지 않습니다.
  • allocation type mismatch: 이 옵션을 사용하면 청크에서 호출되는 할당 해제 함수는 할당을 위해 호출된 함수의 유형과 일치해야 합니다. 이러한 유형의 불일치는 보안 문제를 일으킬 수 있습니다.
  • invalid sized delete: C++14로 크기가 조정된 delete 연산자를 사용하고 선택적 검사를 사용 설정하면 청크를 할당 해제할 때 전달된 크기와 할당할 때 요청된 크기 간의 불일치가 발생합니다. 이러한 불일치는 일반적으로 컴파일러 문제이거나 할당 해제되는 객체 유형의 혼동으로 인해 발생합니다.
  • RSS limit exhausted: 선택적으로 지정한 RSS의 최댓값을 초과했습니다.

OS 자체에서 비정상 종료를 디버깅하는 경우 HWASan OS 빌드를 사용할 수 있습니다. 앱에서 비정상 종료를 디버깅하는 경우 HWASan 앱 빌드도 사용할 수 있습니다.