CVE-2026-35361: mknod SELinux Cleanup Fix Replaces rmdir with unlink
Summary
The patch corrects cleanup behavior in uutils coreutils mknod when SELinux labeling fails. The vulnerable code attempted to remove a newly created special file with remove_dir, which does not apply to FIFOs or device nodes, leaving the object on disk with default labeling/permissions. The fix switches cleanup to remove_file and adds a regression test covering invalid SELinux context handling. This directly addresses the reported permission-bypass condition.
Analysis
Vulnerability
CVE-2026-35361 describes a permission-bypass condition in mknod within uutils coreutils. When creation of a FIFO or device node succeeds but subsequent SELinux labeling fails, the utility is expected to roll back by deleting the newly created filesystem object. The vulnerable implementation instead attempted directory removal on a non-directory special file, causing cleanup to fail silently and leaving the object on disk. Per the CVE context from MITRE and NVD, this can leave mislabeled special files under default permissions, undermining mandatory access control expectations.
The root issue is API misuse during error unwinding: special files such as FIFOs and device nodes must be removed with unlink-style semantics, not directory removal. Because the cleanup path ignored the failure result, the orphaned node persisted after the command reported failure.
Vulnerable cleanup path:
let _ = std::fs::remove_dir(file_name);
Patched cleanup path:
let _ = std::fs::remove_file(file_name);The code change and regression test are documented in the upstream patch reference: uutils/coreutils PR #10582.
Patch
The patch replaces std::fs::remove_dir(file_name) with std::fs::remove_file(file_name) in the SELinux-failure cleanup path of src/uu/mknod/src/mknod.rs. This is the correct primitive for deleting FIFOs and device nodes created by mknod. The change is narrowly scoped and directly aligned with the failure mode described in the advisory.
A regression test was added under SELinux-gated coverage in tests/by-util/test_mknod.rs. The test invokes mknod with an invalid SELinux context, expects command failure, and then asserts that the target path does not exist. That test validates the intended rollback behavior after labeling failure.
#[test]
#[cfg(feature = "feat_selinux")]
fn test_mknod_selinux_invalid_cleanup() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let dest = "test_fifo";
new_ucmd!()
.arg("--context=invalid_context_t")
.arg(at.plus_as_string(dest))
.arg("p")
.fails()
.no_stdout();
// invalid context → node must not exist
assert!(!at.file_exists(dest));
}Source: https://github.com/uutils/coreutils/pull/10582.
Review
Pros
- The fix is semantically correct for the affected object types: FIFOs and device nodes are removed via file unlink, not directory removal.
- The patch addresses the actual error-handling path that caused persistence of mislabeled or unlabeled special files, matching the vulnerability description in NVD.
- The added regression test is well targeted: it exercises SELinux context failure and verifies post-failure filesystem state rather than only checking process exit status.
- The change is minimal, reducing risk of unrelated behavioral regressions.
Cons
- The cleanup call still discards the deletion result with
let _ =, so secondary cleanup failures remain unreported and unobservable in this path. - The provided test covers an invalid SELinux context for a FIFO case, but the advisory also mentions device nodes; broader coverage across node types would improve confidence.
- The patch summary does not indicate whether additional hardening was considered for partial-creation states, logging, or explicit diagnostics when rollback itself fails.
Verdict
Root-cause.
This patch directly corrects the erroneous cleanup primitive responsible for the vulnerability. The defect was not an incomplete policy check or a superficial guard; it was a wrong filesystem deletion API in the rollback path. Replacing remove_dir with remove_file fixes the mechanism that left orphaned special files behind after SELinux labeling failure. The accompanying regression test materially supports that conclusion. While additional observability around cleanup failures would be beneficial, the security issue described in MITRE and NVD appears to be directly and adequately addressed by the upstream change in PR #10582.
Recommended Labs
Try this vulnerability pattern yourself with hands-on labs.
- ToCToU.c
This is the closest hands-on defensive match for CVE-2026-35361. The CVE centers on unsafe cleanup after a failed security-sensitive operation, leaving behind dangerous filesystem objects. That maps well to time-of-check/time-of-use and race-condition style file handling flaws, where the fix requires correct object lifecycle handling and defensive cleanup.
- ToCToU.cpp
Also highly relevant for practicing defensive reasoning around filesystem state changes, object replacement, and secure handling of resources during error conditions. Although the CVE is in Rust and this lab is C++, the underlying secure coding lesson is the same: avoid leaving exploitable artifacts behind when operations fail.
- Path Traversal.go
This is a useful complementary defensive lab because the CVE involves unsafe filesystem object handling and broken assumptions about paths and cleanup. While not a direct match, path traversal labs strengthen secure file-operation habits: validating targets, constraining file effects, and preventing attackers from turning filesystem mistakes into security boundary bypasses.