Linux 中是否有某种机制通过清零高 16 位来毒害地址?
我正在调试英特尔 x86-64 机器上的内核崩溃。 导致崩溃的指令尝试访问0x880139f3da00
的地址:
crash> bt
R10: 0000000000000001 R11: 0000000000000001 R12: 0000880139f3da00
~~~~~~~~~~~~~~~~~~~~~
crash> p arp_tbl.nht->hash_buckets[255]
$66 = (struct neighbour *) 0x880139f3da00
crash> p *arp_tbl.nht->hash_buckets[255]
Cannot access memory at address 0x880139f3da00
hash_buckets
表有效:
crash> p arp_tbl.nht->hash_buckets[253]
$70 = (struct neighbour *) 0xffff88007325ae00
$71 = {
next = 0x0,
tbl = 0xffffffff81abbf20 <arp_tbl>,
将大字设置为0xffff
会使地址有效并返回有效的数据结构:
crash> p *((struct neighbour *)0xffff880139f3da00)
$73 = {
next = 0xffff88006de69a00,
tbl = 0xffffffff81abbf20 <arp_tbl>,
... rest looks reasonable too ...
结构由RCU操作更新(例如,很可能由neigh_flush_dev()
中的这些操作更新(。 那么,地址以这种方式变得无效的原因可能是什么?
我可以排除硬件缺陷(在两台机器上看到,地址不同(。 系统运行的是内核为 3.10.0-514.6.1.el7.centos.plus.x86_64 至 3.10.0-514.21.2.el7.centos.plus.x86_64 的 CentOS 7。
更新
从另一个故障转储中,我看到 IPv6 数据包的skb
crash> p *((struct sk_buff *)0xffff880070e25e00)
$57 = {
transport_header = 54,
network_header = 14,
mac_header = 0,
...
head = 0xffff880138e28000 "",
data = 0xffff880138e2800e "`",
...
}
写入前 0x8 个字节时崩溃
#define HH_DATA_MOD 16
static inline int neigh_hh_output(const struct hh_cache *hh, struct sk_buff *skb)
{
if (likely(hh_len <= HH_DATA_MOD)) {
memcpy(skb->data - HH_DATA_MOD, hh->hh_data, HH_DATA_MOD); <<<<<
这可以解释为什么两个字节被覆盖(16 - 14(。
你能检查从中读取这个地址的内存位置吗? 通常,这种"部分零"读取是在该区域上运行 MEMSET 的结果。 在此CPU触发崩溃后,可能有足够的时间让正在修改该区域的其他人完成归零,甚至可能用其他数据填充它。
到目前为止,没有理由怀疑RCU在这里发挥了任何作用
这绝对不是内核完成的"中毒"(以这种方式这样做会很奇怪(。 但是,如果崩溃是可重现的(您说它至少发生在 2 台不同的机器上?(,那么运行调试内核可能会有所帮助,尤其是在启用 Slab 调试的情况下。