deniz.in

Markets

Weather

Loading weather

· via Hacker News – Front Page (native)

Inode memoization cache cuts eBPF security agent kernel CPU cost by ~90%

An open-source eBPF security agent cut kernel cycles during file opens from 28 billion to 3.03 billion by caching policy verdicts per inode, its developers report.

Inode memoization cache cuts eBPF security agent kernel CPU cost by ~90%

Caching the verdict instead of re-walking the path

The developers behind an open-source eBPF security agent report that adding a memoization cache to policy lookups cut the kernel CPU cost of file opens by roughly 90%. In a write-up on nathannavein.dev's blog that reached Hacker News's front page, the author explains that profiling revealed the expensive part of the agent's job is not the final allow-or-deny decision, but working out which of its path-based policies applies to a given file in the first place.

The agent enforces policies expressed as filesystem paths. It attaches to a Linux Security Module hook that fires on every file open, rebuilds the file's path, and climbs the chain of parent directory entries checking each level for a matching rule before merging the results into one decision. That walk repeats on every open, even for files the agent has already judged, which hurts workloads such as databases that continually reopen files under the same directories.

Keying the cache on inodes

The team considered keying the cache on dentries but ruled it out: dentries are pointers, which cannot be stored in eBPF maps, and copying their contents into a struct would make for a heavy key. The cache is keyed on the inode number instead, qualified by two extra fields, the mount ID and the mount namespace ID. Inode numbers are only unique within a particular mount tree, so the mount ID disambiguates overlapping inode numbers across mounts, while the namespace component stops a cached verdict from one namespace being reused in another.

The cached value stays small: an access index pointing at a bit position in the agent's bitmask-encoded policy table, plus a state byte distinguishing cases such as "no matching policy" or "global read-only". Everything lives in an LRU hash map capped at 10,000 entries. A hit lets the agent enforce directly from the cached result; a miss triggers the full path walk, whose outcome is then stored for next time.

The measured improvement

In the author's benchmark, opening the same file 200,000 times consumed 28 billion kernel CPU cycles without the cache and 3.03 billion with it, measured with perf's cycles:k event. Flamegraphs from before the change showed the agent's own functions dominating the profile: tail_call_security_check appeared on the stack in 89.2% of samples, is_restricted_filepath in 81.9%, and path_check_callback in 63.7%. Afterward the latter two shrank to roughly 0.02%, effectively disappearing once the first lookup for a file has been cached.

Hardlinks force a conservative fallback

One correctness hazard remains: multiple paths can point at a single inode, hardlinks being the obvious case, so a cached policy derived from one path could be wrong for another. The authors handle this by reading the inode's link count and skipping the cache entirely whenever it is greater than one, falling back to the full path walk. The author describes this as a workaround rather than a true fix, accepting reduced cache coverage in exchange for accuracy, on the grounds that a wrong cached answer is worse than a slower correct one.

Why it matters

Security agents that ride LSM hooks run on every file open across a machine, so their overhead acts as a tax on the whole system and is a known obstacle to adopting eBPF-based enforcement. This result shows that much of that tax can come from redundant lookup work rather than enforcement itself, and that a modest LRU map keyed on mount namespace, mount and inode can eliminate it. The cache is entirely internal, meaning deployments get the speedup without touching a single policy rule. The technique is a reproducible pattern for anyone building eBPF LSM tooling, and because the authors have open-sourced the agent as bomfather/agent on GitHub, the implementation and benchmarks can be inspected and rerun directly.

  • #ebpf
  • #linux-kernel
  • #security
  • #performance
  • #caching

Related posts