{"id":"CVE-2026-98595","kind":"vulnerability","aliases":["CVE-2026-98595"],"related":[],"title":"Linux kernel UDP: disconnected sockets stay in the 4-tuple hash table","description":"In the Linux kernel, the following vulnerability has been resolved:\n\nudp: remove a disconnected socket from the 4-tuple hash table\n\nA UDP socket bound to a specific address and port keeps its entry in the\n4-tuple hash table after it is disconnected:\n\n    sk binds to 127.0.0.1:21001\n    sk connects to 127.0.0.2:20001\t// filed in the 4-tuple table\n    sk disconnects, connect(AF_UNSPEC)\t// still filed, peer now 0.0.0.0:0\n\n__udp_disconnect() takes a socket out of that table only as a side effect\nof ->rehash() or ->unhash(), and it skips ->rehash() when\nSOCK_BINDADDR_LOCK is set and ->unhash() when SOCK_BINDPORT_LOCK is set.\ncommit 6996a2d2d0a6 (\"udp: Unhash auto-bound connected sk from 4-tuple hash\ntable when disconnected.\") fixed the same end state for a wildcard-bound\nsocket, by a path this one does not take.\n\nThe entry is counted whether or not anything hits it. hash4_cnt on the\nhash2 slot stays raised for as long as the socket lives, so udp_has_hash4()\nkeeps sending every packet for that address and port through the 4-tuple\nlookup first.\n\nOn IPv6 it can also be hit. __udp_disconnect() does not clear sk_v6_daddr,\nso udp_v6_rehash() files the entry under the peer the socket was connected\nto with a zero dport, and inet6_match() compares that same\nfield: a datagram from the former peer with a zero source port matches,\nand source port zero is accepted on receive. On IPv4 the peer is cleared,\nso a match would need a zero source address as well, which the routing\nlayer rejects as martian. The stale sk_v6_daddr is a separate defect, not\naddressed here; removing the entry closes this path either way.\n\nThe entry can also be relocated. __udp_disconnect() clears sk_bound_dev_if,\nso a subsequent SO_BINDTODEVICE calls ->rehash(), and because the receive\naddress is still specific udp_lib_rehash() moves the entry instead of\nremoving it, into the bucket that (rcv_saddr, num, 0, 0) hashes to -- a\npure function of the address and port, so every socket reaching this state\non one address and port collects in one bucket. The bucket cannot be chosen\nfrom outside, as udp_ehashfn() is seeded with a per-boot secret. This last\none became reachable only with commit 644f9108f3a5 (\"udp: Make rehash4\nindependent in udp_lib_rehash()\"), which moved the hash4 handling out of a\nbranch a disconnected socket does not take; the stale entry itself dates\nfrom the commit in Fixes.\n\nTake the socket out of the table before __udp_disconnect() runs, while it\nstill matches how it was filed. This also reaches the wildcard case ahead\nof udp_lib_rehash()'s udp_unhash4() branch, leaving that branch unreachable\nfrom udp_disconnect(); removing it belongs in net-next. udp_disconnect()\nand udp_abort() are the only UDP entries into __udp_disconnect(), which is\nshared with raw, ping and l2tp sockets that are not struct udp_sock:\nping_prot.obj_size is sizeof(struct inet_sock), so udp_hashed4() on one\nwould read past the allocation.","summary":"A UDP socket that is disconnected keeps its entry in the kernel's 4-tuple hash table, so every later packet for that address and port takes a slower lookup path. On IPv6 a datagram from the former peer with source port zero can match the stale entry.","severity":"unknown","cvss":null,"epss":null,"exploited":false,"exploited_at":null,"products":[],"ecosystems":[],"tags":["linux-kernel","package","self-hosted","udp"],"not_affected_if":["fixed-kernel"],"how_to_check":"- Check the running kernel with `uname -r` and compare it to the fixed kernel versions in the upstream commit referenced by the NVD entry.\n- unknown","fix":"- Update the Linux kernel to a release that includes the upstream fix (the commit titled 'udp: remove a disconnected socket from the 4-tuple hash table').\n- unknown","status":"open","published_at":"2026-10-18T16:17:12+00:00","first_seen_at":"2026-10-23T21:57:16+00:00","triaged_at":"2026-10-25T05:21:38+00:00","updated_at":"2026-10-25T05:21:38+00:00","category":"package","confidence":0.4,"triage":{"at":"2026-10-25T05:21:38Z","model":"claude-haiku-5-5","output":{"fix":"- Update the Linux kernel to a release that includes the upstream fix (the commit titled 'udp: remove a disconnected socket from the 4-tuple hash table').\n- unknown","reach":"unknown","title":"Linux kernel UDP: disconnected sockets stay in the 4-tuple hash table","impact":"low","summary":"A UDP socket that is disconnected keeps its entry in the kernel's 4-tuple hash table, so every later packet for that address and port takes a slower lookup path. On IPv6 a datagram from the former peer with source port zero can match the stale entry.","category":"package","nicknames":[],"who_cares":["linux-kernel","self-hosted","udp"],"confidence":0.4,"needs_auth":"unknown","worst_case":"unknown; the sources describe a lookup and hash-placement defect, not code execution","analyst_note":"Find the NVD entry's affected kernel version ranges and the fixed stable commits; the sources do not list them.","how_to_check":"- Check the running kernel with `uname -r` and compare it to the fixed kernel versions in the upstream commit referenced by the NVD entry.\n- unknown","needs_analyst":true,"not_affected_if":["fixed-kernel"],"who_is_affected":"Systems running a Linux kernel without this fix that use UDP sockets which are bound, connected and then disconnected, most likely on IPv6. The sources do not say which kernel versions are affected or which workloads are most exposed. Systems on fixed kernels can skip it.","user_interaction":"no","needs_special_setup":"unknown","severity_if_unknown":"low"},"cost_usd":0.000714,"input_tokens":4356,"output_tokens":557,"severity_before":"unknown"},"analyst_status":null,"analyst_note":null,"search":"'-2026':14A,17A '-98595':15A,18A '0':145C,411C,412C '0.0.0.0':144C '127.0.0.1':122C '127.0.0.2':127C '20001':128C '21001':123C '4':9A,40B,92C,111C,132C,192C,257C '644f9108f3a5':464C '6996a2d2d0a6':183C 'abort':550C 'accept':319C 'address':50B,103C,252C,336C,356C,390C,420C,431C 'af':138C 'ahead':523C 'alloc':590C 'also':265C,369C,518C 'anyth':224C 'auto':187C 'auto-bound':186C 'becam':459C 'belong':541C 'bind':120C 'bindaddr':170C 'bindport':178C 'bindtodevic':383C 'boot':454C 'bound':99C,188C,207C,376C 'branch':481C,531C,534C 'bucket':406C,437C,439C 'call':384C 'cannot':440C 'case':522C 'chosen':442C 'clear':272C,327C,374C 'close':361C 'cnt':228C 'collect':434C 'commit':182C,463C,495C 'compar':298C 'connect':125C,137C,189C,289C 'count':220C 'cve':13A,16A 'daddr':275C,350C 'datagram':61B,303C 'date':492C 'defect':354C 'dev':377C 'disconnect':4A,32B,88C,118C,136C,147C,197C,269C,373C,483C,507C,538C,547C,558C 'dport':294C 'effect':159C 'ehashfn':447C 'either':364C 'end':201C 'entri':35B,74B,108C,218C,282C,360C,367C,399C,490C,555C 'everi':45B,248C,424C 'field':301C 'file':129C,141C,280C,516C 'first':260C 'fix':198C,497C 'follow':80C 'former':64B,306C 'function':417C 'handl':477C 'hash':11A,42B,94C,113C,194C,413C 'hash2':231C 'hash4':227C,245C,476C 'hashed4':583C 'hit':225C,267C 'host':25B 'independ':468C 'inet':579C 'inet6':296C 'instead':400C 'ipv4':323C 'ipv6':59B,262C 'keep':33B,106C,246C 'kernel':2A,21B,38B,78C 'l2tp':566C 'last':457C 'later':46B 'layer':342C 'leav':532C 'lib':395C,471C,526C 'linux':1A,20B,77C 'linux-kernel':19B 'live':241C 'lock':171C,179C 'long':237C 'lookup':56B,259C 'make':466C 'martian':345C 'match':71B,297C,313C,330C,512C 'move':397C,474C 'need':332C 'net':544C 'net-next':543C 'next':545C 'num':410C 'one':213C,430C,436C,458C,585C 'outsid':444C 'packag':22B 'packet':47B,249C 'past':588C 'path':57B,211C,363C 'peer':65B,142C,285C,307C,325C 'per':453C 'per-boot':452C 'ping':564C 'ping_prot.obj':574C 'port':52B,68B,105C,254C,312C,316C,422C,433C 'pure':416C 'rais':234C 'raw':563C 'rcv':408C 'reach':426C,519C 'reachabl':460C 'read':587C 'receiv':321C,389C 'rehash':161C,167C,279C,385C,396C,472C,527C 'rehash4':467C 'reject':343C 'reloc':371C 'remov':86C,358C,402C,539C 'resolv':84C 'rout':341C 'run':508C 'saddr':409C 'secret':455C 'seed':449C 'self':24B 'self-host':23B 'send':247C 'separ':353C 'set':173C,181C 'share':561C 'side':158C 'size':575C 'sizeof':577C 'sk':119C,124C,135C,190C,273C,348C,375C 'skip':166C 'slot':232C 'slower':55B 'sock':169C,177C,573C,580C 'socket':5A,29B,89C,98C,150C,208C,240C,287C,425C,484C,500C,567C 'sourc':67B,311C,315C,335C 'specif':102C,393C 'stale':73B,347C,489C 'state':202C,428C 'stay':6A,233C 'still':140C,392C,511C 'struct':571C,578C 'subsequ':381C 'tabl':12A,43B,95C,114C,134C,154C,195C,504C 'take':53B,148C,216C,487C,498C 'tupl':10A,41B,93C,112C,133C,193C,258C 'udp':3A,26B,28B,85C,97C,146C,184C,243C,268C,277C,372C,394C,446C,465C,470C,506C,525C,529C,537C,546C,549C,554C,557C,572C,582C 'unhash':163C,175C,185C 'unhash4':530C 'unreach':535C 'unspec':139C 'v6':274C,278C,349C 'vulner':81C 'way':365C 'well':338C 'whether':221C 'wildcard':206C,521C 'wildcard-bound':205C 'would':331C,586C 'zero':69B,293C,310C,317C,334C","nicknames":[],"mitigations":[],"who_affected":"Systems running a Linux kernel without this fix that use UDP sockets which are bound, connected and then disconnected, most likely on IPv6. The sources do not say which kernel versions are affected or which workloads are most exposed. Systems on fixed kernels can skip it.","tier":"C","verified":{},"analyst_revisit_at":null,"first_public_at":"2026-10-18T16:17:12+00:00","kev":null,"cvss_vector":null,"cvss_by":null,"poc_count":0,"sources":[{"source":"nvd","external_id":"CVE-2026-98595","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-98595","severity":null,"published_at":"2026-10-18T16:17:12+00:00","modified_at":"2026-10-19T10:19:20+00:00","first_seen_at":"2026-10-23T21:57:16+00:00","fetched_at":"2026-10-23T22:28:56+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"}