{"id":"CVE-2026-98954","kind":"vulnerability","aliases":["CVE-2026-98954"],"related":[],"title":"Linux kernel: malformed virtio GSO packets cause out-of-bounds read in network stack","description":"In the Linux kernel, the following vulnerability has been resolved:\n\nnet: always dissect GSO packets in __virtio_net_hdr_to_skb()\n\nCommit 9e8db5913264 (\"net: avoid false positives in untrusted gso\nvalidation\") added a '&& skb->network_header' check before flow-dissecting\nGSO packets without VIRTIO_NET_HDR_F_NEEDS_CSUM in\n__virtio_net_hdr_to_skb(), because some callers (such as tun_get_user(),\ntun_xdp_one(), virtnet_receive_done(), and raw_verify_header()) called\nvirtio_net_hdr_*_to_skb() before initializing skb->network_header and\nskb->dev.\n\nBecause __alloc_skb() and __build_skb_around() zero-initialize\nskb->network_header to 0 (unlike mac_header and transport_header which\nare initialized to ~0U), those four callers always had\nskb->network_header == 0 and bypassed flow dissection in\n__virtio_net_hdr_to_skb(). More generally, skb->network_header is an\noffset from skb->head (where 0 is also a valid offset whenever\nskb_headroom(skb) is 0), not a boolean flag.\n\nWhenever the 'if (gso_type && skb->network_header)' branch was skipped,\nthe fallback 'else if (gso_type)' only pulled nh_min_len + thlen (40 bytes\nfor TCPv4) without dissecting the packet, without validating ip_proto or\nn_proto, and without setting skb->transport_header.\n\nIf the packet has a malformed network header, it is not rejected and a\nsubsequent skb_probe_transport_header() also fails, leaving\nskb->transport_header at ~0U (0xffff). Similarly, if an IPv4 packet\ncarries IP options (ihl > 5) or an IPv6 packet carries extension headers,\npulling only nh_min_len + thlen can leave the TCP header outside\nskb->head. In both cases, tcp_hdrlen(skb) in skb_gso_transport_seglen()\nreads out-of-bounds:\n\n  BUG: KASAN: slab-out-of-bounds in skb_gso_transport_seglen\n  Read of size 2 by task poc/133\n  skb_gso_transport_seglen (net/core/gso.c:155)\n  skb_gso_validate_mac_len (net/core/gso.c:270)\n  tbf_enqueue (net/sched/sch_tbf.c:260)\n  dev_qdisc_enqueue (net/core/dev.c:4227)\n  __dev_queue_xmit (net/core/dev.c:4884)\n\nIn addition, checking virtio_net_hdr_match_proto() only inside\n'if (!skb->protocol)' before flow dissection both skipped validation when\nskb->protocol was pre-set by the caller and rejected VLAN-tagged frames\nwhose outer L2 protocol is ETH_P_8021Q or ETH_P_8021AD.\n\nFix this by:\n1. Initializing skb->dev and skb->network_header (plus skb->protocol for\n   IFF_TUN) before virtio_net_hdr_*_to_skb() in tun_get_user(),\n   tun_xdp_one(), virtnet_receive_done(), and raw_verify_header(). In\n   tun_get_user(), drop the redundant skb_reset_mac_header(skb) in the\n   IFF_TUN case since __virtio_net_hdr_to_skb() unconditionally resets\n   mac_header.\n2. Removing '&& skb->network_header' and the unvalidated\n   'else if (gso_type)' fallback in __virtio_net_hdr_to_skb() so all GSO\n   packets without VIRTIO_NET_HDR_F_NEEDS_CSUM are flow-dissected, have\n   their transport header pulled into linear data, and have\n   skb->transport_header set.\n3. Moving the virtio_net_hdr_match_proto() check to after\n   skb_flow_dissect_flow_keys_basic(), validating keys.basic.n_proto\n   against hdr_gso_type.","summary":"The Linux kernel's virtio network header handling skips flow dissection for some GSO packets, so a malformed packet can leave the transport header unset. The network scheduler then reads past the end of a slab buffer, which KASAN reports as an out-of-bounds read.","severity":"unknown","cvss":null,"epss":null,"exploited":false,"exploited_at":null,"products":[],"ecosystems":[],"tags":["kvm","linux-kernel","runtime","virtualization","vm-hosts"],"not_affected_if":["no-virtio-or-tun-use"],"how_to_check":"- `uname -r` and compare against the fixed kernel listed in the upstream CVE record\n- Check whether virtio_net, tun or raw sockets are in use with `lsmod | grep -E 'virtio_net|tun'`","fix":"- Upgrade the Linux kernel to a release that includes the upstream commit resolving CVE-2026-98954 (version unknown from sources)\n- Reboot after installing the patched kernel","status":"open","published_at":"2026-10-23T13:17:16+00:00","first_seen_at":"2026-10-23T21:57:33+00:00","triaged_at":"2026-10-25T04:57:51+00:00","updated_at":"2026-10-25T04:57:51+00:00","category":"runtime","confidence":0.5,"triage":{"at":"2026-10-25T04:57:51Z","model":"claude-haiku-5-5","output":{"fix":"- Upgrade the Linux kernel to a release that includes the upstream commit resolving CVE-2026-98954 (version unknown from sources)\n- Reboot after installing the patched kernel","reach":"local","title":"Linux kernel: malformed virtio GSO packets cause out-of-bounds read in network stack","impact":"medium","summary":"The Linux kernel's virtio network header handling skips flow dissection for some GSO packets, so a malformed packet can leave the transport header unset. The network scheduler then reads past the end of a slab buffer, which KASAN reports as an out-of-bounds read.","category":"runtime","nicknames":[],"who_cares":["linux-kernel","virtualization","kvm","vm-hosts"],"confidence":0.5,"needs_auth":"unknown","worst_case":"kernel out-of-bounds read; possible crash or memory disclosure","analyst_note":"Find the affected and fixed kernel version ranges and whether an unprivileged local user or guest can trigger the path, from the NVD entry and the kernel stable commit.","how_to_check":"- `uname -r` and compare against the fixed kernel listed in the upstream CVE record\n- Check whether virtio_net, tun or raw sockets are in use with `lsmod | grep -E 'virtio_net|tun'`","needs_analyst":true,"not_affected_if":["no-virtio-or-tun-use"],"who_is_affected":"Systems running a Linux kernel with the virtio_net, tun or raw socket paths that call the affected GSO validation code. Hosts that do not use these drivers or accept untrusted virtio/TUN input are less exposed. The sources do not state a specific affected version range.","user_interaction":"no","needs_special_setup":"unknown","severity_if_unknown":"unknown"},"cost_usd":0.000767,"input_tokens":4580,"output_tokens":618,"severity_before":"unknown"},"analyst_status":null,"analyst_note":null,"search":"'-2026':17A,20A '-98954':18A,21A '0':181C,201C,224C,235C '0u':192C,310C '0xffff':311C '1':455C '155':383C '2':374C,516C '260':394C '270':390C '3':564C '40':263C '4227':399C '4884':404C '5':321C '8021ad':451C '8021q':447C '9e8':100C 'add':110C 'addit':406C 'alloc':168C 'also':226C,303C 'alway':89C,196C 'around':173C 'avoid':103C 'basic':580C 'boolean':238C 'bound':11A,76B,358C,365C 'branch':248C 'buffer':67B 'bug':359C 'build':171C 'bypass':203C 'byte':264C 'call':153C 'caller':137C,195C,433C 'carri':317C,326C 'case':345C,505C 'caus':7A 'check':115C,407C,572C 'commit':99C 'csum':128C,545C 'cve':16A,19A 'data':557C 'db5913264':101C 'dev':166C,395C,400C,458C 'dissect':41B,90C,119C,205C,268C,420C,549C,577C 'done':148C,484C 'drop':493C 'els':253C,524C 'end':63B 'enqueu':392C,397C 'eth':445C,449C 'extens':327C 'f':126C,543C 'fail':304C 'fallback':252C,528C 'fals':104C 'fix':452C 'flag':239C 'flow':40B,118C,204C,419C,548C,576C,578C 'flow-dissect':117C,547C 'follow':83C 'four':194C 'frame':439C 'general':213C 'get':141C,477C,491C 'gso':5A,44B,91C,108C,120C,243C,255C,351C,368C,379C,385C,526C,537C,587C 'handl':38B 'hdr':96C,125C,132C,156C,209C,410C,472C,509C,532C,542C,569C,586C 'hdrlen':347C 'head':222C,342C 'header':37B,54B,114C,152C,163C,179C,184C,187C,200C,216C,247C,283C,291C,302C,308C,328C,339C,462C,488C,499C,515C,520C,553C,562C 'headroom':232C 'host':30B 'iff':467C,503C 'ihl':320C 'initi':160C,176C,190C,456C 'insid':414C 'ip':273C,318C 'ipv4':315C 'ipv6':324C 'kasan':69B,360C 'kernel':2A,25B,33B,81C 'key':579C 'keys.basic':582C 'kvm':22B 'l2':442C 'leav':51B,305C,336C 'len':261C,333C,388C 'linear':556C 'linux':1A,24B,32B,80C 'linux-kernel':23B 'mac':183C,387C,498C,514C 'malform':3A,48B,289C 'match':411C,570C 'min':260C,332C 'move':565C 'n':276C,583C 'need':127C,544C 'net':88C,95C,102C,124C,131C,155C,208C,409C,471C,508C,531C,541C,568C 'net/core/dev.c':398C,403C 'net/core/gso.c':382C,389C 'net/sched/sch_tbf.c':393C 'network':14A,36B,57B,113C,162C,178C,199C,215C,246C,290C,461C,519C 'nh':259C,331C 'offset':219C,229C 'one':145C,481C 'option':319C 'out-of-bound':8A,73B,355C 'outer':441C 'outsid':340C 'p':446C,450C 'packet':6A,45B,49B,92C,121C,270C,286C,316C,325C,538C 'past':61B 'plus':463C 'poc/133':377C 'posit':105C 'pre':429C 'pre-set':428C 'probe':300C 'proto':274C,277C,412C,571C,584C 'protocol':417C,426C,443C,465C 'pull':258C,329C,554C 'qdisc':396C 'queue':401C 'raw':150C,486C 'read':12A,60B,77B,354C,371C 'receiv':147C,483C 'redund':495C 'reject':295C,435C 'remov':517C 'report':70B 'reset':497C,513C 'resolv':87C 'runtim':26B 'schedul':58B 'seglen':353C,370C,381C 'set':280C,430C,563C 'similar':312C 'sinc':506C 'size':373C 'skb':98C,112C,134C,158C,161C,165C,169C,172C,177C,198C,211C,214C,221C,231C,233C,245C,281C,299C,306C,341C,348C,350C,367C,378C,384C,416C,425C,457C,460C,464C,474C,496C,500C,511C,518C,534C,560C,575C 'skip':39B,250C,422C 'slab':66B,362C 'slab-out-of-bound':361C 'stack':15A 'subsequ':298C 'tag':438C 'task':376C 'tbf':391C 'tcp':338C,346C 'tcpv4':266C 'thlen':262C,334C 'transport':53B,186C,282C,301C,307C,352C,369C,380C,552C,561C 'tun':140C,143C,468C,476C,479C,490C,504C 'type':244C,256C,527C,588C 'uncondit':512C 'unlik':182C 'unset':55B 'untrust':107C 'unvalid':523C 'user':142C,478C,492C 'valid':109C,228C,272C,386C,423C,581C 'verifi':151C,487C 'virtio':4A,35B,94C,123C,130C,154C,207C,408C,470C,507C,530C,540C,567C 'virtnet':146C,482C 'virtual':27B 'vlan':437C 'vlan-tag':436C 'vm':29B 'vm-host':28B 'vulner':84C 'whenev':230C,240C 'whose':440C 'without':122C,267C,271C,279C,539C 'xdp':144C,480C 'xmit':402C 'zero':175C 'zero-initi':174C","nicknames":[],"mitigations":[],"who_affected":"Systems running a Linux kernel with the virtio_net, tun or raw socket paths that call the affected GSO validation code. Hosts that do not use these drivers or accept untrusted virtio/TUN input are less exposed. The sources do not state a specific affected version range.","tier":"C","verified":{},"analyst_revisit_at":null,"first_public_at":"2026-10-23T13:17:16+00:00","kev":null,"cvss_vector":null,"cvss_by":null,"poc_count":0,"sources":[{"source":"nvd","external_id":"CVE-2026-98954","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-98954","severity":null,"published_at":"2026-10-23T13:17:16+00:00","modified_at":"2026-10-23T13:17:16+00:00","first_seen_at":"2026-10-23T21:57:33+00:00","fetched_at":"2026-10-23T22:29:03+00:00","note":null}],"documents":[],"notice":"Free to read at a human pace. An API with keys and an MCP server are in preparation; early access: hello@infrastack.ai"}