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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
// The ring's full surface (offsets, head/tail, slots, atomic helpers) is only
// reached on builds that actually exercise cross-thread free — the production
// stack pulls `alloc-xthread` for that. Under `--features alloc-core` alone the
// module compiles but several items remain unused; suppress the lints there
// rather than tangle the module in feature-cfg branches that would make the
// hot-path reading harder.
//! [`RemoteFreeRing`] — a per-segment, bounded, **non-intrusive** MPSC queue
//! of freed-block **offsets** (`u32`), carved from segment metadata.
//!
//! ## Why this exists — the cross-thread-free drain-reclaim UAF fix
//!
//! The Phase 12.5 inline `ThreadFreeStack` (an intrusive Treiber stack whose
//! "node" was the freed block's own first word) raced fatally across the slot
//! release→claim boundary (root-caused in `docs/RACE_DRAIN_RECLAIM.md` §8): a
//! cross-thread freer and the slot's new owner contended the SAME block word —
//! the freer wrote a `next` pointer into it while the owner had already popped
//! the block from the `BinTable` and handed it to the app (which wrote user
//! data). The drain then read user data as a free-list `next` pointer → UAF.
//!
//! **This queue removes the contended word entirely.** A cross-thread freer
//! never touches the block's bytes: it only pushes the block's
//! *segment-relative offset* (a plain `u32`) into this in-segment ring. The
//! owner drains the ring and reclaims each offset into the segment's `BinTable`
//! as the single writer. The block's first word is owned solely by whoever
//! currently holds it (free-list `next` while queued in the `BinTable`, or user
//! data while live) — there is no third "in-flight to a remote queue" role that
//! the intrusive TFS introduced. This restores the original `ShardedRegion` 7b
//! discipline (queues carry references/indices, never poison the object).
//!
//! ## What this module IS and is NOT
//!
//! - IS: pure safe data + arithmetic over the [`node`](super::node) seam. Every
//! atomic access goes through [`Node::atomic_u32_at`] (a confined-`unsafe`
//! primitive identical in spirit to `atomic_u64_at`). There is NO `unsafe`
//! here — the crate's structural promise ("`unsafe` lives ONLY in `os` +
//! `node`") is upheld by the compiler.
//! - IS: an MPSC bounded queue. **Many producers** (cross-thread freers) push
//! via `fetch_add`-free CAS-reserve; **one consumer** (the owning thread)
//! drains. The single-consumer invariant is the slot's single-writer rule
//! (the slot's owner is the sole `BinTable` writer, hence the sole drainer).
//! - IS NOT: a way to read or write the *payload* of a freed block. Only the
//! offset (an integer) crosses the queue.
//!
//! ## Layout in a segment
//!
//! ```text
//! ... bin_table_off + BinTable::FOOTPRINT (4-byte aligned)
//! ┌──────────────────────────────────────────────────────────┐
//! │ RemoteFreeRing │
//! │ • head: AtomicU32 (4 B) — drain cursor (consumer) │
//! │ • tail: AtomicU32 (4 B) — push reserve cursor (producers)
//! │ • overflow: AtomicU32 (4 B) — count of discarded pushes │
//! │ (ring-full → bounded leak; sound, never corrupts) │
//! │ • pad: 4 B (align slots to 8) │
//! │ • slots: [AtomicU32; RING_CAP] (RING_CAP × 4 B) │
//! │ each slot holds a block offset or RING_SLOT_EMPTY │
//! └──────────────────────────────────────────────────────────┘
//! ```
//!
//! `FOOTPRINT = 16 (cursor block) + RING_CAP * 4`. With `RING_CAP = 256` that
//! is 1040 bytes per segment — under one page, negligible vs. the 4 MiB
//! segment, and amortised across all blocks the segment serves.
//!
//! ## MPSC protocol (Vyukov-style bounded, CAS-reserved)
//!
//! Two monotonic cursors: `tail` (producers reserve push slots) and `head`
//! (the consumer advances past drained slots). `slots[i % CAP]` holds the
//! offset for the reservation `i`, or `RING_SLOT_EMPTY` if not-yet-written /
//! already-drained.
//!
//! **Push (multi-producer):**
//! 1. `t = tail.load(Relaxed)`. If `t.wrapping_sub(head.load(Acquire)) >= CAP`
//! → ring full → return `Err(Overflow)` (the caller discards the block:
//! bounded leak, sound). `Acquire` on the head load sees the consumer's
//! `Release` head advance, so a slot freed by the drain is observable.
//! 2. CAS `tail: t → t+1` with `AcqRel` on success (the reservation is the
//! linearization point — exactly one producer wins each `t`). `Relaxed` on
//! failure (retry; no side-effect).
//! 3. Store `slots[t % CAP] = offset` with `Release` (publishes the offset to
//! the consumer's `Acquire` slot read). Return `Ok(())`.
//!
//! **Drain (single consumer):**
//! 1. `t = tail.load(Acquire)` (sees every producer's `Release` reservation).
//! 2. While `h != t` (wrap-correct — both cursors are monotonic wrapping
//! counters, so the undrained count is `t.wrapping_sub(h)`, NOT `t - h`):
//! load `slots[h % CAP]` with `Acquire`. If `RING_SLOT_EMPTY`
//! → the reservation was won but the publish store hasn't happened yet
//! (producer is between steps 2 and 3); **stop draining** (we cannot skip
//! it — order is preserved by the cursors; a later drain picks it up).
//! Otherwise reclaim the offset, store `slots[h % CAP] = RING_SLOT_EMPTY`
//! (`Relaxed` — only this consumer writes a non-empty value... no: producers
//! also write here on their reserved slot; but a producer only writes to
//! `slots[p % CAP]` for a `p` it reserved, and reservations are unique, so
//! by the time we drain slot `h`, no producer will write it again until
//! `tail` wraps past `h + CAP` — which the full-check prevents. `Relaxed` is
//! safe because the next producer to touch this slot will `Release`-store
//! its offset, and our drain reads with `Acquire`.), `h = h.wrapping_add(1)`.
//! 3. `head.store(h, Release)` (publishes the drain progress to producers'
//! full-check `Acquire` head load).
//!
//! **Ordering summary (each justified above):**
//! - producer reservation CAS: `AcqRel` (success) / `Relaxed` (failure).
//! - producer publish store: `Release`.
//! - consumer tail load: `Acquire`.
//! - consumer slot load: `Acquire`.
//! - consumer slot clear: `Relaxed`.
//! - consumer head store: `Release`.
//! - producer full-check head load: `Acquire`.
//!
//! ## Overflow semantics (the honest remainder)
//!
//! When the ring is full (`tail - head == CAP`), a push returns
//! `Err(PushOverflow)` and the caller **discards** the block (it stays mapped,
//! unused — a bounded leak). This is SOUND (no UAF, no corruption) but costs
//! RSS: at most `(CAP - drained_count)` blocks per segment can be in flight,
//! and a sustained burst faster than the owner drains leaks one block per
//! overflow. In practice the owner drains on every alloc, so the ring rarely
//! fills under normal churn; the leak bound is the in-flight cross-thread-free
//! footprint per segment between drains. This is strictly better than the
//! Phase 12.5 discard (which leaked the ENTIRE cross-thread-free chain per slot
//! recycle) and, crucially, it is a *correctness-preserving* fallback, not a
//! correctness violation — the race is gone.
use Ordering;
use Node;
/// Sentinel slot value meaning "this slot carries no offset" (either
/// not-yet-published by a producer, or already drained by the consumer). A real
/// block offset is always `< SEGMENT` (`1 << 22`), so `u32::MAX` is unambiguous.
pub const RING_SLOT_EMPTY: u32 = u32MAX;
/// The number of offset slots in the ring. 256 → 1 KiB of slots per segment.
///
/// **Rationale:** a 4 MiB segment holds up to `SEGMENT / MIN_BLOCK` blocks
/// (≈ 256 K at `MIN_BLOCK = 16`). The ring need only absorb the *burst* of
/// cross-thread frees that arrive between the owner's drains (the owner drains
/// on every alloc and on the `find_segment_with_free` scan). 256 covers a
/// typical burst with headroom; overflow degrades to a bounded leak (sound).
/// Larger caps trade segment metadata footprint for rarer overflow; 256 is the
/// mimalloc-class default for per-page deferred-free queues.
pub const RING_CAP: usize = 256;
/// The byte footprint of a `RemoteFreeRing` in segment metadata. Fixed so the
/// bootstrap can carve it deterministically alongside the bin table.
pub const FOOTPRINT: usize = CURSOR_BLOCK + RING_CAP * ;
/// Bits of a ring entry reserved for the block's segment-relative offset.
/// `SEGMENT = 1 << 22`, so every offset is `< 2^22` and fits in the low 22 bits;
/// the high bits carry the size **class** the cross-thread freer stamped (it has
/// the `Layout`, unlike the owner, whose `page_map` is unreliable for the
/// mixed-class pages a shared bump cursor produces — see RACE_DRAIN_RECLAIM §13).
pub const ENTRY_OFF_BITS: u32 = 22;
/// Mask for the offset field of a packed ring entry.
pub const ENTRY_OFF_MASK: u32 = - 1;
/// Pack a `(offset, class_idx)` pair into a single `u32` ring entry.
/// `off < 2^22` (a segment offset) and `class_idx < SMALL_CLASS_COUNT (= 40)`,
/// so the result is `< 2^32` and never collides with `RING_SLOT_EMPTY`
/// (`u32::MAX`) for any real block.
pub
/// Unpack a ring entry into `(offset, class_idx)`.
pub
/// The cursor block: `head`, `tail`, `overflow`, and a 4-byte pad so the slot
/// array starts 8-aligned (harmless on `AtomicU32` but tidy). 16 bytes total.
const CURSOR_BLOCK: usize = 4 * ;
/// Offset of the `head` cursor within the ring metadata.
const HEAD_OFF: usize = 0;
/// Offset of the `tail` cursor within the ring metadata.
const TAIL_OFF: usize = 4;
/// Offset of the `overflow` counter within the ring metadata.
const OVERFLOW_OFF: usize = 8;
/// Offset of the first slot within the ring metadata.
const SLOTS_OFF: usize = CURSOR_BLOCK;
/// The per-segment non-intrusive cross-thread-free MPSC ring.
///
/// A thin view over in-segment metadata (no allocation — the bootstrap carves
/// the bytes at [`super::segment_header::Layout::remote_ring_off`]). Producers
/// push block offsets; the single consumer ([`drain`](Self::drain)) reclaims
/// them. See the module docs for the protocol and orderings.
///
/// The struct + `FOOTPRINT` are compiled unconditionally (the segment `Layout`
/// always reserves the ring's bytes); the `push`/`drain`/`at`/`init_in_place`
/// methods exist only under `alloc-xthread` (the cross-thread feature).
/// A push failed because the ring is full. The caller MUST discard the block
/// (bounded leak) — see "Overflow semantics" in the module docs.
;