Table of Contents
Fetching ...

SplittingSecrets: A Compiler-Based Defense for Preventing Data Memory-Dependent Prefetcher Side-Channels

Reshabh K Sharma, Dan Grossman, David Kohlbrenner

TL;DR

SplittingSecrets addresses DMP data-at-rest side-channels by transforming memory representations of secrets so that they cannot form valid addresses, preventing DMP-triggered prefetching. The approach is compiler-based, architecture-aware, implemented in LLVM for ARM64, and evaluated on Apple M-series hardware using libsodium; correctness, security, and soundness are demonstrated with both unit tests and real-world cryptographic benchmarks. The evaluation shows notable overhead in compile time, binary size, runtime, and memory, but the defense remains practical as it requires no hardware changes and is adaptable across DMP variants by rotating the chosen prefix. This work provides a software-only defense against evolving microarchitectural threats and opens avenues for selective transformations and finer-grained data-splitting strategies.

Abstract

Traditional side-channels take advantage of secrets being used as inputs to unsafe instructions, used for memory accesses, or used in control flow decisions. Constant-time programming, which restricts such code patterns, has been widely adopted as a defense against these vulnerabilities. However, new hardware optimizations in the form of Data Memory-dependent Prefetchers (DMP) present in Apple, Intel, and ARM CPUs have shown such defenses are not sufficient. These prefetchers, unlike classical prefetchers, use the content of memory as well as the trace of prior accesses to determine prefetch targets. An adversary abusing such a prefetcher has been shown to be able to mount attacks leaking data-at-rest; data that is never used by the program, even speculatively, in an unsafe manner. In response, this paper introduces SplittingSecrets, a compiler-based tool that can harden software libraries against side-channels arising from DMPs. SplittingSecrets's approach avoids reasoning about the complex internals of different DMPs and instead relies on one key aspect of all DMPs: activation requires data to resemble addresses. To prevent secret data from leaking, SplittingSecrets transforms memory operations to ensure that secrets are never stored in memory in a manner resembling an address, thereby avoiding DMP activation on those secrets. Rather than disable a DMP entirely, SplittingSecrets can provide targeted hardening for only specific secrets entirely in software. We have implemented SplittingSecrets using LLVM, supporting both source-level memory operations and those generated by the compiler backend for the AArch64 architecture, We have analyzed the performance overhead involved in safeguarding secrets from DMP-induced attacks using common primitives in libsodium, a popular cryptographic library when built for Apple M-series CPUs.

SplittingSecrets: A Compiler-Based Defense for Preventing Data Memory-Dependent Prefetcher Side-Channels

TL;DR

SplittingSecrets addresses DMP data-at-rest side-channels by transforming memory representations of secrets so that they cannot form valid addresses, preventing DMP-triggered prefetching. The approach is compiler-based, architecture-aware, implemented in LLVM for ARM64, and evaluated on Apple M-series hardware using libsodium; correctness, security, and soundness are demonstrated with both unit tests and real-world cryptographic benchmarks. The evaluation shows notable overhead in compile time, binary size, runtime, and memory, but the defense remains practical as it requires no hardware changes and is adaptable across DMP variants by rotating the chosen prefix. This work provides a software-only defense against evolving microarchitectural threats and opens avenues for selective transformations and finer-grained data-splitting strategies.

Abstract

Traditional side-channels take advantage of secrets being used as inputs to unsafe instructions, used for memory accesses, or used in control flow decisions. Constant-time programming, which restricts such code patterns, has been widely adopted as a defense against these vulnerabilities. However, new hardware optimizations in the form of Data Memory-dependent Prefetchers (DMP) present in Apple, Intel, and ARM CPUs have shown such defenses are not sufficient. These prefetchers, unlike classical prefetchers, use the content of memory as well as the trace of prior accesses to determine prefetch targets. An adversary abusing such a prefetcher has been shown to be able to mount attacks leaking data-at-rest; data that is never used by the program, even speculatively, in an unsafe manner. In response, this paper introduces SplittingSecrets, a compiler-based tool that can harden software libraries against side-channels arising from DMPs. SplittingSecrets's approach avoids reasoning about the complex internals of different DMPs and instead relies on one key aspect of all DMPs: activation requires data to resemble addresses. To prevent secret data from leaking, SplittingSecrets transforms memory operations to ensure that secrets are never stored in memory in a manner resembling an address, thereby avoiding DMP activation on those secrets. Rather than disable a DMP entirely, SplittingSecrets can provide targeted hardening for only specific secrets entirely in software. We have implemented SplittingSecrets using LLVM, supporting both source-level memory operations and those generated by the compiler backend for the AArch64 architecture, We have analyzed the performance overhead involved in safeguarding secrets from DMP-induced attacks using common primitives in libsodium, a popular cryptographic library when built for Apple M-series CPUs.
Paper Structure (49 sections, 4 figures, 2 tables)

This paper contains 49 sections, 4 figures, 2 tables.

Figures (4)

  • Figure 1: The end-to-end pipeline of SplittingSecrets. Extra memory is allocated for annotated secrets, with the top bits of addresses set for dynamic tracking. At the LLVM IR level, memory operations are instrumented with checks and transformed loads and stores for our defense. Memory operations generated by the backend are flagged or lowered to pseudo instructions that are later managed using an MIR pass.
  • Figure 2: Example of transforming a 256-bit unsafe store. Each circle represents 64 bits. 64-bit chunks of the input (e.g the magenta and blue semi-circles) are split and padded with a 32-bit fixed constant value (black semi-circle.) The lower half with padding is stored at the original memory location and the upper half with padding is stored at extra memory allocated for the secret.
  • Figure 3: Example of a 64-bit constant-time conditional swap that would require transformation.
  • Figure 4: Pseudocode for transformed load and store operations of secret data $\geq 4$ bytes. These transformations assume that extra memory has been allocated for the secret data as needed. For both transforms, isSecret() checks if the address has a high-bit set to mark it as secret, unsetBit() clears the secret tracking bit, isShadow() checks if the address points to extra allocated memory, heapAddr() translates a normally allocated address to the corresponding address in the extra memory. For Store, extractBits() extracts 32-bit chunks that will be merged with the special prefix using merge(). For Load, extractBits() extracts 32-bit chunks that were stored with the prefix, and insertBits() combines the extracted chunks into the final value.