一、OOM
root@jenet:~# sysctl -a | grep oom
vm.oom_dump_tasks = 1
vm.oom_kill_allocating_task = 0
vm.panic_on_oom = 0
二、panic
root@jenet:~# sysctl -a | grep panic
fs.xfs.panic_mask = 0
kernel.max_rcu_stall_to_panic = 0
kernel.panic = 0
kernel.panic_on_oops = 0
kernel.panic_on_rcu_stall = 0
kernel.panic_on_warn = 0
kernel.panic_print = 0
vm.panic_on_oom = 0
root@jenet:~#
三、串口异常日志
21528.728557\] br0: received packet on lan with own address as source address (addr:5c:85:7e:a1:6f:e9, vlan:0) \[21533.677753\] net_ratelimit: 397 callbacks suppressed \[21533.677777\] br0: received packet on lan with own address as source address (addr:5c:85:7e:a1:6f:e9, vlan:0) \[21533.688851\] br0: received packet on lan with own address as source address (addr:5c:85:7e:a1:6f:e9, vlan:0) \[21533.691098\] br0: received packet on lan with own address as source address (addr:5c:85:7e:a1:6f:e9, vlan:0) \[21533.704985\] br0: received packet on lan with own address as source address (addr:5c:85:7e:a1:6f:e9, vlan:0) \[21533.706235\] br0: received packet on lan with own address as source address (addr:5c:85:7e:a1:6f:e9, vlan:0) \[21533.719366\] br0: received packet on lan with own address as source address (addr:5c:85:7e:a1:6f:e9, vlan:0) \[21533.720188\] br0: received packet on lan with own address as source address (addr:5c:85:7e:a1:6f:e9, vlan:0) \[21533.726428\] br0: received packet on lan with own address as source address (addr:5c:85:7e:a1:6f:e9, vlan:0) \[21533.736416\] br0: received packet on lan with own address as source address (addr:5c:85:7e:a1:6f:e9, vlan:0) \[21533.736992\] br0: received packet on lan with own address as source address (addr:5c:85:7e:a1:6f:e9, vlan:0) \[21539.454695\] net_ratelimit: 337 callbacks suppressed \[21539.454720\] br0: received packet on lan with own address as source address (addr:5c:85:7e:a1:6f:e9, vlan:0) \[21539.467874\] br0: received packet on lan with own address as source address (addr:5c:85:7e:a1:6f:e9, vlan:0) \[21539.481457\] br0: received packet on lan with own address as source address (addr:5c:85:7e:a1:6f:e9, vlan:0) \[21539.495025\] br0: received packet on lan with own address as source address (addr:5c:85:7e:a1:6f:e9, vlan:0) \[21539.508246\] br0: received packet on lan with own address as source address (addr:5c:85:7e:a1:6f:e9, vlan:0) \[21539.521441\] br0: received packet on lan with own address as source address (addr:5c:85:7e:a1:6f:e9, vlan:0) \[21539.535674\] br0: received packet on lan with own address as source address (addr:5c:85:7e:a1:6f:e9, vlan:0) \[21539.548835\] br0: received packet on lan with own address as source address (addr:5c:85:7e:a1:6f:e9, vlan:0) \[21539.562277\] br0: received packet on lan with own address as source address (addr:5c:85:7e:a1:6f:e9, vlan:0) \[21539.575947\] br0: received packet on lan with own address as source address (addr:5c:85:7e:a1:6f:e9, vlan:0) \[29522.595222\] Unable to handle kernel paging request at virtual address 97fffc7faa0403e0 \[29522.603172\] Mem abort info: \[29522.605969\] ESR = 0x0000000086000004 \[29522.609717\] EC = 0x21: IABT (current EL), IL = 32 bits \[29522.615031\] SET = 0, FnV = 0 \[29522.618081\] EA = 0, S1PTW = 0 \[29522.621226\] FSC = 0x04: level 0 translation fault \[29522.626105\] \[97fffc7faa0403e0\] address between user and kernel address ranges
1,接收到自己MAC地址包
这条内核日志信息:
`br0: received packet on lan with own address as source address (addr:5c:85:7e:a1:6f:e9, vlan:0)`
其含义是:**网桥 `br0` 在其端口 `lan` 上接收到了一个数据包,而该包的源 MAC 地址(5c:85:7e:a1:6f:e9)正是网桥自身或其成员端口的 MAC 地址。
该日志是由内核源码 `net/bridge/br_fdb.c` 中的 `br_fdb_update` 函数触发的:
```c
// net/bridge/br_fdb.c
void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source,
const unsigned char *addr, u16 vid, unsigned long flags)
{
...
fdb = fdb_find_rcu(&br->fdb_hash_tbl, addr, vid);
if (likely(fdb)) {
/* 如果发现收到的包的源地址在本地转发数据库(FDB)中被标记为 BR_FDB_LOCAL */
if (unlikely(test_bit(BR_FDB_LOCAL, &fdb->flags))) {
if (net_ratelimit())
br_warn(br, "received packet on %s with own address as source address (addr:%pM, vlan:%u)\n",
source->dev->name, addr, vid);
}
...
}
}
```
内核认为这是一种异常情况,因为正常情况下,设备不应该从外部接收到由自己发出的包。
这种情况通常指向网络拓扑或配置问题:
- 网络环路
物理拓扑中存在环路(例如两根网线连接了同一个交换机的两个口,或者回环连接)。
数据包发出后经过外部网络又被发回了接收端。
- 无线中继/桥接问题
如果你在使用无线网卡做桥接,某些中继器或 AP 可能会将原始包重新广播回来。
- MAC 地址冲突
网络中存在另一个设备的 MAC 地址与你的 `br0` 或其端口完全相同。
- 虚拟化/容器配置错误
在使用 Docker 或虚拟机时,虚拟网卡(veth/tap)配置不当,导致流量回流到物理网桥。
- 交换机芯片 (Switch Chip) 配置
在 RK3568 这类嵌入式平台上,如果使用了外部交换机芯片(如 RTL8367),若 VLAN 隔离或端口镜像配置错误,可能会导致 CPU 发出的包被交换机广播回 CPU。
2,内核页请求异常
**[29522.595222] Unable to handle kernel paging request at virtual address 97fffc7faa0403e0
29522.603172\] Mem abort info: \[29522.605969\] ESR = 0x0000000086000004 \[29522.609717\] EC = 0x21: IABT (current EL), IL = 32 bits \[29522.615031\] SET = 0, FnV = 0 \[29522.618081\] EA = 0, S1PTW = 0 \[29522.621226\] FSC = 0x04: level 0 translation fault \[29522.626105\] \[97fffc7faa0403e0\] address between user and kernel address ranges**
这是一个典型的ARM64内核指令异常(Instruction Abort),通过日志可以得出以下结论:
2.1 核心异常信息解读
- Unable to handle kernel paging request: 内核访问了一个未映射到页表的虚拟地址。
- virtual address 97fffc7faa0403e0 : 这是一个非常奇怪的地址。在 ARM64 中,内核空间通常以
0xFFFF...开头,用户空间以0x0000...开头。该地址(以9开头)处于两者之间的"非法真空地带"(Canonical Address violation),所以日志明确提示:address between user and kernel address ranges。 - EC = 0x21 (IABT) :
IABT代表 Instruction Abort 。这说明 CPU 尝试执行该地址处的代码,而不是读写数据。 - FSC = 0x04 (level 0 translation fault): 转换表第 0 级就出错了,说明这个地址在页表中完全不存在。
2.2 异常是由什么导致的?
由于是指令执行异常 (Instruction Abort) 且地址为非法乱码,最常见的病因有以下几种:
A. 函数指针被篡改 (Function Pointer Corruption)
这是最常见的原因。内核代码调用了一个函数指针,但该指针由于某种原因变成了乱码。
- 原因:结构体被释放后继续使用(Use-After-Free),该内存被重新填充了其他非指针数据;或者发生了越界写(Buffer Overflow),覆盖了结构体中的回调函数指针。
B. 栈被破坏导致返回地址错误 (Stack Corruption)
如果一个函数体内的局部变量发生溢出,可能会覆盖保存堆栈上的 返回地址 (LR/Link Register)。
- 表现 :当函数执行结束尝试返回(执行
RET指令)时,CPU 从栈上加载了一个被破坏的地址并跳转过去,从而触发异常。
C. 野指针调用 (Uninitialized Pointer)
代码中定义了函数指针但没有初始化,或者初始化为某个特定值,导致执行时跳转到了一个随机的内存位置。
D. 内存位翻转 (Bit Flip / Hardware Issue)
虽然概率较低,但在某些不稳定的硬件环境下,DRAM 中的位翻转可能导致指针值从 0xFFFF... 变成 0x97FF... 这种奇怪的值。
2.3 如何进一步定位?
仅凭这段日志很难直接定位到具体的代码行,建议你查看该日志之后的 "Call Trace"(堆栈回溯)部分:
1)查看 pc 指针 :在异常发生时,pc (Program Counter) 应该指向的就是 97fffc7faa0403e0。
2)查看lr寄存器:lr(link register)通常保存了是谁跳转到了这个非法地址。找到lr对应的函数,就是出问题的调用点。
3)检查call trace:
- 如果call trace是完整的,向上找到一个正常的内核函数,那就是崩溃点;
- 如果call trace也是乱码,说明栈已经被彻底破坏了。
2.4 lr和pc指针重启后还有效吗?
这涉及到地址空间和符号化分析的概念。地址本身是静态的,只要内核镜像(Image/vmlinux)没变,地址对应的代码位置就是有效的。
PC(Program Counter):指向崩溃瞬间正在执行的那条指令的虚拟地址。
LR(Link Register):指向函数返回后的下一条指令的虚拟地址(即谁调用了当前函数)
虽然系统重启了,硬件寄存器的值清空了,但是内核代码在内存中的布局通常是固定的(除非开启了KASLR),RK3568上没有开启此功能:

我们并不需要"活着的"进程,需要的是带有调试信息的内核镜像文件vmlinux。
第一步:确认是哪个"进程"闯的祸
虽然系统重启了,但崩溃日志(如果完整的话)会打印出:CPU: 0 PID: 1234 Comm: my_app Not tainted ...
- Comm: my_app: 这就是崩溃时正在运行的进程名称。
- PID:1234: 这是当时的进程号。通过这个信息,就知道是哪个应用程序触发了这段内核路径。
第二步:将地址还原为代码行 (addr2line)
拿到崩溃日志里的pc或lr地址后,在编译内核的那台电脑上执行:
aarch64-linux-gnu-addr2line -e vmlinux -f -p [你的PC地址]
- vmlinux:必须是对应你板子上运行的那个版本的、未压缩且带符号表的内核文件(在内核源码根目录)。
- 结果 :它会直接告诉你:
哪个 .c 文件的第几行代码。
2.5 pc和lr
pc和lr指向的是虚拟地址,在没有开启kaslr时,虚拟地址也是固定的吗?
是的,在没有开启KASLR的情况下,内核主镜像(核心代码)的虚拟地址事完全固定不变的。当你编译完内核生成vmlinux文件的那一刻起,内核中所有函数和静态变量的虚拟地址就已经被链接器(Linker)写死了。这就意味着,只要不重新编译内核,每次系统启动后,同一个函数(比如sys_read)在内存中的虚拟地址都是一模一样的。因此,崩溃日志里打印出的pc和lr虚拟地址,每次也都是固定的。
这正是为什么我们可以事后使用addr2line工具,通过一个地址精准地反推出对应C语言代码行号的原因。因为这本"地址映射字典"(即带有调试信息的vmlinux文件)是静态且唯一的。
需要注意的两个特殊情况。虽然主内核的虚拟地址是固定的,但以下两种情况例外:
2.5.1 动态加载的.ko模块
即使关闭了KASLR,内核模块的虚拟地址也不是固定的。
- 因为模块是在系统运行过程中,通过insmod或modprobe动态加载到内存(通常是vmalloc区域)中的。每次启动,或者以不同顺序加载模块,它被分配到的其实虚拟地址(基地址)都可能不同。
- 如果pc指向的是某个.ko驱动,必须查看崩溃日志里的Modules linked in: 部分。那里通常会打印出发生崩溃时该模块被加载的具体基地址。然后计算 偏移量 = pc地址 - 模块基地址。最后用下面的公式来定位代码
addr2line -e your_driver.ko 相对偏移量
2.5.2 用户空间程序(APP/动态库)
用户空间进程的崩溃(比如Segmentation fault),程序的虚拟地址是否固定取决于是否开启了ASLR(Address Space Layout Raddmization),作用于用户态,由/proc/sys/kernel/randomize_va_space控制。它的值决定了用户程序加载时,栈、堆、共享库、mmap 基址等是否被随机化。
| 值 | 含义 | 说明 |
|---|---|---|
| 0 | 完全关闭 | 不进行任何用户态地址随机化。所有进程每次运行时的地址布局都固定,攻击容易。 |
| 1 | 保守随机化 | 开启栈、共享库(如 ld.so)、mmap 区域、VDSO 等的随机化。不包括堆随机的部分(某些旧内核可能略有差异,但主流行为如此)。 |
| 2 | 完整随机化(默认) | 在"1"的基础上,增加堆 (brk) 的随机化。这是当前所有主流 Linux 发行版的默认设置。 |
四、KASLR
KASLR 是 Kernel Address Space Layout Randomization 的缩写,中文常译为"内核地址空间布局随机化"。它是一项重要的内核安全功能,它通过随机化内核代码和数据的加载地址,能有效防御许多内核漏洞攻击。
1,工作原理:给内核代码"加个锁"
它的核心思想,可以理解为给整个Linux内核在启动时套上一层随机寻址的壳。
- 打破固定模式:在没有KASLR时,内核的启动地址是固定的,攻击者容易预测并加以利用。开启KASLR后,内核的.text(代码段)、.data(数据段)等关键区域会在一个巨大的地址空间中被随机放置。
- 提升成功门槛:由于地址每次都不同,攻击者很难裁到内核的具体位置,这使能利用内核漏洞的难度大增。
- 多重随机化:KASLR不仅会随机化内核映像自身的基址(CONFIG_RADOMIZE_BASE),还会进一步随机化线性映射区、vmalloc区和vmemap区等关键内存区域的布局。
2,开了了KASLR时,如何定位
如果开启了KASLR,崩溃日志通常会打印一个KASLR Offset(偏移量)。需要用 崩溃地址-偏移量得到原始地址,再用addrline分析。