Skip to content

Commit c6af942

Browse files
fix: free retrieval hint with libc free, not deallocShared (SIGSEGV)
The retrieval-hint provider (the Go binding's C.CBytes) allocates the returned hint with malloc and hands us ownership. The hand-written hint closure freed it with Nim's deallocShared, corrupting Nim's shared-heap allocator and crashing with a SIGSEGV in deallocBigChunk/avltree.del once wrapOutgoingMessage starts invoking the provider per causal-history entry. Free it with libc free instead. The hint provider is hand-written (a C function pointer has no JSON form for nim-ffi to marshal), so this free is nim-sds's responsibility on every line. Reproduced end-to-end via the C ABI on release/v0.3: buggy build segfaults (exit 139), fixed build completes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 884ce6f commit c6af942

1 file changed

Lines changed: 38 additions & 12 deletions

File tree

library/libsds.nim

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
import std/[strutils, sequtils, json, base64, locks]
22
import ffi
33
import sds
4-
import ./events/[
5-
json_message_ready_event, json_message_sent_event, json_missing_dependencies_event,
6-
json_periodic_sync_event, json_repair_ready_event,
7-
]
4+
import
5+
./events/[
6+
json_message_ready_event, json_message_sent_event, json_missing_dependencies_event,
7+
json_periodic_sync_event, json_repair_ready_event,
8+
]
9+
10+
# Frees memory the retrieval-hint provider allocated with malloc (the Go
11+
# binding's C.CBytes). Must match that allocator — Nim's deallocShared here
12+
# corrupts Nim's own heap.
13+
proc c_free(p: pointer) {.importc: "free", header: "<stdlib.h>".}
814

915
# Emit the library bootstrap: the {.exported.}/{.callback.} pragmas, the
1016
# `-fPIC`/soname linker flags, the `libsdsNimMain` import and the
@@ -160,7 +166,10 @@ proc onRetrievalHint(ctx: ptr FFIContext[ReliabilityManager]): RetrievalHintProv
160166
if not hint.isNil() and hintLen > 0:
161167
var hintBytes = newSeq[byte](hintLen)
162168
copyMem(addr hintBytes[0], hint, hintLen)
163-
deallocShared(hint)
169+
# The provider allocates `hint` with malloc (the Go binding's C.CBytes)
170+
# and hands us ownership; free it with libc free. Nim's deallocShared here
171+
# corrupts Nim's allocator (SIGSEGV in deallocBigChunk/avltree).
172+
c_free(hint)
164173
return hintBytes
165174

166175
return @[]
@@ -179,8 +188,12 @@ registerReqFFI(SdsCreateRmReq, ctx: ptr FFIContext[ReliabilityManager]):
179188
return err("Failed creating reliability manager: " & $error)
180189

181190
await rm.setCallbacks(
182-
onMessageReady(ctx), onMessageSent(ctx), onMissingDependencies(ctx),
183-
onPeriodicSync(ctx), onRetrievalHint(ctx), onRepairReady(ctx),
191+
onMessageReady(ctx),
192+
onMessageSent(ctx),
193+
onMissingDependencies(ctx),
194+
onPeriodicSync(ctx),
195+
onRetrievalHint(ctx),
196+
onRepairReady(ctx),
184197
)
185198

186199
ctx.myLib[] = rm
@@ -299,7 +312,9 @@ proc SdsNewReliabilityManager(
299312

300313
let sendRes =
301314
try:
302-
ffi_context.sendRequestToFFIThread(ctx, SdsCreateRmReq.ffiNewReq(callback, userData))
315+
ffi_context.sendRequestToFFIThread(
316+
ctx, SdsCreateRmReq.ffiNewReq(callback, userData)
317+
)
303318
except Exception as exc:
304319
Result[void, string].err("sendRequestToFFIThread exception: " & exc.msg)
305320
if sendRes.isErr():
@@ -398,7 +413,9 @@ proc SdsWrapOutgoingMessage(
398413

399414
let sharedMsg = copyToSharedSeqByte(message, messageLen.int)
400415
dispatchReq(
401-
ctx, callback, userData,
416+
ctx,
417+
callback,
418+
userData,
402419
SdsWrapMessageReq.ffiNewReq(callback, userData, sharedMsg, messageId, channelId),
403420
)
404421

@@ -421,7 +438,12 @@ proc SdsUnwrapReceivedMessage(
421438
return RET_ERR
422439

423440
let sharedMsg = copyToSharedSeqByte(message, messageLen.int)
424-
dispatchReq(ctx, callback, userData, SdsUnwrapMessageReq.ffiNewReq(callback, userData, sharedMsg))
441+
dispatchReq(
442+
ctx,
443+
callback,
444+
userData,
445+
SdsUnwrapMessageReq.ffiNewReq(callback, userData, sharedMsg),
446+
)
425447

426448
proc SdsMarkDependenciesMet(
427449
ctx: ptr FFIContext[ReliabilityManager],
@@ -454,7 +476,9 @@ proc SdsMarkDependenciesMet(
454476

455477
let sharedIds = copyToSharedSeqCstr(messageIds, count.int)
456478
dispatchReq(
457-
ctx, callback, userData,
479+
ctx,
480+
callback,
481+
userData,
458482
SdsMarkDepsReq.ffiNewReq(callback, userData, sharedIds, channelId),
459483
)
460484

@@ -466,4 +490,6 @@ proc SdsStartPeriodicTasks(
466490
return RET_ERR
467491
if isNil(callback):
468492
return RET_MISSING_CALLBACK
469-
dispatchReq(ctx, callback, userData, SdsStartPeriodicTasksReq.ffiNewReq(callback, userData))
493+
dispatchReq(
494+
ctx, callback, userData, SdsStartPeriodicTasksReq.ffiNewReq(callback, userData)
495+
)

0 commit comments

Comments
 (0)