How Separate Gaps Become an Economic Attack

This chapter examines the USENIX study’s consolidated FREE case—File-based Resource Expansion & Exploitation—to show how the memory and storage boundaries can be composed in a real application. The focus is not a deployment recipe; it is the engineering question behind the result: can an application preserve useful behavior while shifting enough memory and storage outside the accounting view to change its declared resources and bill? The answer depends on copy-on-write semantics, paging overhead, allocation strategy, prices, and execution time as much as on the original accounting gaps.

A shared file-backed mapping and a deleted storage object are separate observations. The first affects where physical memory pages are charged. The second affects whether disk blocks appear in Kubernetes’ ephemeral-storage measurement. Combining them changes the economic situation because the tenant can reduce two declared resource dimensions at once while the host continues to supply the underlying resources. But combining them also turns a simple accounting experiment into an application-integration problem.

From ordinary allocations to three accounting paths

Ordinary applications do not usually ask for “unaccounted memory.” They call an allocator, receive addresses, fill data structures, read inputs, and produce outputs. The paper’s practical contribution is to show how the same application logic can be made to use different backing mechanisms without rewriting its core computation. A custom shared library intercepts selected high-level allocation calls and redirects them to file-backed mappings. The application continues to see pointers and run its algorithm, while the physical path underneath changes.

The transformation is selective rather than indiscriminate. The study’s example uses private read-only file-backed mappings for input data, shared writable mappings for output data, and a deleted backing object for the output’s storage footprint. These choices correspond to the three relevant accounting paths:

  • Read-only input pages can be loaded through a host backend and charged away from the guest’s pod cgroup in the tested configuration.
  • Writable shared pages can shift physical page population toward that backend while allowing the application to update its output.
  • An unlinked backing object can retain blocks without remaining in the path that Kubernetes’ normal storage accounting scans.

The combination matters because each path covers a different part of the application’s footprint. Inputs can dominate memory without needing to be copied into anonymous allocations. Outputs can remain writable while their pages follow the shared file-backed path. The deleted backing object can retain data after its name disappears. The workload’s computation remains inside the guest, but the host performs the work that creates or retains the underlying resources.

flowchart LR A["Application allocation"] --> B{"Selected allocation class"} B -->|"ordinary control data"| C["Native anonymous memory"] B -->|"large input data"| D["Private read-only file-backed pages"] B -->|"large output data"| E["Shared writable file-backed pages"] D --> F["Host backend page population and charge"] E --> F E --> G["Unlinked backing storage"] G --> H["Storage observer misses path or object"] F --> I["Lower reported pod memory"] H --> J["Lower reported ephemeral storage"] D --> K["Page faults and I/O overhead"] E --> K K --> L["Longer execution time"] I --> M["Lower declared resource request"] J --> M M --> N["Bill depends on prices and execution time"] L --> N

The diagram also shows why the attack is more than two independent tricks. The application’s normal data structures do not all have the same sensitivity to paging or sharing.