CVE Patch Review

CVE-2026-35339: chmod -R Exit Status Aggregation Fix Review

CVE-2026-35339 · Updated 2026-07-06 Root-cause

Summary

The patch changes recursive chmod result handling from overwriting prior status with the most recent traversal result to aggregating failures across targets. This directly addresses the reported 'last-file-wins' behavior that could return success after earlier recursive failures. Added regression coverage validates that a failed recursive target causes the overall command to fail even when a later target succeeds.

Analysis

Vulnerability

CVE-2026-35339 describes incorrect exit-status propagation in Rust uutils/coreutils chmod during recursive execution. The vulnerable behavior is a classic aggregation bug: when processing multiple targets with chmod -R, the command stored the latest recursive result instead of preserving any earlier failure. As summarized in the code reference at the upstream patch discussion, the vulnerable line was:

r = self.walk_dir_with_context(file, true);

This means a failure on an earlier operand could be overwritten by a later successful operand, producing exit code 0 despite a real permission or traversal error. In automation contexts, that is security-relevant because scripts, CI jobs, and policy checks often trust process exit status as the sole success signal. The CVE record and related CNA context are published at NVD and CVE.org.

Patch

The patch changes result propagation so that recursive traversal failures are accumulated rather than overwritten. The patched line is:

r = self.walk_dir_with_context(file, true).and(r);

Semantically, this converts the logic from "last result wins" to "any failure persists." If the current recursive walk fails, the combined result remains failed; if the current walk succeeds, the prior state r is still preserved. That is the correct shape for command-level exit semantics when processing multiple operands.

The patch also adds a regression test in tests/by-util/test_chmod.rs that creates multiple directories, removes permissions from one target, then invokes recursive chmod across both failing and succeeding operands. The test asserts that the command fails overall and emits the expected permission-denied error. This directly exercises the reported scenario where an earlier failure was previously masked by a later success. The source reference for both code and tests is PR #9793.

Review

Pros

  • The code change is minimal and targeted at the actual failure mode: result aggregation during recursive processing.
  • The new logic preserves prior failure state across subsequent operands, which matches expected Unix utility exit behavior.
  • The regression test is scenario-driven and reproduces the multi-target recursive case central to the CVE.
  • The test validates command failure rather than only stderr text, which is important because the vulnerability is specifically about exit status.
  • The patch is source-aligned with the CVE description at NVD.

Cons

  • The patch summary exposes only a single call-site change, so review confidence depends on whether all recursive chmod paths funnel through this same aggregation variable.
  • The use of .and(r) is compact but slightly non-obvious; maintainers unfamiliar with the chosen result type may need to reason carefully about evaluation and ordering semantics.
  • The added test covers one recursive permission-denied case, but the provided snippets do not show broader coverage for mixed non-recursive operands, symlink traversal edge cases, or multiple independent failures.
  • One test snippet also updates expected stderr wording from "chmod: 'a': Permission denied\n" to "chmod: cannot access 'a': Permission denied\n"; that appears orthogonal to the exit-code bug and may reflect platform or message normalization rather than the vulnerability itself.

Verdict

Root-cause.

The patch addresses the underlying defect: incorrect aggregation of per-target recursive results. The vulnerable implementation overwrote prior failure state, while the patched implementation preserves failure across later successful traversals. That directly fixes the silent-success condition described in CVE.org and NVD. Based on the supplied diff, this is not merely a message tweak or a narrow special-case guard; it corrects the command's result-composition logic and adds a regression test that would have caught the original bug.

Sources