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
|
|
@ -56,16 +56,25 @@ int xce_encode(xce_request_t* req, io_packet_t* pkt)
|
|||
return 1;
|
||||
}
|
||||
|
||||
/* extract_data_bytes: copy a capnp Data (List(UInt8)) field into a fresh
|
||||
/* extract_data_bytes: copy a capnp Data/List(UInt8) field into a fresh
|
||||
* malloc'd buffer. Returns NULL (*out_len = 0) for absent/empty fields or
|
||||
* allocation failure. Used by xce_decode() to pre-extract raw payload
|
||||
* bytes before capn_free() destroys the segment. */
|
||||
static uint8_t *extract_data_bytes(capn_data dp, size_t *out_len)
|
||||
* bytes before capn_free() destroys the segment.
|
||||
*
|
||||
* Takes a raw capn_ptr rather than capn_data/capn_list8 (bug-1505 fix):
|
||||
* both wrapper types are a single `.p` field with identical layout — this
|
||||
* function already only ever touched `.p` — so accepting the shared
|
||||
* underlying type lets every call site pass either wrapper's `.p` directly,
|
||||
* regardless of whether the source schema field is spelled `Data` (still
|
||||
* SnapshotTransfer.payload) or `List(UInt8)` (WalCatchupEntry.payload/
|
||||
* batchPayloads, converted from Data by bug-1505's schema fix — see
|
||||
* controller.capnp's WalCatchupEntry comment for why). */
|
||||
static uint8_t *extract_data_bytes(capn_ptr p, size_t *out_len)
|
||||
{
|
||||
*out_len = 0;
|
||||
|
||||
capn_list8 l;
|
||||
l.p = dp.p;
|
||||
l.p = p;
|
||||
if (l.p.type == CAPN_NULL)
|
||||
return NULL;
|
||||
|
||||
|
|
@ -199,7 +208,7 @@ int xce_decode(io_packet_t* pkt, xce_request_t* req)
|
|||
|
||||
/* Top-level payload (single-entry mode) */
|
||||
batch->raw_payload =
|
||||
extract_data_bytes(WalCatchupEntry_get_payload(b_ptr),
|
||||
extract_data_bytes(WalCatchupEntry_get_payload(b_ptr).p,
|
||||
&batch->raw_payload_len);
|
||||
|
||||
/* bug-847: the top-level batch_payloads blob — every sub-entry's
|
||||
|
|
@ -208,7 +217,7 @@ int xce_decode(io_packet_t* pkt, xce_request_t* req)
|
|||
* exhibit the per-element cross-segment pointer loss that scattered
|
||||
* per-sub-entry Data blobs did. */
|
||||
batch->raw_batch_payloads =
|
||||
extract_data_bytes(WalCatchupEntry_get_batchPayloads(b_ptr),
|
||||
extract_data_bytes(WalCatchupEntry_get_batchPayloads(b_ptr).p,
|
||||
&batch->raw_batch_payloads_len);
|
||||
|
||||
/* ha-term-safety task 3.2: the top-level container's AppendEntries
|
||||
|
|
@ -249,7 +258,7 @@ int xce_decode(io_packet_t* pkt, xce_request_t* req)
|
|||
}
|
||||
} else {
|
||||
batch->entries[i]->raw_payload =
|
||||
extract_data_bytes(we.payload,
|
||||
extract_data_bytes(we.payload.p,
|
||||
&batch->entries[i]->raw_payload_len);
|
||||
}
|
||||
}
|
||||
|
|
@ -263,7 +272,7 @@ int xce_decode(io_packet_t* pkt, xce_request_t* req)
|
|||
snapshot_transfer_t *chunk = req->body.snapshottransfermsg;
|
||||
SnapshotTransfer_ptr s_ptr = rq.body.snapshottransfermsg;
|
||||
chunk->raw_payload =
|
||||
extract_data_bytes(SnapshotTransfer_get_payload(s_ptr),
|
||||
extract_data_bytes(SnapshotTransfer_get_payload(s_ptr).p,
|
||||
&chunk->raw_payload_len);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue