PINK: fix ctypes dangling-pointer + venue.submit guard
Two bugs causing INVALID_INTENT_PARSE at FFI boundary: 1. Dangling pointer: ctypes.c_char_p stores a raw C pointer without incrementing the Python refcount. Temporaries passed inline are freed by CPython before the Rust FFI call executes, giving Rust a dangling pointer whose freed memory looks like truncated JSON (column 41). Fix: assign bytes to local vars (_pb/_mb/_vb) to hold refs alive. 2. venue.submit guard: process_intent() called venue.submit() even when the kernel returned INVALID_INTENT, cascading a 30s BingX timeout into a fatal crash. Fix: gate on outcome.accepted. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -231,11 +231,18 @@ class _RustKernelLib:
|
|||||||
mode: str,
|
mode: str,
|
||||||
verbosity: str,
|
verbosity: str,
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
|
# Keep local refs so CPython's ref-count doesn't free the bytes objects
|
||||||
|
# before the Rust FFI call completes. ctypes.c_char_p stores a raw pointer
|
||||||
|
# without incrementing the Python refcount; a temporary would be freed
|
||||||
|
# after c_char_p() returns, giving Rust a dangling pointer.
|
||||||
|
_pb = _to_rust_bytes(payload)
|
||||||
|
_mb = mode.encode("ascii")
|
||||||
|
_vb = verbosity.encode("ascii")
|
||||||
raw = self.lib.dita_kernel_process_intent_json(
|
raw = self.lib.dita_kernel_process_intent_json(
|
||||||
handle,
|
handle,
|
||||||
ctypes.c_char_p(_to_rust_bytes(payload)),
|
ctypes.c_char_p(_pb),
|
||||||
ctypes.c_char_p(mode.encode("ascii")),
|
ctypes.c_char_p(_mb),
|
||||||
ctypes.c_char_p(verbosity.encode("ascii")),
|
ctypes.c_char_p(_vb),
|
||||||
)
|
)
|
||||||
return json.loads(self._take_string(raw))
|
return json.loads(self._take_string(raw))
|
||||||
|
|
||||||
@@ -247,11 +254,14 @@ class _RustKernelLib:
|
|||||||
mode: str,
|
mode: str,
|
||||||
verbosity: str,
|
verbosity: str,
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
|
_pb = _to_rust_bytes(payload)
|
||||||
|
_mb = mode.encode("ascii")
|
||||||
|
_vb = verbosity.encode("ascii")
|
||||||
raw = self.lib.dita_kernel_on_venue_event_json(
|
raw = self.lib.dita_kernel_on_venue_event_json(
|
||||||
handle,
|
handle,
|
||||||
ctypes.c_char_p(_to_rust_bytes(payload)),
|
ctypes.c_char_p(_pb),
|
||||||
ctypes.c_char_p(mode.encode("ascii")),
|
ctypes.c_char_p(_mb),
|
||||||
ctypes.c_char_p(verbosity.encode("ascii")),
|
ctypes.c_char_p(_vb),
|
||||||
)
|
)
|
||||||
return json.loads(self._take_string(raw))
|
return json.loads(self._take_string(raw))
|
||||||
|
|
||||||
@@ -263,11 +273,14 @@ class _RustKernelLib:
|
|||||||
mode: str,
|
mode: str,
|
||||||
verbosity: str,
|
verbosity: str,
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
|
_pb = _to_rust_bytes(list(payload))
|
||||||
|
_mb = mode.encode("ascii")
|
||||||
|
_vb = verbosity.encode("ascii")
|
||||||
raw = self.lib.dita_kernel_reconcile_slots_json(
|
raw = self.lib.dita_kernel_reconcile_slots_json(
|
||||||
handle,
|
handle,
|
||||||
ctypes.c_char_p(_to_rust_bytes(list(payload))),
|
ctypes.c_char_p(_pb),
|
||||||
ctypes.c_char_p(mode.encode("ascii")),
|
ctypes.c_char_p(_mb),
|
||||||
ctypes.c_char_p(verbosity.encode("ascii")),
|
ctypes.c_char_p(_vb),
|
||||||
)
|
)
|
||||||
return json.loads(self._take_string(raw))
|
return json.loads(self._take_string(raw))
|
||||||
|
|
||||||
@@ -294,7 +307,8 @@ class _RustKernelLib:
|
|||||||
def on_account_event(
|
def on_account_event(
|
||||||
self, handle: ctypes.c_void_p, event: Dict[str, Any]
|
self, handle: ctypes.c_void_p, event: Dict[str, Any]
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
raw = self.lib.dita_kernel_on_account_event_json(handle, ctypes.c_char_p(_to_rust_bytes(event)))
|
_eb = _to_rust_bytes(event)
|
||||||
|
raw = self.lib.dita_kernel_on_account_event_json(handle, ctypes.c_char_p(_eb))
|
||||||
if not raw:
|
if not raw:
|
||||||
return {}
|
return {}
|
||||||
return json.loads(self._take_string(raw))
|
return json.loads(self._take_string(raw))
|
||||||
@@ -761,7 +775,7 @@ class ExecutionKernel:
|
|||||||
self._last_settled_pnl[intent.slot_id] = 0.0
|
self._last_settled_pnl[intent.slot_id] = 0.0
|
||||||
emitted_events = []
|
emitted_events = []
|
||||||
all_venue_transitions: List[KernelTransition] = []
|
all_venue_transitions: List[KernelTransition] = []
|
||||||
if intent.action in {KernelCommandType.ENTER, KernelCommandType.EXIT}:
|
if outcome.accepted and intent.action in {KernelCommandType.ENTER, KernelCommandType.EXIT}:
|
||||||
emitted_events = self.venue.submit(intent)
|
emitted_events = self.venue.submit(intent)
|
||||||
for event in emitted_events:
|
for event in emitted_events:
|
||||||
evt_outcome = self.on_venue_event(event)
|
evt_outcome = self.on_venue_event(event)
|
||||||
|
|||||||
Reference in New Issue
Block a user