Linux Security / Advanced: constrained design and verified change / Lesson 14
Review race-aware resource access
Connect check/use timing, trusted directory references, object identity, and atomic platform interfaces.
On this page
Lesson goals
- Explain why a successful precheck can become stale.
- Define the evidence needed for a constrained resource-access design without opening host paths.
Name the mutable fact between check and use
A program checks that a path refers to an approved output and later looks up the same text again to write. If another actor can change a namespace component, the second lookup may reach another object. The vulnerability is the unsupported continuity assumption, not merely the presence of a symlink string. Identify who controls each component and which authority the operation uses. This lesson models a changed dictionary reference only. It opens no file, creates no link, races no process, and performs no privilege or filesystem operation on the host.
Show a stale observation without a live race
The first program records a checked object id, changes the supplied mapping, and demonstrates that a later name lookup differs. It does not write to either object and is not an actual TOCTOU exploit. The example makes the interleaving explicit so a reviewer can explain the policy failure. A real system needs platform-supported constraints or atomic resource acquisition appropriate to the exact threat model. Merely checking more often can leave the same gap. Keep the trusted object reference, lookup policy, and subsequent operation’s semantics in the design record.
namespace = {"report-name":"approved-object"}
checked_object = namespace["report-name"]
assert checked_object == "approved-object"
namespace["report-name"] = "other-object"
used_object = namespace["report-name"]
assert used_object != checked_object
print("a repeated name lookup can differ from the checked fixture object")
a repeated name lookup can differ from the checked fixture object
The complete program changes an invented dictionary and compares labels. It performs no actual race, symlink operation, file access, or exploit. The interleaving is supplied by the lesson rather than observed on Linux.
Use documented constraints for the selected platform
Linux directory-relative APIs and openat2 resolution flags can express specific lookup constraints on supporting kernels, but their exact guarantees, combinations, and errors must be reviewed. The second record is a custom requirement inventory, not syscall code. It requires a trusted starting reference, a reviewed resolution policy, and observed object-level outcomes. It calls nothing. Do not equate openat, openat2, chroot, lexical normalization, and a container root as interchangeable protections. A real implementation must handle unavailable features and errors without silently falling back to an unconstrained path operation.
{"design":"LAB-RESOURCE-ACCESS-1","trustedDirectoryReferenceRequired":true,"resolutionPolicyReviewed":false,"featureSupportVerified":false,"fallbackMustPreserveConstraint":true,"objectOutcomeVerified":false,"syscallExecuted":false}
The custom JSON configures no syscall and names no valid openat2 flag combination. It records missing design evidence. No kernel feature, directory descriptor, path, or fallback behavior is tested.
Review creation, replacement, and cleanup as distinct actions
Safe resource handling also needs a policy for temporary names, exclusive creation, replacement semantics, ownership, durability, and cleanup. A method appropriate for opening an existing input may not satisfy writing a new output or replacing a result atomically. Do not invent a universal secure temporary-file algorithm or run host experiments for this course. Use reviewed library/platform primitives in the actual application and retain clear ownership of created resources. Cleanup should not delete another workload’s files merely because a name resembles the expected pattern. Preserve state invariants when any step fails.
Verify the intended object and failure invariant
An authorized isolated test should control relevant namespace changes, include legitimate access, and verify which actual object was reached and what remained unchanged on refusal. Record platform support and fallback behavior. A string assertion or a precheck log is insufficient. No such filesystem or concurrency test runs here. The in-memory model proves only that a changed reference invalidates continuity, while the JSON preserves pending mechanism evidence. This distinction helps reviewers choose a concrete safe implementation without confusing educational interleavings with verified Linux race resistance.
Common errors and fixes
A second lookup must reach the checked object
- Why it happens
- Mutable namespace state is assumed stable.
- How to fix it
- Use a reviewed constrained or atomic platform design.
- Prevent it next time
- Test object identity and failure effects under controlled interleavings.
Unsupported constraints fall back to an ordinary open
- Why it happens
- Feature failure silently weakens the security property.
- How to fix it
- Define a fallback that preserves the required boundary or refuses safely.
- Prevent it next time
- Review feature and error paths explicitly.
Cleanup removes every matching name
- Why it happens
- Resource ownership is inferred from appearance.
- How to fix it
- Track owned resources and approved cleanup scope.
- Prevent it next time
- Verify unrelated fixture objects remain unchanged.
Check your understanding
1. Why does checking a path twice not automatically remove a TOCTOU gap?
Show worked solution
Each check can become stale before the later operation if the namespace remains mutable. The design must establish the relevant resource relationship at the actual use boundary through supported primitives. The dictionary exercise illustrates the assumption only and performs no host race or file operation.
2. A constrained API is unavailable. What should the review reject?
Show worked solution
Reject a silent fallback that proceeds with an unconstrained lookup while reporting the same security guarantee. Define supported alternatives or a documented refusal under the requirement. Verify that behavior only in an authorized environment and preserve the unsupported-platform limitation.
Key takeaways
- The mutable fact between check and use must be explicit.
- Supported primitives need exact guarantees and safe failure behavior.
- Object outcomes matter more than repeated path-text checks.