In the Linux kernel, the following vulnerability has been resolved:
skmsg: Skip zero length skb in skmsgrecvmsg
When running BPF selftests (./testprogs -t sockmapbasic) on a Loongarch platform, the following kernel panic occurs:
[...] Oops[#1]: CPU: 22 PID: 2824 Comm: testprogs Tainted: G OE 6.10.0-rc2+ #18 Hardware name: LOONGSON Dabieshan/Loongson-TC542F0, BIOS Loongson-UDK2018 ... ... ra: 90000000048bf6c0 skmsgrecvmsg+0x120/0x560 ERA: 9000000004162774 copypagetoiter+0x74/0x1c0 CRMD: 000000b0 (PLV0 -IE -DA +PG DACF=CC DACM=CC -WE) PRMD: 0000000c (PPLV0 +PIE +PWE) EUEN: 00000007 (+FPE +SXE +ASXE -BTE) ECFG: 00071c1d (LIE=0,2-4,10-12 VS=7) ESTAT: 00010000 [PIL] (IS= ECode=1 EsubCode=0) BADV: 0000000000000040 PRID: 0014c011 (Loongson-64bit, Loongson-3C5000) Modules linked in: bpftestmod(OE) xtCHECKSUM xtMASQUERADE xtconntrack Process testprogs (pid: 2824, threadinfo=0000000000863a31, task=...) Stack : ... Call Trace: [<9000000004162774>] copypagetoiter+0x74/0x1c0 [<90000000048bf6c0>] skmsgrecvmsg+0x120/0x560 [<90000000049f2b90>] tcpbpfrecvmsgparser+0x170/0x4e0 [<90000000049aae34>] inetrecvmsg+0x54/0x100 [<900000000481ad5c>] sockrecvmsg+0x7c/0xe0 [<900000000481e1a8>] _sysrecvfrom+0x108/0x1c0 [<900000000481e27c>] sysrecvfrom+0x1c/0x40 [<9000000004c076ec>] dosyscall+0x8c/0xc0 [<9000000003731da4>] handlesyscall+0xc4/0x160 Code: ... ---[ end trace 0000000000000000 ]--- Kernel panic - not syncing: Fatal exception Kernel relocated by 0x3510000 .text @ 0x9000000003710000 .data @ 0x9000000004d70000 .bss @ 0x9000000006469400 ---[ end Kernel panic - not syncing: Fatal exception ]--- [...]
This crash happens every time when running sockmapskbverdictshutdown subtest in sockmapbasic.
This crash is because a NULL pointer is passed to pageaddress() in the skmsgrecvmsg(). Due to the different implementations depending on the architecture, pageaddress(NULL) will trigger a panic on Loongarch platform but not on x86 platform. So this bug was hidden on x86 platform for a while, but now it is exposed on Loongarch platform. The root cause is that a zero length skb (skb->len == 0) was put on the queue.
This zero length skb is a TCP FIN packet, which was sent by shutdown(), invoked in testsockmapskbverdictshutdown():
shutdown(p1, SHUT_WR);
In this case, in skpsockskbingressenqueue(), numsge is zero, and no page is put to this sge (see sgsetpage in sgsetpage), but this empty sge is queued into ingressmsg list.
And in skmsgrecvmsg(), this empty sge is used, and a NULL page is got by sgpage(sge). Pass this NULL page to copypagetoiter(), which passes it to kmaplocalpage() and to page_address(), then kernel panics.
To solve this, we should skip this zero length skb. So in skmsgrecvmsg(), if copy is zero, that means it's a zero length skb, skip invoking copypagetoiter(). We are using the EFAULT return triggered by copypagetoiter to check for isfin in tcpbpf.c.