Cabecera Penligente

CVE-2026-46331, pedit COW and Linux Root via Page Cache

CVE-2026-46331 is a Linux kernel local privilege escalation flaw in the traffic-control subsystem, specifically the act_pedit packet-editing action under net/sched. It is not a remote unauthenticated bug that lets an attacker scan the internet and instantly own any Linux server. The practical risk is narrower but still serious: when an attacker already has a low-privileged local foothold, CVE-2026-46331 can become the step that turns that foothold into root on an affected host.

That distinction matters. “Local” is not the same as “low priority.” CI runners execute untrusted pull requests. Kubernetes workers run containers built by different teams. Shared hosting systems run code from many customers. Multi-user research servers, student labs, bug bounty ranges, build workers, developer jump boxes, and AI agent sandboxes all treat local code execution as part of normal operations. On those systems, a Linux kernel local privilege escalation bug is not a theoretical second-stage issue. It is the boundary between a contained low-privileged workload and full host control.

The official NVD description says the bug is caused by tcf_pedit_act() computing the copy-on-write range for skb_ensure_writable() once before the key loop using tcfp_off_max_hint. That hint did not account for the runtime header offset added by typed keys, leaving part of the eventual write region outside the area that had been made private. The fix moves skb_ensure_writable() into the per-key loop where the real write offset is known, adds overflow checks, uses skb_cow() for negative offsets such as Ethernet header edits at ingress, and guards against the undefined negation of INT_MIN. NVD had published the record but had not yet assigned its own CVSS enrichment when checked. (nvd.nist.gov)

Red Hat describes the same issue as an Important privilege escalation flaw in the Linux kernel traffic-control subsystem, where a missing bounds check allows an out-of-bounds write and page cache memory corruption. Red Hat’s bulletin states that a user with a local account could exploit the flaw to gain root privileges, and it lists RHEL 8, RHEL 9, RHEL 10, Red Hat Enterprise Linux for NVIDIA, and OpenShift-related environments as affected or potentially impacted through the underlying kernel. (Portal del cliente de Red Hat)

The facts that matter first

ZonaPractical answer
CVECVE-2026-46331
Common name used by researcherspedit COW
Componente afectadoLinux kernel net/sched traffic-control act_pedit acción
Main bug classPartial copy-on-write failure leading to page cache corruption
Vector de ataqueLocal code execution, not direct remote exploitation
Main impactLow-privileged local user may escalate to root on vulnerable systems
Public exploit statusPublic proof-of-concept code exists, with verified targets listed by the author
Highest-priority environmentsCI runners, Kubernetes workers, shared hosts, multi-user servers, build systems, developer jump boxes, lab machines, container platforms
Primary fixInstall a vendor-fixed kernel and reboot into it
Temporary mitigationsBlock act_pedit if unused, or restrict unprivileged user namespaces where compatible
Detection challengePage-cache-only corruption may not change the file on disk, so disk hash checks can miss evidence

The public proof-of-concept repository identifies the issue as net/sched act_pedit partial-COW page-cache corruption, says the vulnerable upstream range is present from Linux 5.18 through a fix in 7.1-rc7, and lists verified results for RHEL 10.0, Debian 13 trixie, Ubuntu 24.04.4, and Ubuntu 26.04, with Ubuntu behavior depending on AppArmor user namespace hardening. The same repository provides exploit code; defenders should treat that fact as evidence of exploitability, not as an invitation to run the exploit on production hosts. (GitHub)

The safer operational conclusion is straightforward: if a Linux host allows untrusted or semi-trusted local code execution, patch it quickly, verify the running kernel after reboot, and consider interim mitigation only when you understand the workload impact.

What Linux traffic control and pedit actually do

Linux traffic control, usually managed with the tc utility, is not a niche exploit feature. It is a normal kernel facility for controlling packet behavior. The tc manual describes traffic control as covering shaping, scheduling, policing, and dropping, with packet processing controlled through qdiscs, classes, and filters. It also notes that qdiscs can invoke actions when traffic-control events occur. (man7.org)

Red Hat’s networking documentation describes Linux Traffic Control as helping with policing, classifying, shaping, and scheduling network traffic, using queuing disciplines and filters to manage and manipulate packet transmission, including packet mangling. In real deployments, this can support QoS, packet rewriting, lab simulations, CNI behavior, traffic shaping, and custom network policy logic. (Red Hat Documentation)

En pedit action is the generic packet editor. The tc-pedit manual says it can change arbitrary packet data, either by specifying an offset and size or by naming a header field so the size can be chosen automatically. The same manual lists supported fields across Ethernet, IPv4, IPv6, TCP, and UDP headers, and it explains that raw operations can use signed offsets and variable offset calculations. (man7.org)

That flexibility is the point of pedit. It is also why the code path is subtle. A packet editor must reason about where packet bytes live, which header offsets apply, whether an skb is linear or nonlinear, whether data is in headroom or fragments, and whether a write is safe. In kernel networking, a few bytes of packet metadata can determine whether an operation modifies a private buffer or a shared page.

CVE-2026-46331 lives in that boundary. It is not about tc being dangerous by design. It is about the kernel making an incorrect assumption about the range of packet bytes it had made writable before performing edits whose final offsets were only known later.

The root cause, partial COW in tcf_pedit_act()

How pedit COW Breaks the Writable Range Assumption

Copy-on-write exists to protect shared data. Before a kernel path modifies data that might be shared, it should ensure the target bytes are privately owned by the writer. If the writer owns the destination, in-place mutation is safe. If the destination points into shared or externally backed memory, the kernel must copy first, then write into the private copy.

In CVE-2026-46331, the failure is not that the kernel forgot COW entirely. The failure is that it computed the COW range too early and too coarsely. The vulnerable path calculated a writable range once before iterating over edit keys. That calculation used tcfp_off_max_hint, but typed keys can add a runtime header offset during the actual edit. The real write location can therefore be beyond the range that was made writable.

That is the “partial COW” in pedit COW. The kernel believed it had prepared the target packet memory for modification. In reality, part of the write could land outside the protected range. NVD’s fix description is unusually helpful: move skb_ensure_writable() into the per-key loop, where the actual write offset is known, add overflow checking, use skb_cow() for negative offsets such as Ethernet header edits at ingress, and harden offset_valid() against INT_MIN. (nvd.nist.gov)

TuxCare’s technical analysis describes the same bug as a partial COW condition where act_pedit computed the copy-on-write range once before editing, missed the runtime offset added by typed keys, and wrote into a page it never made private. TuxCare also notes that the upstream fix moves the writable-range handling into the per-key path, adds offset guards, handles negative offsets through skb_cow(), and linearizes skbs carrying shared fragments so a zero-copy page cannot be silently corrupted. (TuxCare) (TuxCare)

A simplified safe pseudocode model looks like this:

/*
 * This is illustrative pseudocode, not Linux kernel source.
 * It shows the bug class: making an estimated range writable,
 * then writing later at a runtime-resolved offset.
 */

estimated_max = pedit_offset_hint(keys);
skb_ensure_writable(skb, estimated_max);

for each key in keys {
    actual_offset = base_offset(key) + runtime_header_offset(skb, key);

    /*
     * In the vulnerable pattern, actual_offset can point outside
     * the range that was made private before the loop.
     */
    write_packet_bytes(skb, actual_offset, key->value);
}

/*
 * Safer pattern:
 * decide the actual offset inside the loop, then make that exact
 * range writable before the write.
 */

for each key in keys {
    actual_offset = base_offset(key) + runtime_header_offset(skb, key);

    if (offset_math_overflows(actual_offset, key->size))
        reject();

    skb_ensure_writable(skb, actual_offset + key->size);
    write_packet_bytes(skb, actual_offset, key->value);
}

The exploitability comes from what that incorrect write can touch. In modern Linux networking, packet data may be represented through socket buffers and associated fragments. Those fragments can point to pages that are not privately owned by the skb path. When a kernel fast path modifies a page it does not exclusively own, the page cache can become the victim.

Why page-cache corruption is different from ordinary memory corruption

The page cache is the kernel’s in-memory cache of file contents. If a process reads /bin/su, /usr/bin/sudo, a shared library, a script, or another file, Linux may serve those bytes from memory rather than reading them from disk each time. That is normal. It is a major reason Linux file I/O performs well.

A page-cache write primitive becomes dangerous when an attacker can alter the cached bytes of a file that they cannot modify on disk. The disk file remains clean. A file integrity tool that hashes the on-disk bytes may still report the expected value. But another process that reads or executes the cached file can observe the poisoned memory copy.

This is why page-cache bugs are attractive for local privilege escalation. The attacker does not need to change file permissions, replace a root-owned binary on disk, or persist a modified executable. The attacker only needs to corrupt the cached representation long enough for privileged code to consume it.

TuxCare states that in pedit COW the on-disk binary never changes, the corruption lives only in the page cache, and file hashing can return clean while a root shell is already open. It also identifies behavioral hunting signals such as unexpected act_pedit module loads, unusual tc activity, unprivileged namespace creation before setuid-binary execution, and shell execution following traffic-control activity without a legitimate reason. (TuxCare)

That is also why incident response cannot stop at “the package hash matches.” A clean disk file is good news, but it is not proof that no page-cache-only corruption happened during the exposure window. Rebooting or dropping page cache can evict poisoned cached pages, but neither action removes persistence an attacker may have created after obtaining root.

Why “local” still matters in real attack chains

CVE-2026-46331 requires local execution or a local account-style foothold. That is a meaningful constraint. A random unauthenticated internet user cannot exploit it merely by sending a packet to an exposed service.

But modern intrusion paths often produce local code execution before they produce full host control. A vulnerable web application may give an attacker a shell as www-data. A compromised CI job may run untrusted build steps. A malicious package may execute during install or test. A container breakout chain may start with code running as an unprivileged user in a container. A stolen SSH credential may give access to a low-privileged account. A browser exploit may land inside a sandbox. A student or contractor account may be intentionally low privilege but still able to execute code on a shared machine.

In all of those cases, a kernel local privilege escalation is the second stage. It changes the impact from “the attacker controls one account or one workload” to “the attacker may control the host.”

The highest-risk systems are therefore not every Linux machine in the same order. They are systems where local execution by untrusted or semi-trusted users is expected:

Medio ambienteWhy CVE-2026-46331 deserves priority
CI/CD runnersBuild jobs often execute untrusted pull requests, package scripts, test harnesses, and generated code
Kubernetes workersContainers share the host kernel, so a kernel LPE can threaten the node
Shared hosting nodesMany low-privileged users run code on the same host
Multi-user research or lab serversLocal shell access is normal, but users are not equally trusted
Student cyber rangesRunning exploit code is part of the environment, so local boundaries matter
Developer jump boxesCredentials, cloud tokens, and internal network reach can make root highly valuable
Build serversRoot compromise can poison artifacts, secrets, and release pipelines
AI agent sandboxesAgent-executed code may be local but should not become host root

Red Hat’s mitigation guidance explicitly calls out local-access reduction measures alongside kernel patching, including limiting SSH access, keeping SELinux in enforcing mode, running OpenShift workloads as non-root, using default Security Context Constraints, and restricting oc debug access to trusted cluster administrators. Those controls do not fix the kernel bug, but they reduce the paths by which an attacker reaches exploitable local execution. (Portal del cliente de Red Hat)

Public PoC status and safe validation boundaries

The public PoC for CVE-2026-46331 is important because it compresses uncertainty. The repository’s README says the exploit turns the act_pedit partial-COW primitive into unprivileged local root and lists verified target results for several Linux distributions and kernel lines. It also describes Ubuntu-specific AppArmor user namespace behavior, including a failure result on Ubuntu 26.04 that reflects hardening behavior rather than necessarily proving the underlying kernel path is fixed. (GitHub)

A defender does not need to run that PoC on production to make a good decision. Running a public root exploit on a production server can contaminate page cache, trigger instability, destroy evidence, violate policy, and create a bigger incident than the one being investigated. Safe validation should focus on exposure conditions and remediation state.

A practical exposure check asks six questions:

  1. What kernel is running right now?
  2. Has the distribution shipped a fixed kernel for that exact release stream?
  3. Has the host rebooted into the fixed kernel, or is it still running the old one?
  4. Es act_pedit present, loaded, or loadable?
  5. Are unprivileged user namespaces enabled?
  6. Can untrusted or semi-trusted local users run code on the host?

Start with non-destructive inventory:

#!/usr/bin/env bash
set -euo pipefail

echo "== OS release =="
cat /etc/os-release 2>/dev/null || true

echo
echo "== Running kernel =="
uname -a

echo
echo "== Kernel package hints =="
if command -v rpm >/dev/null 2>&1; then
  rpm -qa 'kernel*' | sort
fi

if command -v dpkg >/dev/null 2>&1; then
  dpkg -l 'linux-image*' 2>/dev/null | awk '/^ii/ {print $2, $3}'
fi

echo
echo "== act_pedit module state =="
lsmod | grep -w act_pedit || echo "act_pedit not currently loaded"

echo
echo "== act_pedit module availability =="
modinfo act_pedit 2>/dev/null | head -n 5 || echo "act_pedit module not found by modinfo"

echo
echo "== Existing tc pedit usage =="
if command -v tc >/dev/null 2>&1; then
  tc actions list action pedit 2>/dev/null || true
else
  echo "tc command not found"
fi

echo
echo "== User namespace controls =="
sysctl user.max_user_namespaces 2>/dev/null || true
sysctl kernel.unprivileged_userns_clone 2>/dev/null || true
sysctl kernel.apparmor_restrict_unprivileged_userns 2>/dev/null || true
sysctl kernel.apparmor_restrict_unprivileged_unconfined 2>/dev/null || true

The important phrase is “running kernel.” Installing a fixed package is not enough if the system has not rebooted into it. Kernel vulnerabilities often survive a patch window because a package manager updated files on disk while production continued to run the old kernel in memory.

Vendor status and severity scoring are not perfectly aligned

One reason CVE-2026-46331 is easy to misprioritize is that public scoring and release status differ across sources. NVD had published the CVE record and the kernel.org description but had not yet provided its own CVSS 3.x or 4.0 assessment when checked. (nvd.nist.gov)

Red Hat rates the issue Important, identifies it as a traffic-control privilege escalation flaw, and lists RHEL 8, RHEL 9, RHEL 10, RHEL for NVIDIA, and OpenShift Container Platform as affected or relevant through the underlying RHEL kernel. Red Hat also provides an interim mitigation that blocks act_pedit from loading, while warning that the mitigation may not fit systems that actually use tc pedit rules. (Portal del cliente de Red Hat)

Ubuntu marks CVE-2026-46331 as High priority. Its status page, last updated on June 27, 2026 when fetched, showed multiple Ubuntu release streams as vulnerable, including 26.04 LTS, 25.10, 24.04 LTS, 22.04 LTS, 20.04 LTS, and 18.04 LTS for the main linux package stream, while older or unsupported package streams had different statuses. That page must be checked directly because Ubuntu package streams and HWE variants differ by release. (Ubuntu)

Debian’s security tracker lists CVE-2026-46331 against the linux source package and shows release-specific status. When fetched, bullseye, bookworm, trixie, and forky entries had vulnerable statuses in some streams, while trixie security and sid showed fixed versions. That illustrates why “Debian is affected” is too coarse; the exact release and package version matter. (security-tracker.debian.org)

SUSE rates the issue Important and shows a CVSS 3.1 score of 7.8 with local attack vector, low privileges required, no user interaction, changed scope, and high confidentiality, integrity, and availability impact. Amazon Linux, by contrast, shows Medium severity with a CVSS 3.1 base score of 6.0 and pending fixes for several Amazon Linux kernel package tracks when fetched. (suse.com) (explore.alas.aws.amazon.com)

FuenteStatus or scoring signalWhat defenders should do with it
NVDCVE published, NVD CVSS not yet provided when checkedUse the root-cause description, but do not wait for NVD enrichment before checking exposure
Red HatImportant impact, RHEL 8/9/10 and related products affectedFollow Red Hat errata and mitigation guidance for RHEL-derived systems
UbuntuHigh priority, multiple releases listed vulnerable when checkedTrack the exact Ubuntu release and kernel package stream
DebianRelease-specific vulnerable and fixed statusesCompare the installed linux package to the tracker’s fixed versions
SUSEImportant, CVSS 7.8Treat local root on exposed multi-user systems as urgent
Amazon LinuxMedium, CVSS 6.0, several package streams pending fix when checkedFollow ALAS status and do not assume Red Hat scoring maps exactly

The correct takeaway is not that one vendor is “right” and another is “wrong.” CVSS scoring depends on assumptions about privileges, complexity, scope, and environment. For this class of bug, operational exposure matters more than a single number. A single-user workstation with no untrusted local execution has a different risk profile from a CI runner that executes arbitrary pull requests all day. The same kernel flaw can be a routine patch on one host and an urgent emergency on another.

Exposure checks that avoid false confidence

The easiest mistake is to check only one condition and declare victory. CVE-2026-46331 exposure depends on a combination of kernel version, vendor patch status, module reachability, namespace policy, and local execution risk.

lsmod | grep act_pedit is useful, but it is not enough. If the module is not loaded now, it may still be loadable on demand when a tc action references it. TuxCare explicitly warns that act_pedit can autoload when a pedit action is configured and recommends checking both current module state and module availability. (TuxCare)

Similarly, a PoC failing on one Ubuntu release does not prove the kernel is fixed. The public PoC README describes Ubuntu user namespace restrictions and says Ubuntu 26.04 failed against the PoC because of AppArmor-related user namespace behavior, while Ubuntu 24.04.4 worked with an Ubuntu-specific path. That is a hardening result, not a substitute for a fixed kernel. (GitHub)

A safer triage flow looks like this:

# 1. Confirm what is actually running.
uname -r
uname -v

# 2. Confirm distribution release and package manager context.
cat /etc/os-release

# 3. Check whether act_pedit is currently loaded.
lsmod | grep -w act_pedit || echo "not loaded"

# 4. Check whether act_pedit can be found as a module.
modinfo act_pedit 2>/dev/null | head -n 3 || echo "not available via modinfo"

# 5. Check whether pedit actions exist today.
tc actions list action pedit 2>/dev/null || true

# 6. Check user namespace controls.
sysctl user.max_user_namespaces 2>/dev/null || true
sysctl kernel.unprivileged_userns_clone 2>/dev/null || true

# 7. Check Ubuntu AppArmor namespace gates where present.
sysctl kernel.apparmor_restrict_unprivileged_userns 2>/dev/null || true
sysctl kernel.apparmor_restrict_unprivileged_unconfined 2>/dev/null || true

Interpret the output conservatively. A host with act_pedit not loaded, user namespaces disabled, and no untrusted local users is lower priority than a shared build node with user namespaces enabled and act_pedit loadable. But lower priority is not fixed. The durable remediation remains a fixed kernel.

Mitigation paths and their trade-offs

The preferred remediation is to install a vendor-fixed kernel and reboot into it. That sounds obvious, but two details matter. First, use the distribution’s own advisory or package stream, not a raw upstream version guess. Enterprise kernels are heavily backported. Second, verify the running kernel after reboot. A patched package on disk does not help if the old kernel is still running.

Red Hat’s bulletin recommends blocking the affected act_pedit module as an interim mitigation and gives a blacklist example, while warning that the mitigation may not be suitable for systems that use tc pedit rules for traffic shaping or packet header rewriting. (Portal del cliente de Red Hat)

CloudLinux and TuxCare recommend a stronger install override when act_pedit is not needed, because a simple blacklist can be weaker against on-demand loading behavior. They also recommend checking whether act_pedit is loaded before unloading it and warn that systems using traffic-control pedit rules may break if the module is blocked. (blog.cloudlinux.com) (TuxCare)

A cautious module-blocking approach looks like this:

# Check whether pedit is in use before blocking it.
tc actions list action pedit 2>/dev/null || true
lsmod | grep -w act_pedit || true

# Stronger prevention of module loading through install override.
echo 'install act_pedit /bin/true' | sudo tee /etc/modprobe.d/disable-act_pedit.conf

# Unload only if it is loaded and you have confirmed no legitimate dependency.
if lsmod | grep -q -w act_pedit; then
  sudo rmmod act_pedit
fi

# Confirm current state.
lsmod | grep -w act_pedit || echo "act_pedit not loaded"

To revert after a fixed kernel is deployed:

sudo rm -f /etc/modprobe.d/disable-act_pedit.conf
sudo depmod -a

The second mitigation is restricting unprivileged user namespaces. The public exploit path relies on namespace-local capability, especially CAP_NET_ADMIN inside a user and network namespace. TuxCare notes that closing unprivileged user namespaces breaks the public chain regardless of module state, but it can disrupt rootless containers, CI sandboxes, unprivileged unshare, sandboxed browsers, and Flatpak. (TuxCare)

Example controls:

# EL-family systems often use this control.
sudo sysctl -w user.max_user_namespaces=0
echo 'user.max_user_namespaces = 0' | sudo tee /etc/sysctl.d/99-pedit-cow.conf

# Debian and Ubuntu commonly expose this control.
sudo sysctl -w kernel.unprivileged_userns_clone=0
echo 'kernel.unprivileged_userns_clone = 0' | sudo tee /etc/sysctl.d/99-pedit-cow.conf

Do not roll these controls out blindly across developer fleets. A hardened hosting node may tolerate user namespaces being disabled. A developer workstation, CI sandbox, rootless container environment, or browser-heavy desktop may not. The right mitigation depends on the workload.

A useful decision tree is:

CondiciónPreferred action
Vendor-fixed kernel available and reboot window possiblePatch and reboot
Livepatch available from a trusted vendor and verified activeApply livepatch, then schedule normal reboot later if policy requires
No tc pedit dependencyBlock act_pedit as a temporary mitigation
tc pedit is needed but user namespaces are notRestrict unprivileged user namespaces temporarily
Both act_pedit and user namespaces are requiredIsolate untrusted local execution until patching is complete
Host may already have been targetedPatch, reboot, investigate root-level compromise, and do not rely only on hash checks

Detection and hunting, what you can and cannot see

There is no clean, universal post-compromise signature for CVE-2026-46331. Page-cache-only corruption may leave the disk file unchanged. The evidence can disappear after reboot or cache eviction. A successful attacker may also establish persistence after reaching root, so clearing the page cache is not remediation.

TuxCare lists useful behavioral signals: unexpected act_pedit module loads on hosts that do not normally use traffic-control packet editing, unusual tc activity by users or processes that do not configure networking, creation of unprivileged user namespaces shortly before setuid-binary execution, and su, sudo, or shell execution following traffic-control activity without a legitimate reason. (TuxCare)

You can collect some of those signals with auditd:

# Watch execution of common tc paths.
sudo auditctl -w /sbin/tc -p x -k tc_exec
sudo auditctl -w /usr/sbin/tc -p x -k tc_exec

# Watch module management tooling.
sudo auditctl -w /sbin/modprobe -p x -k module_mgmt
sudo auditctl -w /usr/sbin/modprobe -p x -k module_mgmt

# Watch high-value setuid tools for execution context review.
sudo auditctl -w /bin/su -p x -k su_exec
sudo auditctl -w /usr/bin/sudo -p x -k sudo_exec

# Review events.
sudo ausearch -k tc_exec
sudo ausearch -k module_mgmt
sudo ausearch -k su_exec
sudo ausearch -k sudo_exec

On systemd hosts, you can also look for module loads and unusual command execution:

journalctl -k --since "2026-06-16" | grep -iE 'act_pedit|pedit|module'
journalctl --since "2026-06-16" | grep -iE 'tc |act_pedit|unshare|newuidmap|newgidmap'

These commands do not prove exploitation. They help identify machines that deserve deeper review. A normal network appliance or QoS host may legitimately use tc. A CI runner probably should not suddenly load act_pedit and execute su in a strange sequence. Baseline matters.

If you suspect a host was exploited, do not treat drop_caches as a fix. CloudLinux recommends dropping page cache after mitigation if a host may have been targeted, because the public exploit class can modify legitimate system binaries in page cache. But once an attacker has reached root, the host needs compromise review: persistence, credentials, keys, containers, build artifacts, cron, systemd units, SSH authorized keys, cloud metadata access, runner secrets, and lateral movement paths. (blog.cloudlinux.com)

A safe containment sequence is:

# 1. Remove the immediate trigger path where compatible.
echo 'install act_pedit /bin/true' | sudo tee /etc/modprobe.d/disable-act_pedit.conf

# 2. Optional and workload-dependent: restrict user namespaces.
sudo sysctl -w user.max_user_namespaces=0 2>/dev/null || true
sudo sysctl -w kernel.unprivileged_userns_clone=0 2>/dev/null || true

# 3. Evict clean page cache after mitigation if exposure is suspected.
# This is containment, not proof of cleanup.
sudo sh -c 'echo 3 > /proc/sys/vm/drop_caches'

# 4. Patch and reboot into a fixed kernel.
# Use your distribution's normal package process, then verify with uname -r.

Dirty COW, Dirty Pipe, Copy Fail, Dirty Frag, and pedit COW

CVE-2026-46331 is easier to understand when placed in the larger family of Linux bugs where copy-on-write, page-cache state, zero-copy data movement, or page ownership assumptions fail.

VulnerabilidadCVECore primitiveSubsystemWhy it is relevant
Dirty COWCVE-2016-5195Incorrect COW handling allows writes to read-only mappingsMemory managementFamous Linux local privilege escalation based on COW breakage
Tubería suciaCVE-2022-0847Uninitialized pipe buffer flags allow writes to page-cache-backed read-only filesPipe buffers and page cacheClear predecessor for page-cache write to local root
Copy FailCVE-2026-31431In-place crypto operation can corrupt readable file cacheAF_ALG, AEAD, splice, page cacheRecent page-cache local root pattern in the crypto path
FragnesiaCVE-2026-46300Shared-frag marker preservation failure can expose page-cache-backed fragsNetworking, XFRM ESP-in-TCP, skbuffSimilar networking/page-cache family
pedit COWCVE-2026-46331Typed-key offset causes partial COW and page-cache corruptionTraffic control act_peditSame page ownership failure class, different entry point

NVD describes Dirty COW, CVE-2016-5195, as a race condition in Linux kernel memory handling that allowed local users to gain privileges by leveraging incorrect handling of copy-on-write to write to a read-only memory mapping. (nvd.nist.gov)

NVD describes Dirty Pipe, CVE-2022-0847, as a flaw where the flags member of a pipe buffer structure lacked proper initialization, allowing an unprivileged local user to write to pages in the page cache backed by read-only files and escalate privileges. (nvd.nist.gov)

NVD’s record for Copy Fail, CVE-2026-31431, centers on reverting Linux algif_aead back to out-of-place operation because the source and destination came from different mappings. Microsoft’s analysis describes Copy Fail as a Linux kernel crypto-subsystem bug that can corrupt the cache of readable files, including setuid binaries, and result in root privilege escalation from a low-privileged local user. (nvd.nist.gov)

NVD’s CVE-2026-46300 record describes a Linux networking fix to preserve the shared-frag marker during skbuff coalescing, where losing the marker could break invariants relied on by later in-place writers. Tenable’s public summary ties CVE-2026-46300, also called Fragnesia, to Linux kernel local privilege escalation in the XFRM ESP-in-TCP subsystem and public PoC availability. (nvd.nist.gov)

The shared lesson is not “Linux networking is broken” or “zero-copy is unsafe.” The lesson is more precise: when high-performance kernel paths pass around pages, fragments, and shared buffers, every writer must preserve ownership metadata and prove that the destination is private before modifying it. If that invariant fails, a local memory bug can become a page-cache write, and a page-cache write can become root.

Penligent has previously analyzed Dirty Frag as part of the same Linux page-cache local-root bug family, focusing on how skbuff fragments, shared ownership, and in-place operations can turn a low-privileged local foothold into host-level risk. That context is useful for teams building a broader response program rather than treating CVE-2026-46331 as a one-off patch ticket. (Penligente)

Containers, Kubernetes, and CI runners

Containers change the exposure model, not the kernel reality. A container has its own filesystem view, process namespace, and security profile, but it still relies on the host kernel. If a containerized process can reach a vulnerable kernel path, the risk belongs to the node, not just the container image.

Kubernetes worker nodes deserve special attention for three reasons. First, workloads from different trust zones may share the same kernel. Second, cluster operators sometimes allow debug containers, privileged pods, host networking, or broad capabilities for troubleshooting. Third, node compromise can expose service account tokens, image pull secrets, mounted volumes, workload credentials, and lateral movement paths.

For Kubernetes and OpenShift environments, the priority order should be practical:

  1. Patch and reboot worker nodes into fixed kernels.
  2. Drain nodes before rebooting to avoid unnecessary workload disruption.
  3. Restrict privileged pods and host namespace access.
  4. Keep SELinux, AppArmor, seccomp, and Pod Security Admission policies enforced.
  5. Limit debug access to trusted administrators.
  6. Review nodes that ran untrusted workloads during the exposure window.
  7. Rotate secrets that may have been accessible to compromised workloads.

Red Hat’s OpenShift-related guidance emphasizes default Security Context Constraints, non-root workloads, SELinux enforcing mode, and restricted oc debug access as risk-reduction measures while kernel fixes are applied. (Portal del cliente de Red Hat)

CI/CD runners are just as important. A self-hosted GitHub Actions runner, GitLab runner, Jenkins worker, Buildkite agent, or custom build farm may execute code from forks, pull requests, package install scripts, test fixtures, or generated artifacts. That makes “local execution” a normal part of the job. If the runner host is vulnerable, a malicious build step may not need a remote exploit at all.

For CI environments:

# Example triage checklist for runner hosts.
hostname
uname -a
cat /etc/os-release
lsmod | grep -w act_pedit || true
sysctl user.max_user_namespaces 2>/dev/null || true
sysctl kernel.unprivileged_userns_clone 2>/dev/null || true
tc actions list action pedit 2>/dev/null || true

Then apply process controls:

Runner controlPor qué es importante
Use ephemeral runners for untrusted jobsLimits persistence after a single build
Separate trusted release jobs from untrusted PR jobsPrevents root on a test runner from becoming artifact compromise
Minimize runner secretsReduces blast radius if a node is compromised
Rebuild runner images after kernel patchingEnsures new jobs start from a known baseline
Avoid privileged Docker-in-Docker where possibleReduces kernel attack surface and host reach
Rotate secrets after suspected exposureAssumes root compromise may have read local credentials

Por qué act_pedit module state can mislead

A common response pattern is to run lsmod, fail to see act_pedit, and decide the host is safe. That can be wrong. Module state is a snapshot, not a reachability proof.

Si act_pedit is compiled as a module and can be loaded on demand, an attacker may be able to trigger loading by configuring a pedit action inside a reachable namespace. If it is built into the kernel, lsmod will not show it as a module at all. If a vendor kernel has backported changes, raw upstream version matching can also mislead.

A better check combines several sources:

echo "Running kernel:"
uname -r

echo "Loaded module:"
lsmod | grep -w act_pedit || echo "not loaded as module"

echo "Available module metadata:"
modinfo act_pedit 2>/dev/null | sed -n '1,12p' || echo "no module metadata"

echo "Kernel config hints:"
zgrep -E 'CONFIG_NET_SCHED|CONFIG_NET_ACT_PEDIT|CONFIG_USER_NS' /proc/config.gz 2>/dev/null || \
grep -E 'CONFIG_NET_SCHED|CONFIG_NET_ACT_PEDIT|CONFIG_USER_NS' /boot/config-$(uname -r) 2>/dev/null || true

echo "pedit rules currently configured:"
tc actions list action pedit 2>/dev/null || true

Even this is not a substitute for vendor patch status. The goal is triage, not final vulnerability proof.

Temporary controls should be chosen by workload, not by panic

Blocking act_pedit is narrow and often attractive on ordinary servers that do not intentionally use packet editing. It is not universally safe. Some advanced networking, CNI, QoS, testbed, or packet rewrite setups may depend on pedit behavior.

Restricting unprivileged user namespaces is broader. It can reduce exposure to many kernel local privilege escalation chains, not just pedit COW, because it removes a common path to namespace-local capabilities. But it can also break legitimate workloads. Rootless Docker and Podman, build sandboxes, browser sandboxes, Flatpak, developer tools, and some CI isolation mechanisms may rely on unprivileged user namespaces.

The operational choice should be explicit:

QuestionIf yesIf no
Can you patch and reboot now?Patch and rebootMove to temporary mitigation
Does the host use tc action pedit?Avoid blocking act_pedit unless testedBlocking act_pedit is likely safer
Does the host rely on rootless containers or userns sandboxes?Avoid disabling userns without testingRestricting userns may be acceptable
Does the host run untrusted local code?Treat as high priorityStill patch, but priority may be lower
Was the host exposed before mitigation?Hunt for compromise and consider cache evictionContinue normal verification

TuxCare’s “decision moment” is a useful summary: if the host runs no pedit rules, block act_pedit; if it needs pedit but not rootless containers or unprivileged unshare, restrict user namespaces; if it needs both, patching or verified livepatching is the real answer. (TuxCare)

A practical remediation runbook

Safe Validation Workflow for CVE-2026-46331

Start with the systems where exploitation would matter most. Do not spend the first hour debating a single CVSS score while CI runners and Kubernetes nodes continue to execute untrusted code.

First wave:

  • CI/CD runners and build workers
  • Kubernetes and OpenShift worker nodes
  • Shared hosting systems
  • Multi-user shell servers
  • Student labs and cyber ranges
  • Developer jump boxes
  • Bastions with many local users
  • AI agent execution sandboxes
  • Research servers with mixed-trust accounts

Second wave:

  • Internet-facing application servers where a web exploit could become local execution
  • Internal application servers with sensitive secrets
  • Container hosts running lower-trust workloads
  • Package build or artifact signing infrastructure
  • Systems with local contractor or partner access

Third wave:

  • Single-user workstations
  • Isolated appliances
  • Hosts with no untrusted local code execution path
  • Environments already protected by restrictive user namespace policies and no pedit reachability

A concise runbook:

# 1. Inventory running kernels and release streams.
uname -a
cat /etc/os-release

# 2. Compare with your vendor advisory.
# Do not rely only on upstream version strings.

# 3. Check act_pedit and user namespace exposure.
lsmod | grep -w act_pedit || true
modinfo act_pedit 2>/dev/null | head -n 5 || true
tc actions list action pedit 2>/dev/null || true
sysctl user.max_user_namespaces 2>/dev/null || true
sysctl kernel.unprivileged_userns_clone 2>/dev/null || true

# 4. Apply vendor kernel update.
# RHEL-family example:
sudo dnf update kernel\* -y

# Debian/Ubuntu example:
sudo apt-get update
sudo apt-get install --only-upgrade 'linux-image*' 'linux-modules*' -y

# 5. Reboot into the fixed kernel.
sudo reboot

# 6. Verify after reboot.
uname -a

For temporary mitigation before patching, document both the control and the reason:

# Temporary mitigation: block act_pedit only after confirming no legitimate pedit usage.
tc actions list action pedit 2>/dev/null || true
echo 'install act_pedit /bin/true' | sudo tee /etc/modprobe.d/disable-act_pedit.conf
lsmod | grep -w act_pedit && sudo rmmod act_pedit || true

For user namespace mitigation:

# Temporary mitigation: restrict unprivileged user namespaces.
# Validate workload impact before broad rollout.

if sysctl user.max_user_namespaces >/dev/null 2>&1; then
  sudo sysctl -w user.max_user_namespaces=0
  echo 'user.max_user_namespaces = 0' | sudo tee /etc/sysctl.d/99-disable-unpriv-userns.conf
fi

if sysctl kernel.unprivileged_userns_clone >/dev/null 2>&1; then
  sudo sysctl -w kernel.unprivileged_userns_clone=0
  echo 'kernel.unprivileged_userns_clone = 0' | sudo tee -a /etc/sysctl.d/99-disable-unpriv-userns.conf
fi

For exposed hosts, add a compromise review:

# Review unusual tc execution.
sudo ausearch -k tc_exec 2>/dev/null || true

# Review recent setuid tool execution if audit rules existed.
sudo ausearch -k su_exec 2>/dev/null || true
sudo ausearch -k sudo_exec 2>/dev/null || true

# Look for suspicious persistence.
sudo find /etc/systemd/system /usr/lib/systemd/system -type f -mtime -14 -ls 2>/dev/null
sudo find /etc/cron* -type f -mtime -14 -ls 2>/dev/null
sudo find /root /home -name authorized_keys -mtime -14 -ls 2>/dev/null

# Review new privileged users.
getent passwd | awk -F: '$3 == 0 {print}'

None of these commands is a full incident response program. They are a starting point for deciding whether a vulnerable host needs deeper forensic handling.

How automated validation can help without running exploit code

For CVE-2026-46331, safe validation is mostly a workflow problem: asset discovery, kernel inventory, vendor advisory matching, module reachability checks, namespace policy checks, patch verification, reboot verification, and evidence capture. The risky part is attempting to “prove” exploitability by running a public local-root PoC on production. A disciplined team can prove exposure without doing that.

In authorized security programs, an AI-assisted workflow can help standardize the safe checks: collect uname and package data, map hosts to vendor advisories, test whether act_pedit is loadable, inspect user namespace controls, record whether the host runs untrusted local workloads, and generate a reproducible report. Penligent’s authorized AI pentest workflow emphasizes evidence-first findings, controlled agent workflows, and report generation for security engineers and red teams, which is relevant when teams need repeatable CVE validation without turning a public exploit into a production test. (Penligente)

The important rule remains human: never let automation expand scope or execute destructive proof-of-concept code just because a public exploit exists. For a local kernel privilege escalation, production validation should stop at exposure and remediation evidence unless the test is explicitly authorized, isolated, and designed for a lab environment.

Common mistakes that slow down response

The first mistake is treating “local” as “not urgent.” That may be reasonable for a locked-down single-user appliance. It is not reasonable for a CI runner, Kubernetes worker, shared host, or server that could already be reached through a low-privileged application compromise.

The second mistake is trusting lsmod alone. A module not currently loaded may still be loadable. A built-in kernel feature may not appear as a module. A host with no current pedit rules may still be exposed if an attacker can configure the action inside a namespace.

The third mistake is assuming file integrity monitoring will catch exploitation. Page-cache-only corruption can leave disk bytes unchanged. If root was obtained, the attacker may also create persistence elsewhere.

The fourth mistake is patching but not rebooting. Kernel packages on disk do not protect a host still running the old kernel.

The fifth mistake is applying mitigations without checking workload impact. Disabling user namespaces can break rootless containers and sandboxing. Blocking act_pedit can break legitimate traffic-control packet editing. Temporary controls should be tested, documented, and removed after patching when appropriate.

The sixth mistake is assuming a PoC failure equals safety. Ubuntu-specific AppArmor hardening may break one public exploit path while the underlying kernel remains vulnerable until patched.

PREGUNTAS FRECUENTES

Is CVE-2026-46331 remotely exploitable?

  • No, the public information describes CVE-2026-46331 as a local privilege escalation in the Linux kernel traffic-control subsystem, not a direct remote unauthenticated RCE.
  • The attacker usually needs local code execution first, such as a shell account, compromised service process, malicious CI job, container workload, or other low-privileged execution path.
  • It still matters in remote intrusions because many attacks first gain low-privileged execution and then use a kernel LPE to become root.

What makes pedit COW different from Dirty Pipe?

  • Dirty Pipe, CVE-2022-0847, involved pipe buffer flag initialization and allowed writes to page-cache-backed read-only files.
  • pedit COW, CVE-2026-46331, reaches a similar page-cache danger zone through Linux traffic control, specifically act_pedit.
  • The common theme is page ownership: kernel code writes into memory it should have made private first.
  • The operational lesson is similar: patch the kernel, do not rely only on disk file hashes, and prioritize hosts where untrusted local code runs.

How do I check whether a host is exposed?

  • Confirm the running kernel with uname -a.
  • Check the exact distribution and release with /etc/os-release.
  • Compare against the vendor’s advisory for that release stream.
  • Check whether act_pedit is loaded or loadable with lsmod y modinfo.
  • Check whether tc action pedit is used today with tc actions list action pedit.
  • Check user namespace controls with sysctl user.max_user_namespaces y sysctl kernel.unprivileged_userns_clone.
  • Treat CI runners, Kubernetes nodes, shared hosts, and multi-user systems as higher priority.

Is disabling act_pedit safe?

  • It is often safe on ordinary servers that do not use traffic-control packet editing.
  • It can break systems that depend on tc action pedit for packet header rewriting, QoS behavior, CNI behavior, lab simulation, or custom network policy.
  • Check existing pedit usage before blocking the module.
  • Remove the temporary block after the host is patched and rebooted, unless your hardening policy intentionally keeps it disabled.

Should I disable unprivileged user namespaces?

  • Disabling unprivileged user namespaces can break the public exploit chain by removing a common path to namespace-local capabilities.
  • It can also break rootless containers, browser sandboxes, Flatpak, CI sandboxes, unprivileged unshare, and developer workflows.
  • It is more suitable for hardened servers and hosting nodes than for developer workstations or container-heavy platforms.
  • Test before broad rollout and document the exception path.

Can file integrity monitoring detect exploitation?

  • Not reliably.
  • The dangerous corruption can live in page cache while the file on disk remains unchanged.
  • Disk hashes may still match expected values.
  • Behavioral telemetry, such as unusual tc execution, act_pedit loading, namespace creation, and suspicious setuid execution sequences, is more useful.
  • If root compromise is plausible, treat the host as potentially compromised even if file hashes look clean.

What should container and CI teams do first?

  • Patch and reboot worker nodes and runner hosts.
  • Separate untrusted jobs from release and signing infrastructure.
  • Minimize secrets available to CI jobs.
  • Restrict privileged containers, host namespace access, and debug access.
  • Rebuild or rotate runners that executed untrusted code during the exposure window.
  • Verify the running kernel after reboot; do not stop at package installation.

Useful resources for deeper reading

NVD’s CVE-2026-46331 record is the best starting point for the official root-cause wording and upstream affected metadata, especially the description of tcf_pedit_act(), skb_ensure_writable(), typed-key offsets, and the fix strategy. (nvd.nist.gov)

Red Hat’s RHSB-2026-008 bulletin is useful for enterprise Linux operators because it explains affected Red Hat products, gives an Important impact rating, and provides practical mitigation guidance for blocking act_pedit. (Portal del cliente de Red Hat)

Ubuntu’s CVE page is useful for release-stream tracking because it shows Ubuntu priority, publication and update dates, and package status across supported and older Ubuntu releases. (Ubuntu)

The Debian security tracker is useful for package-level status because it shows vulnerable and fixed versions by Debian release and security stream, instead of treating all Debian systems as one bucket. (security-tracker.debian.org)

The public proof-of-concept repository is useful for defenders to understand exploit availability and tested environments, but it should not be run on production systems. Use it only in authorized, isolated lab conditions if reproduction is genuinely required. (GitHub)

Closing judgment

CVE-2026-46331 should be handled as a serious Linux local privilege escalation, not as a remote internet worm and not as a harmless local-only bug. The systems that matter most are the ones where local code execution is normal: CI runners, Kubernetes workers, shared hosts, multi-user Linux machines, build servers, and sandbox platforms.

The right response is concrete. Identify exposed hosts. Check the running kernel, not just installed packages. Patch and reboot. Where patching is delayed, block act_pedit if the host does not need it, or restrict unprivileged user namespaces where the workload allows it. Hunt for suspicious tc, namespace, module, and setuid execution activity during the exposure window. Treat clean disk hashes as incomplete evidence, because the most important corruption path may live in memory.

The core lesson is bigger than one CVE: when kernel fast paths combine zero-copy behavior, shared fragments, copy-on-write assumptions, and late-resolved offsets, a small ownership mistake can become root. CVE-2026-46331 is another reminder that local privilege boundaries are production security boundaries.

Comparte el post:
Entradas relacionadas
es_ESSpanish