mirror of
https://github.com/logos-messaging/nim-ffi.git
synced 2026-05-17 07:39:34 +00:00
Fix Rust callbacks dereference msg without a null check (rust.nim:272, 317). CStr::from_ptr(msg) is UB if msg == nullptr. The C++ side guards with msg ? ... : "". The Nim side
appears to always pass a non-null pointer, but soundness across an FFI boundary shouldn't hinge on a producer's discipline — the Rust receiver should null-check. Especially since the C ABI signature here is the one downstream consumers will rely on indefinitely.
This commit is contained in:
parent
2f6ef08e6c
commit
24a56032af
@ -12,6 +12,17 @@ struct FfiCallbackResult {
|
||||
|
||||
type Pair = Arc<(Mutex<FfiCallbackResult>, Condvar)>;
|
||||
|
||||
/// Null-safe conversion from a C string returned by the Nim side.
|
||||
/// The C ABI permits a null `msg` even though the current producer
|
||||
/// always passes a non-null pointer; treat null as an empty string.
|
||||
unsafe fn cstr_to_string(msg: *const c_char) -> String {
|
||||
if msg.is_null() {
|
||||
String::new()
|
||||
} else {
|
||||
CStr::from_ptr(msg).to_string_lossy().into_owned()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn on_result(
|
||||
ret: c_int,
|
||||
msg: *const c_char,
|
||||
@ -21,11 +32,8 @@ unsafe extern "C" fn on_result(
|
||||
let pair = Arc::from_raw(user_data as *const (Mutex<FfiCallbackResult>, Condvar));
|
||||
let (lock, cvar) = &*pair;
|
||||
let mut state = lock.lock().unwrap();
|
||||
state.payload = Some(if ret == 0 {
|
||||
Ok(CStr::from_ptr(msg).to_string_lossy().into_owned())
|
||||
} else {
|
||||
Err(CStr::from_ptr(msg).to_string_lossy().into_owned())
|
||||
});
|
||||
let s = cstr_to_string(msg);
|
||||
state.payload = Some(if ret == 0 { Ok(s) } else { Err(s) });
|
||||
cvar.notify_one();
|
||||
}
|
||||
|
||||
@ -60,11 +68,8 @@ unsafe extern "C" fn on_result_async(
|
||||
let tx = Box::from_raw(
|
||||
user_data as *mut tokio::sync::oneshot::Sender<Result<String, String>>,
|
||||
);
|
||||
let value = if ret == 0 {
|
||||
Ok(CStr::from_ptr(msg).to_string_lossy().into_owned())
|
||||
} else {
|
||||
Err(CStr::from_ptr(msg).to_string_lossy().into_owned())
|
||||
};
|
||||
let s = cstr_to_string(msg);
|
||||
let value = if ret == 0 { Ok(s) } else { Err(s) };
|
||||
let _ = tx.send(value);
|
||||
}
|
||||
|
||||
|
||||
@ -258,6 +258,17 @@ proc generateApiRs*(procs: seq[FFIProcMeta], libName: string): string =
|
||||
lines.add("")
|
||||
lines.add("type Pair = Arc<(Mutex<FfiCallbackResult>, Condvar)>;")
|
||||
lines.add("")
|
||||
lines.add("/// Null-safe conversion from a C string returned by the Nim side.")
|
||||
lines.add("/// The C ABI permits a null `msg` even though the current producer")
|
||||
lines.add("/// always passes a non-null pointer; treat null as an empty string.")
|
||||
lines.add("unsafe fn cstr_to_string(msg: *const c_char) -> String {")
|
||||
lines.add(" if msg.is_null() {")
|
||||
lines.add(" String::new()")
|
||||
lines.add(" } else {")
|
||||
lines.add(" CStr::from_ptr(msg).to_string_lossy().into_owned()")
|
||||
lines.add(" }")
|
||||
lines.add("}")
|
||||
lines.add("")
|
||||
lines.add("unsafe extern \"C\" fn on_result(")
|
||||
lines.add(" ret: c_int,")
|
||||
lines.add(" msg: *const c_char,")
|
||||
@ -267,11 +278,8 @@ proc generateApiRs*(procs: seq[FFIProcMeta], libName: string): string =
|
||||
lines.add(" let pair = Arc::from_raw(user_data as *const (Mutex<FfiCallbackResult>, Condvar));")
|
||||
lines.add(" let (lock, cvar) = &*pair;")
|
||||
lines.add(" let mut state = lock.lock().unwrap();")
|
||||
lines.add(" state.payload = Some(if ret == 0 {")
|
||||
lines.add(" Ok(CStr::from_ptr(msg).to_string_lossy().into_owned())")
|
||||
lines.add(" } else {")
|
||||
lines.add(" Err(CStr::from_ptr(msg).to_string_lossy().into_owned())")
|
||||
lines.add(" });")
|
||||
lines.add(" let s = cstr_to_string(msg);")
|
||||
lines.add(" state.payload = Some(if ret == 0 { Ok(s) } else { Err(s) });")
|
||||
lines.add(" cvar.notify_one();")
|
||||
lines.add("}")
|
||||
lines.add("")
|
||||
@ -311,11 +319,8 @@ proc generateApiRs*(procs: seq[FFIProcMeta], libName: string): string =
|
||||
lines.add(" let tx = Box::from_raw(")
|
||||
lines.add(" user_data as *mut tokio::sync::oneshot::Sender<Result<String, String>>,")
|
||||
lines.add(" );")
|
||||
lines.add(" let value = if ret == 0 {")
|
||||
lines.add(" Ok(CStr::from_ptr(msg).to_string_lossy().into_owned())")
|
||||
lines.add(" } else {")
|
||||
lines.add(" Err(CStr::from_ptr(msg).to_string_lossy().into_owned())")
|
||||
lines.add(" };")
|
||||
lines.add(" let s = cstr_to_string(msg);")
|
||||
lines.add(" let value = if ret == 0 { Ok(s) } else { Err(s) };")
|
||||
lines.add(" let _ = tx.send(value);")
|
||||
lines.add("}")
|
||||
lines.add("")
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user