fix(controller): stop SIGSEGV in WalCatchupEntry capnp encode (bug-1505)
controller_unit_test crashed deterministically in write_WalCatchupEntry (test_wal_catchup_append_term_fields_round_trip) -- the same bug class as bug-1504 (SnapshotData.internalState): this codebase's capnpc-c backend silently no-ops Data-field encoding in generated encode_XXX() composite helpers without zero-initializing the caller's local struct first, leaving an uninitialized capn_ptr written onto the wire. Confirmed zero production exposure before fixing: xce_wal_batch_encode_ raw()/wal_entry_to_wire() (request.c) never call the generic encode_WalCatchupEntry() at all -- they memset() their own struct and call write_WalCatchupEntry() directly with every field hand-set, so this was 100% test-only exposure (grepped every caller of the generic encode/decode pair; only the crashing test uses it). Fix mirrors bug-1504: WalCatchupEntry.payload (@2) and batchPayloads (@10) switched from Data to List(UInt8) in controller.capnp (new batchPayloadsLen @11 companion; payload reuses the existing wirePayloadLen @9), typemap.h's wal_catchup_entry_t field types updated, and request.c's extract_data_bytes() widened to take a raw capn_ptr (it already only used .p internally) so its call sites work regardless of which Data-shaped wrapper the generated accessor returns now. batchPayloads' single-top-level-blob shape confirmed this does not reintroduce bug-847's per-element cross-segment far-pointer loss -- that issue is inherent to per-element data regardless of Data vs List(UInt8) spelling. Strengthened test_wal_catchup_append_term_fields_round_trip with real payload/batchPayloads content assertions -- previously never exercised, since the test's src fields were left NULL. Verified: controller suite 3/3 (was 2/3, crashing), scheduler suite unaffected at 37/37 (unrelated schema area). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JPPXeKSUXUqcqvzNXY1rzJ
This commit is contained in:
parent
829fbe41dc
commit
1fd467d072
7 changed files with 121 additions and 22 deletions
|
|
@ -954,7 +954,29 @@ struct SnapshotTransfer $C.mapname("snapshot_transfer_t") {
|
|||
struct WalCatchupEntry $C.mapname("wal_catchup_entry_t") {
|
||||
lsn @0 :UInt64 $C.mapname("lsn");
|
||||
entryType @1 :UInt8 $C.mapname("entry_type");
|
||||
payload @2 :Data $C.mapname("payload");
|
||||
# payload is List(UInt8), NOT Data (bug-1505 fix, same class as bug-1504 on
|
||||
# SnapshotData.internalState -- see that struct's own comment for the full
|
||||
# root cause: this codebase's capnpc-c backend silently no-ops every Data
|
||||
# field's encoding in generated encode_XXX() helpers without zero-
|
||||
# initializing the caller's local struct first, leaving a genuinely
|
||||
# uninitialized capn_ptr written onto the wire -- a real, reproducible
|
||||
# SIGSEGV in write_WalCatchupEntry, hit by
|
||||
# test_wal_catchup_append_term_fields_round_trip even though that test
|
||||
# never touches payload content at all (src.payload stays NULL -- the
|
||||
# crash is from the OUTPUT struct's uninitialized field, independent of
|
||||
# the input). List(UInt8) gets a real generated encode/decode from this
|
||||
# same backend (proven precedent: FetchFileReply.content/bytesRead), so
|
||||
# no hand-written accessor is needed. Reuses wirePayloadLen (@9, below)
|
||||
# as its maplistcount companion -- already exactly "this sub-entry's
|
||||
# payload byte count" for the bug-847 slicing use below, same value.
|
||||
# NOTE: production (xce_wal_batch_encode_raw/wal_entry_to_wire, request.c)
|
||||
# never goes through this generic encode_WalCatchupEntry() path at all --
|
||||
# it memset()s its own struct and calls write_WalCatchupEntry() directly
|
||||
# with every field hand-set (payload deliberately left at its post-memset
|
||||
# zero value, per bug-847's own comment below) -- so this fix has zero
|
||||
# production behavior impact, it only stops the generic codec (used by
|
||||
# this one test) from crashing.
|
||||
payload @2 :List(UInt8) $C.mapname("payload") $C.maplistcount("wire_payload_len");
|
||||
entries @3 :List(WalCatchupEntry) $C.mapname("entries") $C.maplistcount("n_entries");
|
||||
# Raft AppendEntries term fields (ha-term-safety task 1.1, Req 1.3/1.4).
|
||||
# This envelope is the replication-carrying message (WAL catch-up); the four
|
||||
|
|
@ -996,8 +1018,25 @@ struct WalCatchupEntry $C.mapname("wal_catchup_entry_t") {
|
|||
# n_entries (0/1/N); sub-entries no longer carry their own @2 payload on
|
||||
# this path (kept for any other/legacy sender; decode still handles it
|
||||
# defensively as a fallback when batchPayloads is absent).
|
||||
#
|
||||
# NOTE (bug-1505 fix, same as `payload` above): this note's own "cross-
|
||||
# segment loss" concern is about scattered PER-ELEMENT Data (or
|
||||
# List(UInt8) -- identical wire representation either way in this
|
||||
# codegen; the Data vs List(UInt8) schema keyword only changes whether
|
||||
# THIS backend's generic encode_XXX() composite helper populates the
|
||||
# field automatically, not the underlying wire type). batchPayloads
|
||||
# itself is the single TOP-LEVEL blob this fix already routes around that
|
||||
# exact problem with -- so switching ITS OWN keyword to List(UInt8) does
|
||||
# not reintroduce bug-847's issue; it only stops the generic codec's
|
||||
# SnapshotData.internalState-class no-op/uninitialized-capn_ptr crash
|
||||
# (bug-1504) for this field too. batchPayloadsLen (@11, below) is its
|
||||
# maplistcount companion, distinct from raw_batch_payloads_len
|
||||
# (typemap.h) -- that field is populated by request.c's OWN hand-rolled
|
||||
# xce_decode() extraction (a completely separate, already-correct code
|
||||
# path this fix does not touch), not by this generic decode.
|
||||
wirePayloadLen @9 :UInt32 $C.mapname("wire_payload_len");
|
||||
batchPayloads @10 :Data $C.mapname("batch_payloads");
|
||||
batchPayloads @10 :List(UInt8) $C.mapname("batch_payloads") $C.maplistcount("batch_payloads_wire_len");
|
||||
batchPayloadsLen @11 :UInt32 $C.mapname("batch_payloads_wire_len");
|
||||
}
|
||||
|
||||
struct CtrlHelloRequest $C.mapname("ctrl_hello_req_t") {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue