发生内核事件时转储用户和内核堆栈

如果在系统执行内核中的特定代码路径时转储本机内核和用户空间堆栈,将有助于您在调试特定行为(例如在日志中发现的错误)时了解代码流。一种此类情况是:您在日志中发现了 SELinux 拒绝事件消息,但想知道哪个路径触发了这些消息,以便更好地了解为什么发生这种情况。

在本文中,我们将向您介绍如何使用内核工具和 BPF 编译器集合 (BCC) 在 Android 系统中发生内核事件时转储用户和内核堆栈。BCC 是一个用于创建高效内核跟踪功能的工具包。

安装 adeb

adeb 项目会在您的 Android 设备上安装 chroot 环境。我们将在文章的后续步骤中使用 adeb。

按照 adeb README 中的说明安装 adeb。

运行以下命令,在目标 Android 设备上安装 adeb:

adeb prepare --full
adeb 预封装了 BCC,因此上一步还会安装 BCC 的 trace 实用程序,在后续步骤中我们需要使用该程序。

示例:了解哪个路径触发了 SELinux 拒绝事件

向内核添加跟踪点

下面的 diff 会在内核中记录了 SELinux 拒绝事件的位置添加一个跟踪点。在本文的后续部分,我们需要将它与 BCC 配合使用。您可以将该 diff 应用于内核源代码,以便添加 SELinux 拒绝事件跟踪点。如果该 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 拒绝事件跟踪点时跟踪堆栈,请运行以下命令:

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]

上面的调用链是一个统一的内核和用户原生调用链,可让您更好地查看从用户空间直到内核中发生拒绝事件的位置的代码流。在上面的示例调用链中,从用户空间启动的 Binder 事务涉及传递文件描述符。由于文件描述符没有所需的 SELinux 政策设置,因此 SELinux 拒绝了它并且 Binder 事务失败了。

在大多数情况下,通过更改传递给 trace 命令的参数,可以使用相同的跟踪技术在发生系统调用、内核函数输入等事件时转储堆栈。

其他参考资料

如需详细了解 trace,请参阅 BCC 跟踪工具文档。如需详细了解如何在 Android 设备上运行 BCC,请参阅 adeb 项目的 BCC 指南