커널의 제어 흐름 무결성

제어 흐름 무결성 (CFI)은 컴파일된 바이너리의 원래 제어 흐름 그래프에 관한 변경을 차단하는 보안 메커니즘으로, 이러한 공격을 실행하기 훨씬 어렵게 만듭니다.

Android 9부터 커널에서 CFI를 사용 설정할 수 있습니다.

Linux 커널에는 두 가지 다른 CFI 구현이 있습니다.

  • Linux 6.0 이하의 경우 Clang LTO를 사용하는 Clang CFI
  • Linux 6.1 이상의 경우 Clang KCFI

Clang CFI에는 링크 시간 최적화 (LTO)를 통한 컴파일이 필요합니다. LTO는 링크 시간까지 객체 파일의 LLVM 비트코드 표현을 보존하여 컴파일러가 어떤 최적화를 실행할 수 있는지에 관해 더 나은 추론을 내릴 수 있게 해줍니다. Android에서 테스트할 때는 LTO 및 CFI 조합이 코드 크기 및 성능에 있어 미미한 수준의 오버헤드로 이어졌습니다. 하지만 LTO를 사용 설정하면 커널 빌드 시간이 크게 늘어납니다.

Clang KCFI에는 LTO가 필요하지 않으므로 최신 Android 커널은 LTO의 빌드 시간 오버헤드 없이 CFI의 이점을 누릴 수 있습니다.

구현

CFI는 Clang CFI 또는 Clang KCFI를 사용 설정하는 CONFIG_CFI_CLANG 옵션으로 제어됩니다.

CFI에 관한 기술적 세부정보, 그리고 정방향 제어 검사가 처리되는 방식은 LLVM 설계 문서를 참고하세요. KCFI는 여기서 -fsanitize=kcfi라고 합니다.

문제 해결

사용 설정 후에는 드라이버에 존재할 수 있는 유형 불일치 오류를 해결합니다. 호환되지 않는 함수 포인터를 통한 간접 함수 호출은 CFI를 트립합니다. CFI 장애가 감지되면 커널은 호출된 함수와 장애로 이어진 스택트레이스를 둘 다 포함하는 경고를 출력합니다. 함수 포인터가 항상 호출된 함수와 동일한 유형을 취하도록 하여 이를 수정하세요.

CFI 장애 디버그를 지원하려면 커널 패닉을 일으키는 대신 경고를 출력하는 CONFIG_CFI_PERMISSIVE를 사용 설정하세요. 프로덕션에는 허용 모드를 사용하면 안 됩니다.

유효성 검사

현재로서는 CFI 전용 CTS 테스트가 없습니다. 대신, CFI 사용 설정 여부와 상관없이 CTS 테스트가 통과되도록 하여 CFI가 기기에 영향을 미치지 않는지 확인하세요.