Welcome to the Black Parade
962 subscribers
560 photos
4 videos
10 files
273 links
Death has many faces, I look forward to seeing this one.
Download Telegram
拖了好几天,终于在明天觐见 bpf 子系统 tailcall maintainer 之前测完了 bpf_get_stackid() vs 手动收集 LR 再用 header-payload 模式提交变长数据。

结论:别看手动模式复杂,但是手动模式性能更好、栈回溯更全、更不易丢失数据、且可继续扩展变长消息。

https://github.com/jschwinger233/bpf_get_stack_test
3
和 LeonHwang 年度面基不完全纪要,负面版,被逼着发的。太负面的内容就只保留一个小标题好了(

1. bug 太多,只能祝福。

2. 干不下去,上班如坟。

3. 全组被端,全部开除。

4. 周周刷领英,找不到工作。

5. kprobe 和 fentry。我一直更喜欢 kprobe 因为 pt_regs 非常灵活,而 fentry 莫名其妙加了一堆限制+校验 BTF 结果体验还不如 kprobe,建议合体。不过黄师最近让 fentry 可以获取 LR 的工作确实很精彩,最重要的是很合理,一看就知道思路是正确的。

6. 用三年合并一个大功能。回顾 Anton 过去两年 static keys 曲折经历已经可以总结出给内核 bpf 提交重大改变的流程了:第一年发 PoC,patch cover letter 写作文,参加 LPC 画大饼说服四巨头;第二年来回拉扯,patch 提交到 v10 被 nack,参加 LPC 和四巨头现场吵实现细节下不了台;第三年 patch 改到 v20 运气好的话终于能合并。喜欢我内核开发流程吗?

7. GNU/Linux,自由个屁。等万岁爷打那个岛,中国开发者就等着一样的下场吧。

8. Linux 就是故意用这种难用的方式来阻止更多人参与到内核开发。反馈流程极长,正反馈极少,沟通成本高,人情社会,还不解决眼前的工程问题—— bpf-next 和我 5.15 有何相干,就算把 bug 复现流程详细介绍给开发者也常常不想管,大家太疲惫了,不想动。

9. 新的一年,黄师打算开搞 LLVM,而我甚至将目光转向了。。。区块链,如果找不到工作的话😀都怪外星人。当然 pwru 在高负载下的可用性会是我非常感兴趣的一个优化工作。希望日本 LPC 见。

10. 共识:不知道未来咋办。也算是岁岁有今朝了。
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2😱1
问下面两个 bpf_ringbuf_reserve 的第二参数在 verifier 眼里有何区别

A:
u32 data_len = BPF_CORE_READ(skb, tail) - (u32)BPF_CORE_READ(skb, network_header);
u64 total_len = sizeof(struct event) + data_len;
void *ringbuf = bpf_ringbuf_reserve(&event_ringbuf, total_len, 0);


B:
u64 depth = 0;
for (depth = 0; depth < MAX_STACK_DEPTH-3; depth++)
if (cond)
break;

u64 total_len = sizeof(u64) + (depth + 3) * sizeof(u64);
void *ringbuf = bpf_ringbuf_reserve(&event_ringbuf, total_len, 0);


答案是,B 的第二参数在 verifier 眼里是常量🤯A 的第二参数不是。

这时候观众就要问了,为什么 B 的 total_len 是常量?因为 verifier 把循环展开作为平行宇宙分别检查,比如宇宙一的 for 循环执行了一次就 break,此时 depth 为常量 0;宇宙二 for 执行了两次才 break,depth 为常量 1;…… 。每个宇宙里的 total_len 就是常量。

然后 bpf_ringbuf_reserve 第二参数只接受常量😭 所以 A 没救了,我也没救了。。。
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
今天(2024-01-20)是一年一度的 Blue Monday!大家来听新浪潮乐队 New Order 的同名歌曲吧!

spotify: https://open.spotify.com/track/6hHc7Pks7wtBIW8Z6A0iFq
qq: https://c6.y.qq.com/base/fcgi-bin/u?__=BHmOFuDaBymZ
要是有那位老师能教我用 bpftrace 读 skb 非线性区,我就🤬🥹

我卡在了 page_address(page) 这一步:
k:__dev_queue_xmit
{
$skb = (struct sk_buff *)reg("di");
$data=(uint8*)($skb->network_header + $skb->head);
if ((*(uint8[4]*)($data+16) != pton("1.1.1.1"))) {
return;
}
$shinfo = (struct skb_shared_info *)($skb->head + $skb->end);
if ($shinfo == 0) {
return;
}
$i = 0;
while ($i < 17) {
$frag = *(struct bio_vec *)(((uint8*)$shinfo) + 48 + $i*16);
if ($frag.bv_page == 0) {
return;
}
printf("page=%llx len=%d off=%d ", $frag.bv_page, $frag.bv_len, $frag.bv_offset);
printf("payload=%r\n", buf(kptr(((uint8*)$frag.bv_page) + $frag.bv_offset), $frag.bv_len));
$i++;
}
}
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥1
搞定了,疑似全网第一 😃

#define vmemmap_base ((uint64)0xffffffffaf6f5558)
#define page_offset_base ((uint64)0xffffffffaf6f5568)
k:__dev_queue_xmit
{
$skb = (struct sk_buff *)reg("di");
$data=(uint8*)($skb->network_header + $skb->head);
if ((*(uint8[4]*)($data+16) != pton("1.1.1.1"))) {
return;
}

$vmemmap = *(uint64*)vmemmap_base;
$page_offset = *(uint64*)page_offset_base;

$shinfo = (struct skb_shared_info *)($skb->head + $skb->end);
if ($shinfo == 0) {
return;
}
$i = (uint8)0;
$nr_frags = $shinfo->nr_frags;
while ($i < 17) {
if ($i >= $nr_frags) {
return;
}
$frag = *(struct bio_vec *)(((uint8*)$shinfo) + 48 + $i*16);
if ($frag.bv_page == 0) {
return;
}
printf("page=%llx len=%d off=%d ", $frag.bv_page, $frag.bv_len, $frag.bv_offset);
$page = (uint64)$frag.bv_page - $vmemmap;
$page = (uint64)$page >> 6;
$page = (uint64)$page << 0xc;
$page = (uint64)$page + $page_offset;
printf("page=%llx payload=%s\n", $page, str((uint8*)$page + $frag.bv_offset, $frag.bv_len));
$i++;
}
}


对着 6.8 源码跳转了半天都写不对,最后用 tracing 技术定位汇编,一下就出来了!

等着我 ringbuf 手动档和 bpf_skb_output() 一决高下吧!
Please open Telegram to view this post
VIEW IN TELEGRAM
👍122🥰2
分享一下探索经历,因为我也觉得很有趣。

总之我已经知道了 skb 非线性区藏有 tcp payload,一些简单的摸索之后很容易找到 (struct skb_shared_info*)(skb->head + skb->end)->frags ,类型定义是这样的
struct skb_shared_info {
...
struct bio_vec frags[17]; /* 48 272 */
};
struct bio_vec {
struct page * bv_page; /* 0 8 */
unsigned int bv_len; /* 8 4 */
unsigned int bv_offset; /* 12 4 */
};

看起来已经近在咫尺了,那个 page 指针没准就指向 tcp payload,收工大吉。

然后在内核源码里颠鸾倒凤数小时,AI 也已问烂,脚本改了四十多版,依然不知道怎么把 struct page * 转化成正确的 u64 vaddr 。

如果你也像我一样阅读代码的能力菜到口交,这就是为你准备的方法!

首先找一个内核要“展开”非线性区的地方,熟悉 bpf 的可以会脱口而出 bpf_skb_pull_data(),但更好的选择是 tcpdump! 你想,不管 skb 有多少数据在非线性区,tcpdump 总能 dump 完整的 payload,它必然从非线性区拷贝数据出来了。

所以快速看一下 tcpdump 的内核函数 tpacket_rcv (如果不熟悉也可以 pwru 一下你就知道),找到拷贝 skb 数据的函数 skb_copy_bits() ,跳进去,搜 frag,立刻看到
2865                         struct page *p;
2866 u8 *vaddr;
2867
2868 if (copy > len)
2869 copy = len;
2870
2871 skb_frag_foreach_page(f,
2872 skb_frag_off(f) + offset - start,
2873 copy, p, p_off, p_len, copied) {
2874 vaddr = kmap_atomic(p);
2875 memcpy(to + copied, vaddr + p_off, p_len);
2876 kunmap_atomic(vaddr);
2877 }


对,就是这个 kmap_atomic() 把一个 struct page * 转成 u64 vaddr,然后我们就可以直接 memcpy / bpf_probe_read_kernel。

不看 kmap_atomic() 的代码,我们来找它的汇编。

运行 ktcpdump -i 'skb_copy_bits+*' -d /usr/lib/debug/boot/vmlinux-6.8.0-50-generic 'dst host 1.1.1.1 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)'tcpdump -ni eth0 dst host 1.1.1.1 ,因为我们只想看 net/core/skbuff.c 2874 行,找到附近的输出:
171  skb_copy_bits+237 /build/linux-hwe-6.8-GtMYNi/linux-hwe-6.8-6.8.0/net/core/skbuff.c:2871
172 skb_copy_bits+239
173 skb_copy_bits+241 /build/linux-hwe-6.8-GtMYNi/linux-hwe-6.8-6.8.0/arch/x86/include/asm/preempt.h:79
174 skb_copy_bits+248 /build/linux-hwe-6.8-GtMYNi/linux-hwe-6.8-6.8.0/include/linux/uaccess.h:210
175 skb_copy_bits+256 /build/linux-hwe-6.8-GtMYNi/linux-hwe-6.8-6.8.0/include/linux/mm.h:2226
176 skb_copy_bits+263 /build/linux-hwe-6.8-GtMYNi/linux-hwe-6.8-6.8.0/net/core/skbuff.c:2875
177 skb_copy_bits+265 /build/linux-hwe-6.8-GtMYNi/linux-hwe-6.8-6.8.0/net/core/skbuff.c:2875
178 skb_copy_bits+268 /build/linux-hwe-6.8-GtMYNi/linux-hwe-6.8-6.8.0/include/linux/mm.h:2226
179 skb_copy_bits+272
180 skb_copy_bits+276
181 skb_copy_bits+283 /build/linux-hwe-6.8-GtMYNi/linux-hwe-6.8-6.8.0/net/core/skbuff.c:2875
182 skb_copy_bits+286 // CALL __memcpy


mm.h:2226 什么的不重要,反正 skb_copy_bits+286 就是 call __memcpy (天啦撸 memcpy 居然没有内联),它第二参数就是 vaddr + offset,去看看它是怎么来的。

gdb /usr/lib/debug/boot/vmlinux-6.8.0-50-generic 直接看 +285
(gdb) disas skb_copy_bits
0xffffffff81eb68bf <+255>: sub 0xe3ec92(%rip),%rsi # 0xffffffff82cf5558 <vmemmap_base>
0xffffffff81eb68c6 <+262>: mov %ebx,%edx
0xffffffff81eb68c8 <+264>: mov %r13,%rdi
0xffffffff81eb68cb <+267>: sar $0x6,%rsi
0xffffffff81eb68cf <+271>: shl $0xc,%rsi
0xffffffff81eb68d3 <+275>: add 0xe3ec8e(%rip),%rsi # 0xffffffff82cf5568 <page_offset_base>
0xffffffff81eb68da <+282>: add %rax,%rsi
0xffffffff81eb68dd <+285>: call 0xffffffff8220d130 <memcpy>


注意不要用 disas/m ,本频道多次警告。

关注 rsi 第二参数的经历:
1. sub 0xe3ec92(%rip),%rsi => page -= *vmemmap_base
2. sar $0x6,%rsi => page >>= 6
3. shl $0xc,%rsi => page <<= 0xc
4. add 0xe3ec8e(%rip),%rsi => page += *page_offset_base
5. add %rax,%rsi => page += off
至此,已经可以用 bpftrace 写出最核心的 page_to_vaddr 的逻辑:
#define vmemmap_base ((uint64)0xffffffffaf6f5558)
#define page_offset_base ((uint64)0xffffffffaf6f5568)
{
$vmemmap = *(uint64*)vmemmap_base;
$page_offset = *(uint64*)page_offset_base;
$page = (uint64)$frag.bv_page - $vmemmap;
$page = (uint64)$page >> 6;
$page = (uint64)$page << 0xc;
$page = (uint64)$page + $page_offset;
}

其中 vmemmap_base 和 page_offset_base 查询 /proc/kallsyms 得到。

现在我终于可以说,我能在内核任意地方抓包了(并没有哈,kprobe 有一大堆 notrace 禁飞区,除此之外大部分地方还是可以的
👍7
测完了 tp_btf/netif_receive_skb + bpf_skb_output 大战 kporbe/__netif_receive_skb_core + 手动拆非线性区,结果是🤬😭

rps 从 80192.46 baseline 分别下降到 60532.69 (bpf_skb_output) 和 46360.68 (手动档),下降越少越好😀 tcpdump 只下降到 63131.31 ,赢!

所以仅剩的卖点就是好玩了。不对,仅剩的卖点是可以在任意地方抓包,但我还是觉得好玩最重要!只是狂歌一曲,恍惚间就化入无穷,debug,杯莫停,人生不过一场大梦~

(下面开始找补)

其实在任意地方抓包还是有工程价值的,尤其是 tcpdump 只能在 interface 上抓,还容易被 bpf 干扰,还有美美隐身的 xfrm , slirp 转化的 vxlan0 ,ipsec offload ,还有 GRO GSO 这些之前几乎没有工具能观测的内核功能以前只能看着文档催眠自己“对就是这样的不要多想照着抄就行了”现在也终于能手动展开 skb_shared_info 而仔细灌肠。

费曼说 “What I cannot create, I do not understand” ;我说 “What I cannot see, I do not believe” 。
Please open Telegram to view this post
VIEW IN TELEGRAM
我不信内核还有比 xfrm 更糟糕的用户态 api 😀

# ip x p add src 10.0.0.0/8 dst 10.36.1.0/24 dir in mark 0x58d73e00 mask 0xffffff00 
# ip x p add src 10.0.0.0/8 dst 10.36.1.0/24 dir in mark 0x58d73e00 mask 0xffffff00
RTNETLINK answers: File exists
# ip x p add src 10.0.0.0/8 dst 10.36.1.0/24 dir in mark 0x58d73e01 mask 0xffffff00
# ip x p
src 10.0.0.0/8 dst 10.36.1.0/24
dir in priority 0
mark 0x58d73e01/0xffffff00
src 10.0.0.0/8 dst 10.36.1.0/24
dir in priority 0
mark 0x58d73e00/0xffffff00
Please open Telegram to view this post
VIEW IN TELEGRAM
👻1
乌克兰同事被强制征兵了😢 希望公司能帮帮他。

他在内核做了很多非平凡贡献,主导了某项目 clang10 -> clang17 的升级,是编译器专家,XDP/XSK 专家,网卡性能专家,bpf verifier 专家。

https://lore.kernel.org/netdev/?q=maximmi

😢48😱142👏1😁1🕊1
我其实不太追新,我看着各种频道里我读不出来的 AI 名字推陈出新毫无波动,继续日常和 gpt 做英语语法检查、问答 kubectl 怎么过滤字段、问一些我理解不了的内核问题然后收获一堆胡说八道,习惯了,预期建立得低就不会失望。

但是! DeepSeek!真的太爆炸了,下面请欣赏 chatgpt 4o 回答得一塌糊涂的编程问题在 deepseek 的精彩表现。

1. 用 golang 标准库 syscall 实现 inotify watch 普通文本文件,然后输出每次修改事件的 diff。

我觉得这个问题超简单,常用 syscall 的 API 不管是文档和博客都很多,我只是要求用 go 翻译一下,结果收获了无数光怪陆离,曾经是我用来测试 AI 写代码的标准题目,还发过一贴嘲笑 AI 们连 API 小子都做不好实在太菜了,收获了一堆 👎

deepseek 则一发入魂,图一就是核心逻辑,完全正确, 有个 u32 -> i32 的类型转化搞错了,不重要,要知道 gpt 4o 还搁那儿无限 for 循环里 cat file + sleep 呢。

2. 写 tcpdump filter,过滤出指定 src + dst ip 的 ipv6 tcp reset in vxlan。

这个问题前段时间也 po 过,gpt 4o 令人失望到起手就 tcp[tcpflags]=tcp-syn,而我在提问里给的 non-vxlan 示范里已经教学使用 ip6[53]&0x4!=0 了,它理解不了为什么,我多次指出错误之后更是开始打滚输出重复错误的代码。

deepseek 就清新脱俗了,在陷入长达两分钟的长考之后(长考过程很有趣),直接给出了完全正确的表达式,见图二。震撼!

3. 用 bpftrace 读 skb 非线性区。

正是上上上上条 post,gpt 4o 在多轮对话之后害直接解引用 page 指针,我都气笑了。但是 deepseek 直接给出图三的输出,虽错,但错误的原因是它脚本适用于 CONFIG_FLATMEM 配置下的内核而我的是 CONFIG_SPARSEMEM_VMEMMAP。告诉它我的配置之后,图四几乎对了,只差一点点,只要把 kaddr("vmemmap") 改成 *(uint64*)kaddr("vmemmap_base") 就赢了。

这才是我心中的 AI,让我看看怎么给它打钱来着。现在入 AI 神教应该还来得及,我去带路先(

(prompt 见评论区)
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
9🔥2👎1
she年快乐!

Make love, not code 👩‍💻
Please open Telegram to view this post
VIEW IN TELEGRAM
🥰10
不可思议 #NSFW
🌭2
博德之门3里下城区“精灵之歌”酒店里有个讲笑话比赛,中文翻译一如既往的惨不忍睹,堪比神界原罪2里那灾难性的“仁义礼智信”。不如来看看那些笑话笑点究竟是什么8

Say, what’s the difference between a dwarf and a bulette? You can’t toss the bulette!
问,一个矮人和鲨蜥兽有什么区别?鲨蜥兽扔不动

Dwarf-tossing 是澳大利亚人发明的酒吧娱乐活动,就真的是把 dwarf 拿来扔比谁扔得远。。不愧是囚犯流放地,我真的黄豆流汗。。。

Hey! I asked my wizard to cast Shatter. He said, ‘Shatter? I hardly know ‘er!’
哈!我让我的法师施展精力流失。他说,”经理?我基本不认识她啊!“

模板 “X-er? I hardly know 'er!”,比如 “Liquor? I hardly know her!”,liquor 被空耳成 lick her;“Poker? I hardly know her!”,poker 被空耳成 poke her。后来被延申到所有 -er 结尾的单词,比如下次你同事问你怎么用 hrtimer 你就可以 hardly know her 不要在职场用

You know why I hate vampires? They just suck.
你知道我为什么讨厌吸血鬼?因为他们只吸不吐。

这个翻译 sucks

I tried sleeping with a dragonborn the other night, but it didn’t work out - he had reptile dysfunction.
有天晚上我打算跟一个龙裔睡觉,但是没有成功——因为他龙萎了。

reptile dysfunction 谐音 erectile dysfunction (ED)。其实这个翻译还行,“他龙,萎了”。

The Dead Three, am I right? What losers. You know why Bane worshippers will never conquer the world? They don’t have the Bhaals!
死亡三神,我说得对吗?真是。你知道班恩的崇拜者为什么从来没能征服这个世界吗?因为他们没有巴尔!

DND 中的死亡三神是纷争之神班恩、死者之神米尔寇、谋杀之神巴尔,这个笑话的笑点是 Bhaals 可以被空耳成 Balls:they don't have the balls 他们没胆。
Bhaal 在《暗黑破坏神2》里被读作 Baal 巴尔,但其实很多年前就有讨论 Bhaal 是否应该读作 Ball

A mind flayer’s duergar slave lost his nose, so I asked the illiquid, ‘How does he smell?’ and it said, ‘I have eaten its brain’.
一个夺心魔的灰矮人奴隶失去了他的鼻子,所以我问灵吸怪,“他闻起来怎么样?”,然后他说,“脑子没味儿”。

来自经典笑话 “My dog has no nose. How does he smell? Terrible!” 中文翻译完全失去了 how does he smell 在英语里双关。

Our cleric tried to cast Healing Word on a ham, but it was already cured.
我们的牧师打算对一个火腿施展治愈真言,但它已经风干了。

Cure a ham 风干火腿,翻译很难译出这种一词多义。

最后,从今天起,请叫我博得人 Baldurian.
👍72
哇原来!是用 gas 来控制 opcode 复杂度!这听起来比 bpf verifier 先进多了?()

#zzh为将来被开除的我指明方向
Please open Telegram to view this post
VIEW IN TELEGRAM
虽然我一路空格跳剧情,但最后还是感到浪漫和蛋蛋的忧伤。。

在最后回望来路,敬过去的冒险,敬一路的伙伴,敬自由和内心的平静,敬一切让自己开心的 bugs,祝君好运,以及最重要的:遇到值得解决的问题。"Death is but the next great adventure" —— Dumbledore,这既是结束也是开始。

Welcome to the Black Parade.
👏1