1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
//! # ct_grind — Valgrind/memcheck-backed constant-time verification
//!
//! This module implements the **ctgrind** technique (Adam Langley,
//! 2010; used by BearSSL, ring, etc.): secret-bearing buffers are
//! marked as *uninitialized* in Valgrind's memcheck shadow memory,
//! and the program is then run under `valgrind --error-exitcode=1`.
//! Any branch, pointer derefence, or syscall that consumes an
//! uninitialized byte is flagged as an error. Because memcheck
//! propagates definedness through arithmetic, conditional-move tricks
//! and bitmasked selects are tracked correctly — only true branches
//! or secret-indexed memory accesses on the hot path trigger a
//! diagnostic.
//!
//! The instrumentation itself is implemented as two Valgrind client
//! requests:
//!
//! | Helper | Valgrind request |
//! |----------------|------------------------------------------|
//! | [`poison`] | `VG_USERREQ__MAKE_MEM_UNDEFINED` (secret)|
//! | [`unpoison`] | `VG_USERREQ__MAKE_MEM_DEFINED` (public)|
//!
//! # Architectures
//!
//! Client-request instrumentation is emitted only on
//! `target_os = "linux"` and either `x86_64` or `aarch64`. On every
//! other target, and whenever the `ct-grind` cargo feature is off,
//! [`poison`] / [`unpoison`] / [`poison_raw`] / [`unpoison_raw`] are
//! **zero-cost no-ops**, which lets call sites embed them
//! unconditionally (no `#[cfg]` walls at the use site).
//!
//! # Zero dependencies
//!
//! The Valgrind magic sequences are implemented with stable
//! `core::arch::asm!` — no C shim, no crate dependency. This mirrors
//! the BearSSL approach and keeps the krypteia *zero deps* rule.
//!
//! # Safety
//!
//! [`poison`] and [`unpoison`] take shared slice references and are
//! safe. The raw-pointer variants [`poison_raw`] / [`unpoison_raw`]
//! are `unsafe` because the caller must guarantee that `ptr..ptr+len`
//! is a valid addressable region; Valgrind only manipulates its
//! shadow metadata and never reads or writes the underlying bytes.
//!
//! # Example
//!
//! ```no_run
//! use silentops::ct_grind;
//!
//! let mut secret = [0u8; 32];
//! // …fill secret from an RNG…
//! ct_grind::poison(&secret); // memcheck: "now undefined"
//!
//! // Do CT work with `secret`. A leak would trigger a valgrind error.
//!
//! ct_grind::unpoison(&secret); // memcheck: "defined again"
//! ```
/// Mark `buf` as **undefined** (secret) in Valgrind's memcheck shadow
/// memory. Any later branch or memory-access decision that depends
/// on these bytes will be flagged by `valgrind --error-exitcode=1`.
/// Mark `buf` as **defined** again — the complement of [`poison`].
/// Call this once a buffer can legitimately flow into public data
/// (e.g. a ciphertext that is about to be returned to the caller).
/// Raw-pointer variant of [`poison`]. See [module docs](self) for safety.
///
/// Sandwich the client request between two SeqCst compiler fences so
/// LLVM cannot reorder any surrounding memory access across the
/// poisoning — empirically, without the fences LLVM batches the
/// unpoison calls past subsequent `assert_eq!`/`memcmp`, so the
/// comparison runs on bytes that memcheck still marks undefined.
///
/// # Safety
///
/// `ptr` must point to a valid memory region of at least `len`
/// bytes that the caller currently owns (no aliasing constraint
/// stricter than for any other memcheck client request). Passing
/// a dangling pointer, a region the caller does not own, or a
/// `len` that overruns the allocation is undefined behaviour.
/// When the `ct-grind` Cargo feature is disabled the call is a
/// zero-cost no-op (only the compiler fences remain) so the
/// safety contract is trivial in that mode.
pub unsafe
/// Raw-pointer variant of [`unpoison`]. See [module docs](self) for safety.
///
/// See [`poison_raw`] for the rationale behind the surrounding fences.
///
/// # Safety
///
/// Same contract as [`poison_raw`]: `ptr` must point to a valid
/// memory region of at least `len` bytes that the caller currently
/// owns. Calling `unpoison_raw` on a region that was never
/// `poison_raw`'d is allowed (it is a no-op as far as memcheck
/// is concerned) but the pointer / length validity contract
/// still applies. When the `ct-grind` Cargo feature is disabled
/// the call is a zero-cost no-op around the compiler fences.
pub unsafe
/// Whether this build emits real Valgrind client requests.
///
/// `true` only when the `ct-grind` feature is enabled **and** the
/// target is `x86_64-linux` or `aarch64-linux`. Useful in tests
/// that want to skip or annotate behaviour when ctgrind is inert.
pub const