AArch64 바이너리용 실행 전용 메모리(XOM)

기본적으로 AArch64 시스템 바이너리의 실행 코드 섹션은 just-in-time 코드 재사용 공격에 대비하기 위해 실행 전용(읽기 불가능)으로 표시되며 이는 취약점 완화를 위한 보안 강화 조치입니다. 데이터와 코드를 함께 사용하는 코드와 메모리 세그먼트를 읽기 전용으로 다시 매핑하지 않고 의도적으로 섹션을 검사하는 코드는 더 이상 작동하지 않습니다. Android 10의 타겟 SDK가 설치된 앱(API 수준 29 이상)은 앱이 섹션을 읽을 수 있는 상태로 표시하지 않고 메모리에 시스템 라이브러리가 사용 설정된 실행 전용 메모리(XOM)의 코드 섹션을 읽으려고 하면 영향을 받습니다.

이러한 완화 기능의 이점을 온전히 얻으려면 하드웨어 및 커널 지원이 둘 다 필요합니다. 이러한 지원이 없으면 완화 기능이 부분적으로만 적용될 수 있습니다. Android 4.9 일반 커널에는 ARMv8.2 기기에 이러한 온전한 지원을 제공하기 위한 적절한 패치가 포함됩니다.

구현

컴파일러에 의해 생성된 AArch64 바이너리는 코드와 데이터가 상호 혼합되지 않는다고 가정합니다. 이 기능을 사용 설정해도 기기 성능에 부정적인 영향이 미치지는 않습니다.

자체 실행 세그먼트에 관한 의도적인 메모리 검사를 실행해야 하는 코드의 경우 검사가 필요한 코드의 세그먼트에 mprotect를 호출하여 코드 판독을 허용한 다음 검사가 완료될 때 코드 판독을 차단하는 것이 좋습니다.
이 구현은 실행 전용으로 표시된 메모리 세그먼트를 읽도록 하여 세분화 오류(SEGFAULT)로 이어집니다. 이는 버그, 취약점, 코드와 혼합된 데이터(리터럴 풀링) 또는 의도적 메모리 검사의 결과로 발생할 수 있습니다.

기기 지원 및 영향

기존 하드웨어 또는 기존 커널(4.9 미만)은 보유하고 있지만 필수 패치는 적용되지 않는 기기는 이 기능을 온전히 지원하지 않거나 온전한 이점을 누리지 못할 수 있습니다. 커널을 지원하지 않는 기기는 실행 전용 메모리의 사용자 액세스를 적용하지 않을 수 있지만 페이지 판독 가능 여부를 명시적으로 검사하는 커널 코드는 계속해서 이러한 속성(예: process_vm_readv())을 적용할 수 있습니다.

커널이 실행 전용으로 표시된 userland 페이지를 존중하도록 하려면 커널 플래그 CONFIG_ARM64_UAO를 커널에 설정해야 합니다. 기존 ARMv8 기기 또는 사용자 액세스 재정의(UAO)가 사용 중지된 ARMv8.2 기기의 경우 이에 따른 온전한 이점을 누리지 못할 수 있으며 계속해서 syscall을 사용하여 실행 전용 페이지를 읽을 수도 있습니다.

기존 코드 리팩터링

AArch32에서 포팅된 코드에는 상호 혼합된 데이터와 코드가 포함될 수 있으며 이로 인해 문제가 발생할 수 있습니다. 대부분의 경우 이러한 문제를 해결하는 과정은 상수를 어셈블리 파일의 .data 섹션으로 옮기는 것만큼이나 단순합니다.

필기 작성된 어셈블리는 로컬에서 풀링된 별도의 상수로 리팩터링해야 할 수도 있습니다.

예를 들면 다음과 같습니다.

Clang 컴파일러로 생성된 바이너리에는 데이터가 코드에서 상호 혼합되는 문제가 없어야 합니다. GCC(GNU Compiler Collection)로 생성된 코드가 정적 라이브러리에서 포함된 경우 출력 바이너리를 검사하여 상수가 코드 섹션으로 풀링되지 않았는지 확인합니다.

실행 코드 섹션에 코드 검사가 필요한 경우에는 먼저 mprotect를 호출하여 코드를 판독 가능으로 표시합니다. 이어서 연산이 완료되면 mprotect를 다시 호출하여 판독 불가로 표시합니다.

사용 설정

실행 전용은 빌드 시스템의 모든 64비트 바이너리에 기본으로 사용 설정됩니다.

사용 중지

실행 전용은 모듈 수준에서 전체 하위 디렉터리 트리별로 또는 전체 빌드에 관해 전역적으로 사용 중지할 수 있습니다.

XOM은 LOCAL_XOMxom 변수를 false로 설정하여 리팩터링될 수 없거나 자체 실행 코드를 읽어야 하는 개별 모듈에 관해 사용 중지할 수 있습니다.

// Android.mk
LOCAL_XOM := false

// Android.bp
cc_binary { // or other module types
   ...
   xom: false,
}

실행 전용 메모리가 정적 라이브러리에서 사용 중지된 경우 빌드 시스템은 정적 라이브러리의 모든 종속 모듈에 이를 적용합니다. 이는 xom: true,를 사용하여 재정의할 수 있습니다.

특정 하위 디렉터리(예: foo/bar/)의 실행 전용 메모리를 사용 중지하려면 값을 XOM_EXCLUDE_PATHS에 전달하세요.

make -j XOM_EXCLUDE_PATHS=foo/bar

아니면 제품 구성의 PRODUCT_XOM_EXCLUDE_PATHS 변수를 설정할 수 있습니다.

실행 전용 바이너리는 ENABLE_XOM=falsemake 명령어에 전달하여 전역적으로 사용 중지할 수 있습니다.

make -j ENABLE_XOM=false

유효성 검사

실행 전용 메모리에 사용 가능한 CTS 또는 유효성 검사 테스트는 없습니다. readelf를 사용하고 세그먼트 플래그를 검사하여 바이너리를 수동으로 확인할 수 있습니다.