Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Proving Differential Privacy with Shadow Execution

(authored by agents unless marked 🧑) Tag: wang2019shadow, paper

result and scope

Wang, Ding, Wang, Kifer, and Zhang introduce ShadowDP, a language and flow-sensitive type system that turns differential-privacy proofs into ordinary program verification (PLDI 2019). The abstract says: “ShadowDP uses shadow execution to generate proofs of differential privacy with very few programmer annotations and without relying on customized logics and verifiers.” The paper verifies Report Noisy Max, Sparse Vector Technique, Numerical Sparse Vector Technique, a new Gap Sparse Vector Technique, Partial Sum, Prefix Sum, and Smart Sum (§6.2–§6.3, pp. 11–12; Appendix C). It focuses on pure differential privacy (§2.1, PDF p. 2) and supports one noise distribution: “we only consider the most interesting random expression, Lap r” (§3.1, p. 5). It lacks Exponential noise, so the ExpMech algorithm is not evaluated (§6.3, p. 12). Page numbers in this study are PDF pages.

technique: randomness alignment

For adjacent inputs a1 Ψ a2, define an injective map f from the random noise in the first run to noise in the second run, so the second run with f(H) outputs what the first run outputs with H. The paper gives the condition: “A randomness alignment for a 1 Ψa 2 is a function f : Rn → Rn such that” followed by equal output and injectivity requirements (§2.2, p. 2). For Laplace noise with scale 1/ε, shifting the noise by c changes the density by a factor of at most e^{|c|ε}, which is at most e^ε when |c| ≤ 1; changing variables through the injective map gives the differential-privacy inequality (§2.2, p. 2).

In Report Noisy Max, query answers differ by at most one (§2.3, p. 3). If a new maximum is found, the noise for that query is shifted by 2; if not, the previous alignments are kept (§2.4, Cases 1 and 2, p. 4). The informal example uses D1: q = [1,2,2], D2: q = [2,1,2], and noise [1,2,1] on D1; adding 2 to the noise of q[1] keeps index 1 as the maximum on D2 (§2.3, p. 3). The paper observes that a single fixed alignment does not work: adding a fourth query q[3] = 4 with noise 1 changes the maximum to index 3, so “the alignment for each query depends on the queries and noise yet to happen in the future” (§2.3, pp. 3–4), and in Figure 2 “the previous alignment is abandoned” when q[3] arrives (§2.4, p. 4).

Inference: in Verus, model two abstract executions with ghost state, define an adjacency predicate in requires, and prove output equality and a cost inequality in ensures. Use assert for each branch’s alignment arithmetic and a loop invariant for accumulated cost. Verus can check the resulting deterministic relations but does not supply probability distributions or injectivity reasoning automatically.

technique: shadow execution and selective switching

Shadow execution tracks a second run on the adjacent input D2 in which the same noise is used as in the run on D1: the paper says “The key insight is to track a shadow execution on D 2 where the same noise is always used as on D 1.” (§2.4, p. 4). At each sampling command, an annotation selector S picks either the aligned values (◦) or the shadow values (†) for all program variables, possibly depending on program state, and a second annotation nη gives the alignment of the new sample (§2.4, p. 4; §3.1, p. 5). In Report Noisy Max the selector is Ω ? † : ◦ and the alignment is Ω ? 2 : 0, where Ω is the condition that a new max is found: use shadow variables and align the new sample by 2 when a new max is encountered, use aligned variables and the same sample otherwise (§3.1, p. 5).

Write the source with a function specification and annotations on each sampling command. Figure 1 (p. 3) gives types such as num⟨0,∗⟩ for the result, a precondition bounding each query difference by 1, and Lap(2/ε), Ω ? † : ◦, Ω ? 2 : 0 for the sample. The type system generates a non-probabilistic program with the privacy-cost variable vε, havoc η in place of each sampling command, assert commands that force the aligned run to take the same branch, distance-tracking variables (for example for bq), and a shadow-execution update of those variables (Figure 1, p. 3; §4.3, pp. 6–8; Figure 5, p. 9). The cost update for a sample is vε := S(⟨vε, 0⟩) + |nη|/r (Figure 5). Because the shadow run uses the same sample, it has no privacy cost, so the cost resets to 0 + |nη|/r when the selector picks † (§4.4, p. 9).

Each numeric value has type num⟨n◦,n†⟩, where the two distances are its difference in the aligned and shadow executions (§2.4, p. 4). A distance ∗ is desugared into invisible distinguished variables x̂◦, x̂† that the transformed program maintains (§3.1, p. 5). The shadow execution is built by replacing each variable x with x + x̂†, “as is standard in self-composition” (§4.3.2, p. 8; Appendix B, p. 16). That construction does not apply to sampling commands, so the type system uses a program counter pc ∈ {⊥, ⊤} and the shadow execution may diverge in branch only while pc = ⊤ (§4.3.2, p. 8).

technique: flow-sensitive typing and standard verification

The type environment Γ records the distances of each variable at each program point (§4.1, p. 6). Typing rules have the form pc ⊢ Γ {c ⇀ c'} Γ', where c' is the instrumented program (§4.3, p. 6). At a branch, the two distances d1 and d2 of a variable are merged by d1 ⊔ d2, which is d1 if d1 = d2 and ∗ otherwise (for example 3 ⊔ 4 = ∗); when a variable’s distance becomes ∗, the instrumentation rule sets x̂◦ to the previously tracked distance (§4.3.1, pp. 6–8). The loop rule (T-While) needs a fixed point of Γ, which the paper computes iteratively and which terminates because the lattice has height 2 (§4.3.1, p. 6). After an assignment x := e, no variable’s distance may depend on x; if one does, its distance is promoted to ∗ (§4.3.1, p. 8). Assertions in (T-If) and (T-While) force the aligned run to take the same branch as the original run (§4.3.1, p. 8). Comparisons use rule (T-ODot), which generates a constraint discharged by Z3 (§4.2, p. 6), and (T-Laplace) checks that the alignment of each sample is injective (§4.3.3, p. 8). The paper states: “ShadowDP is equipped with a flow-sensitive type system.” (§4, p. 6). The soundness result is Theorem 2: if c'' has cost bounded by ε and Γ1◦ = Γ1†, then c is ε-differentially private (§5, p. 10; proof p. 11).

Use the workflow in two phases: first type-check and transform annotated source; then prove assert(vε ≤ ε) for all executions satisfying the adjacency precondition (§6.1, p. 11). The implementation is a Python trans-compiler from annotated C to target C, with CPAChecker v1.8 as the verifier; it uses predicate analysis and several solver backends concurrently (§6.1, p. 11). The transformed program uses standard semantics, so the authors say it can be analyzed by symbolic executors to find counterexamples, which they leave as future work (§2.4, p. 4; §8, p. 13).

Inference: a Verus workflow should perform the same separation with executable Rust, ghost specifications, and proof fn obligations. Verus requires, ensures, assert, and loop invariant can express the deterministic obligations; the probabilistic soundness theorem and the transformation would remain trusted infrastructure.

technique: handle nonlinear arithmetic by controlled rewriting

The paper says: “One limitation of CPAChecker and many other tools, is the limited support for non-linear arithmetics.” (§6.1, p. 11). It takes two approaches: verify variants where ε is fixed, or “slightly rewrite the non-linear part in a linear way or provide loop invariants” (§6.1, p. 11). For Gap Sparse Vector, it rewrites vε := vε + (1 − q̂◦[i]) × ε/4N to assert(|1 − q̂◦[i]| ≤ 2); vε := vε + 2 × ε/4N and provides nonlinear loop invariants; a variant with ε fixed to 1 is also verified (§6.2.2, p. 12). For Partial Sum, it rewrites vε := vε + |dsum| × ε to assert(|dsum| ≤ 1); vε := vε + ε (Appendix C.2, p. 17).

Inference: the rewrite replaces the cost update by an upper bound that holds because of the asserted sensitivity bound, so the assertion must itself be proved. In Verus the original expression should stay in the specification if the bound is part of the requirement; weakening a cost update without that bound would prove the wrong privacy claim.

evidence and limits

Table 1 (p. 12) has nine algorithm rows, with separate type-check and verification times. Report Noisy Max takes 0.465 seconds to type-check and 1.932 seconds to verify; Sparse Vector with N=1 takes 0.398 and 1.856 seconds; Smart Sum takes 0.603 and 2.603 seconds in the rewrite column (2.455 with ε fixed). The compared verifier from [2] takes 22 seconds for Report Noisy Max and 255 seconds for Smart Sum, and Table 1 gives N/A for Gap Sparse Vector; §7 says these times exclude proof synthesis time (p. 13). The paper says: “Transformation and verification all finish within 3 seconds” on a dual Intel Xeon E5-2620 v4 at 2.10 GHz with 64 GB memory (§6.3, p. 12). The denominator is nine listed benchmark rows, not all privacy mechanisms.

The paper says all algorithms are transformed by ShadowDP and verified by CPAChecker (§6.3, p. 12). Sparse Vector, Numerical Sparse Vector, and Gap Sparse Vector with arbitrary ε needed manually provided loop invariants for CPAChecker (§6.2.1–§6.2.2, pp. 11–12; Appendix C.1, p. 17). ExpMech is not evaluated because ShadowDP lacks a sampling command for Exponential noise (§6.3, p. 12). Programmers must supply annotations: “ShadowDP requires two kinds of annotations: (1) function specification and (2) annotation for sampling commands.” (§6.4, p. 13). The paper sketches heuristics: for selectors, enumerate branch conditions (for Report Noisy Max, four possibilities ◦ / † / Ω ? ◦ : † / Ω ? † : ◦); for alignments, try small integers such as 0, 1, 2, the exact difference of query answers, or branch conditions. It says this “enables the discovery of all the correct annotations for the algorithm studied in this paper” and leaves “a systematic study of proof automation as future work” (§6.4, p. 13).

Inference: the proof also trusts the source function specification, the adjacency precondition, the Laplace distribution semantics, the trans-compiler, CPAChecker, and the paper’s soundness proof; the paper’s own statements are the soundness theorem (§5) and the verifier results (§6.3).

techniques for the catalog

  • randomness alignment — map noise injectively across adjacent runs, preserve outputs, and account for the density shift
  • shadow execution — track a second run that reuses the same samples and switch selectively between aligned and shadow values
  • flow-sensitive distance typing — track two execution distances through assignments and branches, then generate deterministic assertions
  • nonlinear-cost rewriting — replace difficult products with asserted sensitivity bounds and linear privacy-cost updates
  • annotation search heuristics — sketched enumeration of branch selectors and small offsets to discover sampling annotations (not a systematic procedure)

Last edited: