커널 이벤트에서 사용자 및 커널 스택 덤프

커널에서 특정 코드 경로가 실행될 때 네이티브 커널 및 사용자 공간 스택을 덤프하면 로그에서 발견한 오류와 같은 특정 동작을 디버깅할 때 코드 흐름을 이해하는 데 도움이 될 수 있습니다. 일례로, 로그에서 SELinux 거부 메시지를 발견했을 때 발생 원인을 더 정확하게 이해하기 위해 이것이 어떤 경로에서 트리거되었는지 알기를 원할 수 있습니다.

이 도움말에서는 Android 시스템에서 커널 이벤트가 발생할 때 커널 계측 및 BPF Compiler Collection(BCC)을 사용하여 사용자와 커널 스택을 덤프하는 방법을 설명합니다. BCC는 효율적인 커널 추적을 생성하기 위한 도구 키트입니다.

adeb 설치

adeb 프로젝트는 Android 기기에 chroot 환경을 설치합니다. adeb을 이 도움말의 후반 단계에서 사용합니다.

adeb README의 안내에 따라 adeb을 설치합니다.

다음 명령어를 실행하여 타겟 Android 기기에 adeb을 설치합니다.

adeb prepare --full
adeb에는 BCC가 사전 패키징되어 있으므로 adeb 설치 시 나중에 필요한 BCC의 trace 유틸리티도 설치됩니다.

예: SELinux 거부를 트리거한 경로 파악

커널에 tracepoint 추가

아래의 diff는 SELinux 거부가 커널에 로깅되는 지점에 tracepoint를 추가합니다. 이는 도움말의 뒷부분에서 BCC와 함께 사용할 때 필요합니다. diff를 커널 소스에 적용하여 SELinux 거부 tracepoint를 추가할 수 있습니다. diff가 제대로 적용되지 않는 경우 diff를 참조로 사용하여 수동으로 패치합니다.

diff --git a/include/trace/events/selinux.h b/include/trace/events/selinux.h
new file mode 100644
index 000000000000..dac185062634
--- /dev/null
+++ b/include/trace/events/selinux.h
@@ -0,0 +1,34 @@
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM selinux
+
+#if !defined(_TRACE_SELINUX_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_SELINUX_H
+
+#include <linux/ktime.h>
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(selinux_denied,
+
+   TP_PROTO(int cls, int av),
+
+   TP_ARGS(cls, av),
+
+   TP_STRUCT__entry(
+       __field(    int,        cls )
+       __field(    int,        av  )
+   ),
+
+   TP_fast_assign(
+       __entry->cls = cls;
+       __entry->av = av;
+   ),
+
+   TP_printk("denied %d %d",
+       __entry->cls,
+       __entry->av)
+);
+
+#endif /* _TRACE_SELINUX_H */
+
+/* This part ust be outside protection */
+#include <trace/define_trace.h>
diff --git a/security/selinux/avc.c b/security/selinux/avc.c
index 84d9a2e2bbaf..ab04b7c2dd01 100644
--- a/security/selinux/avc.c
+++ b/security/selinux/avc.c
@@ -34,6 +34,9 @@
 #include "avc_ss.h"
 #include "classmap.h"

+#define CREATE_TRACE_POINTS
+#include <trace/events/selinux.h>
+
 #define AVC_CACHE_SLOTS            512
 #define AVC_DEF_CACHE_THRESHOLD        512
 #define AVC_CACHE_RECLAIM      16
@@ -713,6 +716,12 @@ static void avc_audit_pre_callback(struct audit_buffer *ab, void *a)
    struct common_audit_data *ad = a;
    audit_log_format(ab, "avc:  %s ",
             ad->selinux_audit_data->denied ? "denied" : "granted");
+
+   if (ad->selinux_audit_data->denied) {
+       trace_selinux_denied(ad->selinux_audit_data->tclass,
+                    ad->selinux_audit_data->audited);
+   }
+
    avc_dump_av(ab, ad->selinux_audit_data->tclass,
            ad->selinux_audit_data->audited);
    audit_log_format(ab, " for ");

사용자 및 커널 스택 추적

SELinux 거부 tracepoint에 도달하는 경우 스택을 추적하려면 다음 명령어를 실행합니다.

adeb shell
trace -K -U 't:selinux:selinux_denied'

거부가 트리거되면 다음과 같은 메시지가 표시됩니다.

2286    2434    Binder:2286_4   selinux_denied
        avc_audit_pre_callback+0xd8 [kernel]
        avc_audit_pre_callback+0xd8 [kernel]
        common_lsm_audit+0x64 [kernel]
        slow_avc_audit+0x74 [kernel]
        avc_has_perm+0xb8 [kernel]
        selinux_binder_transfer_file+0x158 [kernel]
        security_binder_transfer_file+0x50 [kernel]
        binder_translate_fd+0xcc [kernel]
        binder_transaction+0x1b64 [kernel]
        binder_ioctl+0xadc [kernel]
        do_vfs_ioctl+0x5c8 [kernel]
        sys_ioctl+0x88 [kernel]
        __sys_trace_return+0x0 [kernel]
        __ioctl+0x8 [libc.so]
        android::IPCThreadState::talkWithDriver(bool)+0x104 [libbinder.so]
        android::IPCThreadState::waitForResponse(android::Parcel, int)+0x40
                                                            [libbinder.so]
        android::IPCThreadState::executeCommand(int)+0x460 [libbinder.so]
        android::IPCThreadState::getAndExecuteCommand()+0xa0 [libbinder.so]
        android::IPCThreadState::joinThreadPool(bool)+0x40 [libbinder.so]
        [unknown] [libbinder.so]
        android::Thread::_threadLoop(void)+0x12c [libutils.so]
        android::AndroidRuntime::javaThreadShell(void)+0x90 [libandroid_runtime.so]
        __pthread_start(void*)+0x28 [libc.so]
        __start_thread+0x48 [libc.so]

위의 호출 체인은 통합 커널과 사용자 네이티브 호출 체인으로, 사용자 공간부터 거부가 발생하는 커널까지 코드 흐름을 더 명확하게 볼 수 있습니다. 위의 호출 체인 예시에서는 사용자 공간에서 시작된 바인더 트랜잭션이 파일 설명자를 전달하는 작업을 수반합니다. 파일 설명자에 필요한 SELinux 정책 설정이 없으므로 SELinux는 이를 거부했으며 바인더 트랜잭션이 실패했습니다.

대부분의 경우 trace 명령어에 전달된 인수를 변경하여 시스템 호출, 커널 함수 항목 등에 스택을 덤프하는 데 동일한 추적 기법을 사용할 수 있습니다.

추가 자료

trace에 관한 자세한 내용은 BCC 추적 도구 문서를 참고하세요. Android 기기에서 BCC를 실행하는 것에 관한 자세한 내용은 adeb 프로젝트의 BCC 활용법을 참고하세요.