fix(raft-drive-async): endpoint-quiescence for bug-1197 vote-ep teardown (task 3.4a)
When the (still-default-OFF) peer-control dispatch flag is ON, raft_cb_become_follower runs as a vtask pool worker and frees every peer vote-ep while other pool workers may still hold those eps' data — a UAF (bug-1197). This makes that path quiescence-safe so the flag can later be flipped ON (deferred to the 4.x prod-flip gate; production stays OFF here). Mechanism (no libxce-wide endpoint refcount; reuses the existing per-ep FIFO + busy serialization): - Deferred teardown-as-vtask: the loser loop, when dispatch is ON and the pool is wired, does per vote-ep endpoint_mark_tearing_down -> iomgr_unmonitor -> enqueue a VTASK_IO/VTASK_LIVENESS teardown vtask bound to that ep. Per-ep busy FIFO makes it run strictly after any in-flight/queued same-ep peer-control vtask; the body NULLs iod->ep before endpoint_fini+free(ep) to disarm run_task's post-body endpoint_unblock_tasks (which would touch the freed ep->lock). OFF / pool-unwired path is byte-for-byte the legacy endpoint_fini. - RCU-like drain closes the dispatch/teardown TOCTOU window: new _Atomic int endpoint_t.dispatch_inflight (+ tearing_down:1 bit). The ON dispatch brackets its flag-check+enqueue with fetch_add/fetch_sub (seq_cst); endpoint_mark_tearing_down sets the flag under ep->lock, releases, then spins until dispatch_inflight drains to 0. So every dispatch that read the flag false finishes its enqueue before mark returns (FIFO-before the teardown vtask); any later dispatch reads the flag set and drops the packet. ep->lock is deliberately NOT held across the enqueue (it inverts with the taskq lock in vtask_eligible); the counter is the lock-free handshake. Drain is bounded: the held window is just a calloc + non-blocking enqueue on the single io_worker. Still open before the production flag flip (4.x): the borrowed-ep race in snapshot_lane_fn/configpush_lane_fn is distinct (slowlane thread vs io_worker RDHUP teardown, not the vtask-pool FIFO) and needs its own per-standby-ep discipline. Tests: test_ha_vep_teardown.c — 5 ASan tests (ordering, late-event drop, run_task unblock disarm, OFF-path unchanged, deterministic mark-drains-inflight-dispatch). xcelib_test 282/0; controller_unit_test EXIT=0; ASan-clean. Adversarial cpp-review: no CRITICAL/HIGH; handshake formally sound, flag-ON path race-free. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S6fDCtXkkEgupu7rUah1y2
This commit is contained in:
parent
bd02064ebc
commit
6d510d8cf4
10 changed files with 1037 additions and 10 deletions
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/epoll.h>
|
||||
|
|
@ -72,7 +73,14 @@ typedef struct endpoint_t {
|
|||
int idx;
|
||||
int flags;
|
||||
unsigned int busy : 1;
|
||||
unsigned int tearing_down : 1;
|
||||
unsigned int ssl_handshake_done : 1;
|
||||
/* bug-1197: count of peer-control dispatch attempts currently between the
|
||||
* tearing_down flag-read and the completion of their pool enqueue. Bitfields
|
||||
* cannot be _Atomic, so this is a full atomic int. endpoint_mark_tearing_down
|
||||
* drains this to 0 (after setting the flag) so no dispatch can enqueue a
|
||||
* same-ep vtask after teardown begins. */
|
||||
_Atomic int dispatch_inflight;
|
||||
dataq_t outq;
|
||||
uint64_t seq;
|
||||
conn_handler_t conn_handler;
|
||||
|
|
@ -130,6 +138,11 @@ extern uint64_t endpoint_getseq(endpoint_t*);
|
|||
extern void endpoint_block_tasks(endpoint_t*);
|
||||
extern void endpoint_unblock_tasks(endpoint_t*);
|
||||
extern int endpoint_is_task_blocked(endpoint_t*);
|
||||
/* bug-1197: mark an endpoint as undergoing teardown so a late same-batch event
|
||||
* delivered by the io_worker after iomgr_unmonitor is dropped instead of
|
||||
* dispatched onto the vtask pool (which could outlive the ep). */
|
||||
extern void endpoint_mark_tearing_down(endpoint_t*);
|
||||
extern int endpoint_is_tearing_down(endpoint_t*);
|
||||
extern int endpoint_set_owner(endpoint_t*, void*);
|
||||
extern int endpoint_set_msg_handler(endpoint_t*, msg_handler_t);
|
||||
extern int endpoint_set_err_handler(endpoint_t*, err_handler_t);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue