{"id":"CVE-2026-98368","state":"PUBLISHED","title":"esp: downgrade zerocopy managed frags before mutating skb frags","description":"In the Linux kernel, the following vulnerability has been resolved:\n\nesp: downgrade zerocopy managed frags before mutating skb frags\n\nOn the out-of-place output path (esp->inplace == false) ESP rewrites the\nskb frag array: esp_output_head() appends a trailer frag and\nesp_output_tail() replaces the frags with a destination page, both\nreferenced with get_page().\n\nWhen the skb carries zerocopy managed frags (SKBFL_MANAGED_FRAG_REFS) the\npayload frags are owned by the ubuf and must not be referenced or\nunreferenced individually, but ESP mutates the frag array without ever\ndowngrading the skb.  This breaks the managed-frag invariant two ways:\n\n  - esp_ssg_unref() walks the source scatterlist and drops a page\n    reference for every frag, including the ubuf-owned payload frags,\n    pushing their refcount below the GUP pin bias while the pages are\n    still pinned, i.e. a use-after-free of the zerocopy pages;\n\n  - esp_output_tail() installs its destination page as frag 0 with\n    get_page() but leaves SKBFL_MANAGED_FRAG_REFS set, so\n    skb_release_data() takes the skip_unref branch and never drops that\n    reference, leaking the x->xfrag page at packet rate.\n\nFix this the way every other frag-mutating site does (__ip_append_data(),\n__ip6_append_data(), tcp_sendmsg_locked()) and call\nskb_zcopy_downgrade_managed() before ESP touches the frag array: it takes\na real reference on each existing frag and clears SKBFL_MANAGED_FRAG_REFS,\nso the per-frag unref in esp_ssg_unref() and the frag release in\nskb_release_data() are both balanced and no mixed-ownership frag array is\nleft behind.","publishedAt":"2026-10-06T08:46:53.292Z","updatedAt":"2026-10-19T09:18:20.253Z","severity":{"score":7.8,"label":"High","version":"3.1","vector":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H","source":"Linux"},"assigner":"Linux","products":["Linux Linux"],"affected":[{"vendor":"Linux","product":"Linux","defaultStatus":"unaffected","versions":[{"version":"753f1ca4e1e50248a1b760c9774d6d6b354562cc","status":"affected","lessThan":"2359264f377cdbdef2d95868cc8fb572949e48d3","versionType":"git"},{"version":"753f1ca4e1e50248a1b760c9774d6d6b354562cc","status":"affected","lessThan":"69a768c12398cada8528080332c822623fa7064d","versionType":"git"},{"version":"753f1ca4e1e50248a1b760c9774d6d6b354562cc","status":"affected","lessThan":"6508304ac2c8cdafca2f4ab915df8c707893e134","versionType":"git"},{"version":"753f1ca4e1e50248a1b760c9774d6d6b354562cc","status":"affected","lessThan":"6cab554f2c0f28773f712ed3a5479103f42ce844","versionType":"git"},{"version":"753f1ca4e1e50248a1b760c9774d6d6b354562cc","status":"affected","lessThan":"0d0845ee61c5df47cc68bc446501f48f71e8dcc6","versionType":"git"},{"version":"753f1ca4e1e50248a1b760c9774d6d6b354562cc","status":"affected","lessThan":"f89416eb3db151170a6f3c6dfc5239d26cdce4d2","versionType":"git"}],"repo":"https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git","programFiles":["net/ipv4/esp4.c","net/ipv6/esp6.c"]},{"vendor":"Linux","product":"Linux","defaultStatus":"affected","versions":[{"version":"6.0","status":"affected"},{"version":"0","status":"unaffected","lessThan":"6.0","versionType":"semver"},{"version":"6.1.189","status":"unaffected","lessThanOrEqual":"6.1.*","versionType":"semver"},{"version":"6.6.158","status":"unaffected","lessThanOrEqual":"6.6.*","versionType":"semver"},{"version":"6.12.112","status":"unaffected","lessThanOrEqual":"6.12.*","versionType":"semver"},{"version":"6.18.54","status":"unaffected","lessThanOrEqual":"6.18.*","versionType":"semver"},{"version":"7.2.8","status":"unaffected","lessThanOrEqual":"7.2.*","versionType":"semver"},{"version":"7.3","status":"unaffected","lessThanOrEqual":"*","versionType":"original_commit_for_fix"}],"repo":"https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git","programFiles":["net/ipv4/esp4.c","net/ipv6/esp6.c"]}],"references":["https://git.kernel.org/stable/c/2359264f377cdbdef2d95868cc8fb572949e48d3","https://git.kernel.org/stable/c/69a768c12398cada8528080332c822623fa7064d","https://git.kernel.org/stable/c/6508304ac2c8cdafca2f4ab915df8c707893e134","https://git.kernel.org/stable/c/6cab554f2c0f28773f712ed3a5479103f42ce844","https://git.kernel.org/stable/c/0d0845ee61c5df47cc68bc446501f48f71e8dcc6","https://git.kernel.org/stable/c/f89416eb3db151170a6f3c6dfc5239d26cdce4d2"],"weaknesses":[],"ingestedAt":"2026-10-29T20:13:43.249Z","source":{"repository":"CVEProject/cvelistV5","path":"cves/2026/98xxx/CVE-2026-98368.json","commit":"556c9d3a8cd17e218bb8e452ba7cd619a384ff47"},"raw":{"dataType":"CVE_RECORD","dataVersion":"5.2","cveMetadata":{"cveId":"CVE-2026-98368","state":"PUBLISHED","datePublished":"2026-10-06T08:46:53.292Z","dateUpdated":"2026-10-19T09:18:20.253Z","assignerShortName":"Linux","assignerOrgId":"416baaa9-dc9f-4396-8d5f-8c081fb06d67","dateReserved":"2026-09-25T10:25:14.344Z"},"containers":{"cna":{"title":"esp: downgrade zerocopy managed frags before mutating skb frags","descriptions":[{"lang":"en","value":"In the Linux kernel, the following vulnerability has been resolved:\n\nesp: downgrade zerocopy managed frags before mutating skb frags\n\nOn the out-of-place output path (esp->inplace == false) ESP rewrites the\nskb frag array: esp_output_head() appends a trailer frag and\nesp_output_tail() replaces the frags with a destination page, both\nreferenced with get_page().\n\nWhen the skb carries zerocopy managed frags (SKBFL_MANAGED_FRAG_REFS) the\npayload frags are owned by the ubuf and must not be referenced or\nunreferenced individually, but ESP mutates the frag array without ever\ndowngrading the skb.  This breaks the managed-frag invariant two ways:\n\n  - esp_ssg_unref() walks the source scatterlist and drops a page\n    reference for every frag, including the ubuf-owned payload frags,\n    pushing their refcount below the GUP pin bias while the pages are\n    still pinned, i.e. a use-after-free of the zerocopy pages;\n\n  - esp_output_tail() installs its destination page as frag 0 with\n    get_page() but leaves SKBFL_MANAGED_FRAG_REFS set, so\n    skb_release_data() takes the skip_unref branch and never drops that\n    reference, leaking the x->xfrag page at packet rate.\n\nFix this the way every other frag-mutating site does (__ip_append_data(),\n__ip6_append_data(), tcp_sendmsg_locked()) and call\nskb_zcopy_downgrade_managed() before ESP touches the frag array: it takes\na real reference on each existing frag and clears SKBFL_MANAGED_FRAG_REFS,\nso the per-frag unref in esp_ssg_unref() and the frag release in\nskb_release_data() are both balanced and no mixed-ownership frag array is\nleft behind."}],"affected":[{"vendor":"Linux","product":"Linux","defaultStatus":"unaffected","versions":[{"version":"753f1ca4e1e50248a1b760c9774d6d6b354562cc","status":"affected","lessThan":"2359264f377cdbdef2d95868cc8fb572949e48d3","versionType":"git"},{"version":"753f1ca4e1e50248a1b760c9774d6d6b354562cc","status":"affected","lessThan":"69a768c12398cada8528080332c822623fa7064d","versionType":"git"},{"version":"753f1ca4e1e50248a1b760c9774d6d6b354562cc","status":"affected","lessThan":"6508304ac2c8cdafca2f4ab915df8c707893e134","versionType":"git"},{"version":"753f1ca4e1e50248a1b760c9774d6d6b354562cc","status":"affected","lessThan":"6cab554f2c0f28773f712ed3a5479103f42ce844","versionType":"git"},{"version":"753f1ca4e1e50248a1b760c9774d6d6b354562cc","status":"affected","lessThan":"0d0845ee61c5df47cc68bc446501f48f71e8dcc6","versionType":"git"},{"version":"753f1ca4e1e50248a1b760c9774d6d6b354562cc","status":"affected","lessThan":"f89416eb3db151170a6f3c6dfc5239d26cdce4d2","versionType":"git"}],"repo":"https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git","programFiles":["net/ipv4/esp4.c","net/ipv6/esp6.c"]},{"vendor":"Linux","product":"Linux","defaultStatus":"affected","versions":[{"version":"6.0","status":"affected"},{"version":"0","status":"unaffected","lessThan":"6.0","versionType":"semver"},{"version":"6.1.189","status":"unaffected","lessThanOrEqual":"6.1.*","versionType":"semver"},{"version":"6.6.158","status":"unaffected","lessThanOrEqual":"6.6.*","versionType":"semver"},{"version":"6.12.112","status":"unaffected","lessThanOrEqual":"6.12.*","versionType":"semver"},{"version":"6.18.54","status":"unaffected","lessThanOrEqual":"6.18.*","versionType":"semver"},{"version":"7.2.8","status":"unaffected","lessThanOrEqual":"7.2.*","versionType":"semver"},{"version":"7.3","status":"unaffected","lessThanOrEqual":"*","versionType":"original_commit_for_fix"}],"repo":"https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git","programFiles":["net/ipv4/esp4.c","net/ipv6/esp6.c"]}],"references":[{"url":"https://git.kernel.org/stable/c/2359264f377cdbdef2d95868cc8fb572949e48d3"},{"url":"https://git.kernel.org/stable/c/69a768c12398cada8528080332c822623fa7064d"},{"url":"https://git.kernel.org/stable/c/6508304ac2c8cdafca2f4ab915df8c707893e134"},{"url":"https://git.kernel.org/stable/c/6cab554f2c0f28773f712ed3a5479103f42ce844"},{"url":"https://git.kernel.org/stable/c/0d0845ee61c5df47cc68bc446501f48f71e8dcc6"},{"url":"https://git.kernel.org/stable/c/f89416eb3db151170a6f3c6dfc5239d26cdce4d2"}],"metrics":[{"cvssV3_1":{"version":"3.1","vectorString":"CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H","baseScore":7.8,"baseSeverity":"HIGH"},"scenarios":[{"lang":"en","value":"AV:L - Managed frags are set only by io_uring's io_sg_from_iter() for IORING_OP_SEND_ZC/SENDMSG_ZC with IORING_RECVSEND_FIXED_BUF, so a local process must submit the zerocopy send that esp_output_head()/esp6_output_head() then mutates; a remote peer cannot produce a SKBFL_MANAGED_FRAG_REFS skb.\nAC:L - The attacker picks every input: an uncloned UDP skb whose data_len+tailen fits in a page and lacks tailroom reliably takes the esp->inplace=false branch, then esp_ssg_unref() drops refs on the ubuf-owned frags. No race or outside state is needed.\nPR:L - io_uring zerocopy send needs no privilege. The ESP SA/policy can be added inside an unprivileged user+net namespace because xfrm_user_rcv_msg only checks netlink_net_capable(CAP_NET_ADMIN); on a host with IPsec already set up, any local user's traffic hits it.\nUI:N - The attacker does everything with their own io_uring ring, registered buffer, socket and (namespaced) xfrm state; no other user has to act.\nS:U - The page refcount underflow and the freed page stay inside the kernel's own authority, so this is a normal local privilege escalation with no VM or sandbox boundary crossed.\nC:H - esp_ssg_unref() pushes the pinned registered-buffer pages' refcount below the GUP pin bias, so the page is freed while io_uring still has it mapped; once reallocated (e.g. as page tables or slab) the attacker can read kernel memory through the stale buffer.\nI:H - Writing through the still-registered fixed buffer into the freed and reallocated page is a page-level use-after-free write primitive (Dirty-Pagetable style), enough for arbitrary kernel memory corruption and privilege escalation.\nA:H - The page UAF leads to bad-page/refcount oopses, and the missing put_page on the x->xfrag page installed by esp_output_tail() leaks memory at packet rate; both can take the system down."}]}],"providerMetadata":{"shortName":"Linux","orgId":"416baaa9-dc9f-4396-8d5f-8c081fb06d67","dateUpdated":"2026-10-19T09:18:20.253Z"},"cpeApplicability":[{"nodes":[{"operator":"OR","negate":false,"cpeMatch":[{"vulnerable":true,"criteria":"cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*","versionStartIncluding":"6.0","versionEndExcluding":"6.1.189"},{"vulnerable":true,"criteria":"cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*","versionStartIncluding":"6.0","versionEndExcluding":"6.6.158"},{"vulnerable":true,"criteria":"cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*","versionStartIncluding":"6.0","versionEndExcluding":"6.12.112"},{"vulnerable":true,"criteria":"cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*","versionStartIncluding":"6.0","versionEndExcluding":"6.18.54"},{"vulnerable":true,"criteria":"cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*","versionStartIncluding":"6.0","versionEndExcluding":"7.2.8"},{"vulnerable":true,"criteria":"cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*","versionStartIncluding":"6.0","versionEndExcluding":"7.3"}]}]}],"x_generator":{"engine":"bippy-1.2.0"}}}}}