KASAN+KCOV로 Pixel 커널 빌드

Kernel Address Sanitizer(KASAN)는 커널 개발자 및 테스터가 런타임 메모리와 관련된 버그(예: 경계에서 벗어난 읽기 또는 쓰기 작업 및 use-after-free 문제)를 찾을 수 있게 도와줍니다. KASAN은 런타임 성능 저하 및 메모리 사용량 증가로 인해 프로덕션 빌드에 사용 설정되지는 않지만 디버그 빌드를 테스트하는 경우에는 여전히 중요한 도구입니다.

다른 런타임 도구인 Kernel Coverage(KCOV)와 함께 사용되는 경우, KASAN으로 완전 삭제되고 KCOV로 계측화된 코드는 개발자와 테스터가 런타임 메모리 오류를 감지하고 코드 적용 범위 정보를 가져올 수 있게 도와줍니다. syzkaller 등을 통한 커널 퍼징 테스트의 시나리오에서 KASAN은 비정상 종료의 근본적인 원인을 파악할 수 있게 도와주며, KCOV는 테스트 사례 또는 코퍼스 중복 삭제에 도움이 되도록 퍼징 엔진에 코드 적용 범위 정보를 제공합니다.

이 페이지는 KASAN의 내부 원리 또는 역학을 다루기보다는 Android 오픈소스 프로젝트(AOSP)와 Pixel 커널 소스를 빌드 및 수정하여 KASAN 및 KCOV가 켜진 상태에서 부팅할 수 있도록 하기 위한 가이드 역할을 합니다.

빌드 환경 설정

빌드 환경을 설정하려면 다운로드 및 빌드 섹션의 단계를 따르세요.

AOSP 빌드

Android 소스 코드를 다운로드하세요. KASAN 이미지를 빌드하려면 개발 도중이 아닌 안정적인 빌드를 선택해야 합니다. 대부분의 경우 최신 버전의 안정적인 분기를 선택하는 것이 좋습니다. 빌드 및 분기에 관한 자세한 내용은 소스 코드 태그 및 빌드를 참고하세요.

소스 코드를 정상적으로 확인한 후에는 Nexus 및 Pixel 기기용 드라이버 바이너리에서 사용 중인 기기 및 분기에 상응하는 필요한 기기 blob을 다운로드합니다. 공급업체 이미지와 단일 칩 시스템(SOC) 제조업체의 바이너리 집합을 모두 다운로드합니다. 그런 다음 다운로드한 tarball을 보관 취소하고 포함된 스크립트를 실행한 후 라이선스를 수락합니다.

그런 다음 빌드 준비의 단계에 따라 빌드 환경을 정리 및 설정하고 빌드 타겟을 선택합니다.

작업 기반을 수립하려면 첫 번째 빌드를 수정하지 않고 구축합니다.

make -j48

빌드 결과를 테스트 기기(예: marlin)에 플래시하고 부팅을 허용합니다.

cd out/target/product/marlin
ANDROID_PRODUCT_OUT=`pwd` fastboot flashall -w

홈 화면에 부팅한 후에는 다음과 같은 팝업이 표시될 수 있습니다.

There's an internal problem with your device. Contact your manufacturer for details. 이 팝업은 공급업체 및 시스템 파티션의 빌드 지문이 일치하지 않을 수도 있음을 의미합니다. 이 빌드는 출시 빌드가 아닌 개발 및 테스트 전용이므로 그냥 무시하면 됩니다.

커널 빌드

커널을 빌드하려면 올바른 소스 코드를 확인하고 이를 크로스 컴파일한 다음 올바른 AOSP 디렉터리에서 커널 이미지를 빌드해야 합니다.

커널 소스 코드 확인

커널 소스 코드를 저장할 디렉터리를 생성하고 로컬 저장소에 AOSP 커널 git 저장소를 클론합니다.

mkdir ~/src/marlin-kernel-src
cd ~/src/marlin-kernel-src
git clone https://android.googlesource.com/kernel/msm

작업이 완료되면 msm이라는 빈 디렉터리가 생성됩니다.

빌드 중인 소스 코드에 상응하는 분기를 msm 디렉터리 및 git checkout에 입력합니다. 사용 가능한 분기 및 태그 목록은 Android msm 커널 소스 트리를 참고하세요.

cd msm
git checkout TAG_NAME

이 단계를 완료한 후에는 msm 디렉터리에 콘텐츠가 생성됩니다.

크로스 컴파일 수행

다음으로는 Android 커널을 컴파일해야 합니다.

크로스 컴파일러 설정

커널을 빌드하려면 크로스 컴파일러를 설정해야 합니다. 현재 권장 및 테스트되는 도구 모음은 Android NDK 도구 모음의 안정적인 최신 버전입니다. Android NDK를 다운로드하려면 공식 Android NDK 웹사이트로 이동합니다. 플랫폼에 맞는 zip 보관 파일을 다운로드한 후 압축 해제합니다. 이렇게 하면 android-ndk-NDK_VERSION과 유사한 디렉터리가 생성됩니다.

LZ4c 도구 다운로드

Pixel 커널은 LZ4 압축을 사용합니다. 따라서 커널을 빌드할 때는 lz4c 도구가 필요합니다. Ubuntu를 사용하는 경우에는 다음을 실행하여 lz4c 도구를 설치하세요.

sudo apt-get install liblz4-tool

커널 빌드

marlin-kernel-src/msm 디렉터리에서 다음을 실행하여 빌드 환경을 설정합니다.

export ARCH=arm64
export CROSS_COMPILE=PATH_TO_NDK/android-ndk-NDK_VERSION/toolchains/aarch64-linux-android-TOOLCHAIN_VERSION/prebuilt/linux-x86_64/bin/aarch64-linux-android-

그런 다음 수정되지 않은 버전의 커널을 빌드하여 작업 기반을 수립합니다.

make marlin_defconfig
make -j48

빌드 프로세스 결과는 arch/arm64/boot/Image.lz4-dtb에서 찾을 수 있습니다.

AOSP에서 부팅 이미지 다시 빌드

커널 이미지를 빌드한 후에는 다음을 실행하여 결과를 AOSP의 device/google/marlin-kernel 디렉터리에 복사합니다.

cp ${marlin-kernel-src}/msm/arch/arm64/boot/Image.lz4-dtb device/google/marlin-kernel
source build/envsetup.sh
lunch aosp_marlin-userdebug
make -j48

빌드가 완료되면 다음을 실행하여 대상 기기를 플래시합니다.

cd out/target/product/marlin
fastboot flashall -w

플래싱 후에는 기기가 부팅되어야 합니다. 기기 부팅이 완료된 후에는 Settings -> System -> About phone 아래의 Kernel version을 확인하여 기기에 플래시한 이미지가 개발자가 빌드한 커널 이미지인지 확인합니다.

커널 수정

KASAN 및 KCOV 컴파일 옵션 사용 설정

KASAN 및 KCOV 코드는 일반 빌드에 사용 설정되지 않는 컴파일 플래그로 보호됩니다. 사용 설정하려면 KASAN 및 KCOV 옵션을 구성 파일에 추가하되 LZ4 config는 드롭합니다.

이를 실행하려면 기본 구성 파일(예: marlin_defconfig)의 사본을 생성해야 합니다.

cd arch/arm64/configs
cp marlin_defconfig marlin-kasan_defconfig

새 구성 파일에서 이 플래그 CONFIG_KERNEL_LZ4=y를 삭제하고 다음 플래그를 추가합니다.

CONFIG_KASAN=y
CONFIG_KASAN_INLINE=y
CONFIG_KCOV=y
CONFIG_SLUB=y
CONFIG_SLUB_DEBUG=y

새 구성으로 커널 다시 컴파일

구성 파일 사본의 수정을 마친 후에는 커널을 다시 컴파일합니다.

커널 재구성

빌드 환경을 설정합니다. 수정된 defconfig를 빌드하고 생성된 .config 파일에 새로 추가된 플래그가 있는지 확인합니다.

make marlin-kasan_defconfig
grep KASAN .config
CONFIG_HAVE_ARCH_KASAN=y
CONFIG_KASAN=y
# CONFIG_KASAN_OUTLINE is not set
CONFIG_KASAN_INLINE=y

KASAN 플래그가 표시됩니다. 커널을 컴파일합니다.

make -j48

수정된 커널 이미지 확인

컴파일이 완료된 후에는 arch/arm64/boot 디렉터리로 이동하여 컴파일 결과를 확인합니다. 일반적으로 Image.gz-dtb는 약 23MB이며 표준 빌드보다 큽니다.

cd arch/arm64/boot
ls -lh Image.gz-dtb
-rw-r--r-- 1 username groupname 23M Aug 11 13:59 Image.gz-dtb

KCOV가 제대로 컴파일되었는지 확인하려면 커널 소스 트리 루트에서 생성된 vmlinux에 관한 추가 분석을 실행합니다. vmlinux에서 objdump를 실행하면 다수의 __sanitizer_cov_trace_pc() 호출이 표시됩니다.

sh -c '${CROSS_COMPILE}objdump -d vmlinux' | grep sanitizer
ffffffc000082030:    94040658    bl    ffffffc000183990 <__sanitizer_cov_trace_pc>
ffffffc000082050:    94040650    bl    ffffffc000183990 <__sanitizer_cov_trace_pc>
ffffffc000082078:    94040646    bl    ffffffc000183990 <__sanitizer_cov_trace_pc>
ffffffc000082080:    94040644    bl    ffffffc000183990 <__sanitizer_cov_trace_pc>
ffffffc0000820ac:    94040639    bl    ffffffc000183990 <__sanitizer_cov_trace_pc>

AOSP 코드 수정

새 부팅 이미지를 플러그인하기 전에 기기 부팅 방식에 적용되는 AOSP 소스 코드의 특정 매개변수를 조정해야 합니다. 이는 주로 확장된 새 이미지가 제대로 부팅되는지를 확인하는 데 필요합니다.

보드 매개변수 조정

기기의 BoardConfig.mk 파일에서 정의된 부팅 매개변수를 조정합니다. 이는 AOSP 소스 코드의 루트를 기준으로 하는 device/google/marlin/marlin에 위치합니다.

cd device/google/marlin/marlin
vim BoardConfig.mk

BoardConfig.mk 파일을 수정하고 싶지 않은 경우에는 대신 이름 marlin_kasan을 포함하는 새로운 lunch 타겟을 생성할 수 있습니다. 이 프로세스에 관한 자세한 내용은 새 기기 추가를 참고하세요.

로컬 Makefile에서 커널 타겟 조정

새 커널은 LZ4 압축을 사용하여 속도를 개선하지만 KASAN에는 향상된 압축비를 위한 gzip이 필요합니다. 이를 위해 LOCAL_KERNEL 변수가 device/google/marlin/device-common.mk에서 가리키는 지점을 수정하여 최종 타겟으로 어떤 커널을 번들화할지 빌드 기계에 알립니다.

부팅 이미지 다시 빌드

부팅 이미지를 다시 빌드하려면 새 커널 이미지를 기기별 폴더의 AOSP 트리에 복사합니다(예: device/google/marlin-kernel). 앞서 수정한 방식에 따라 빌드 시스템에서 커널 타겟 이미지가 위치할 것으로 예상하는 곳이 여기가 맞는지 확인하세요.

그런 다음 앞서 AOSP를 빌드한 것처럼 플래시 가능한 이미지를 다시 빌드합니다. 빌드가 완료되면 빌드된 모든 이미지를 평상시처럼 플래시합니다.

수정된 커널 이미지로 기기 부팅

이제 부팅 후 홈 화면으로 전환되는 빌드가 있어야 합니다. 여기서부터는 기기의 dmesg 출력에서 'KernelAddressSanitizer initialized' 메시지를 확인합니다. 아주 초기 부팅 단계에서 확인해야 합니다. 이 메시지는 KASAN이 부팅 시간 도중에 초기화됨을 의미합니다. 또한 /sys/kernel/debug/kcov가 기기에 있는지도 확인할 수 있습니다(루트 상태여야 함).

문제 해결

KASAN+KCOV 컴파일 옵션을 켜기 전에 표준 빌드를 시작으로 여러 커널 버전을 작업 기반으로 실험할 수 있습니다. 중단이 발생하면 먼저 기기의 부트로더 및 베이스밴드 버전이 새 빌드에 필요한 것과 일치하는지 확인해야 합니다. 너무 앞선 커널 버전을 사용하면 이에 맞춰 Android 트리의 최신 분기를 사용해야 할 수도 있습니다.