Merge pull request #3260 from thierrymarianne/dealing-with-invalid-utf8-string

handle invalid UTF-8 string
This commit is contained in:
Mark Thom
2026-03-09 20:56:04 -07:00
committed by GitHub
4 changed files with 64 additions and 4 deletions

View File

@@ -68,6 +68,11 @@ ffi:FUNCTION_NAME(+InputArg1, ..., +InputArgN). % for void and bool
- When using `cstr` as an argument type the string will be deallocated once the function returns.
- When using `cstr` as a return type the string will be copied and won't be deallocated.
- When an ffi function returns bytes that are not a valid utf8-string the bytes will be turned into a list of `codes` (integers)
instead of a string (list of `chars`). Note: passing a list of `codes` is not accepted in argument position.
- In argument position you can also pass a pointer directly instead of a string,
e.g. to pass a null-pointer one can provide the integer 0 as the argument.
- In return position a null-pointer will be returned as the integer 0
## Example

View File

@@ -5125,10 +5125,28 @@ impl Machine {
unify!(self.machine_st, return_value, struct_value);
}
Value::CString(cstr) => {
let str_cell = resource_error_call_result!(
self.machine_st,
self.machine_st.heap.allocate_cstr(cstr.to_str().unwrap())
);
let str_cell = match cstr.to_str() {
Ok(valid_str) => resource_error_call_result!(
self.machine_st,
self.machine_st.heap.allocate_cstr(valid_str)
),
Err(_) => {
let cells: Vec<_> = cstr
.to_bytes()
.iter()
.map(|&b| fixnum_as_cell!(Fixnum::build_with(b)))
.collect();
resource_error_call_result!(
self.machine_st,
sized_iter_to_heap_list(
&mut self.machine_st.heap,
cells.len(),
cells.into_iter()
)
)
}
};
unify!(self.machine_st, str_cell, return_value);
}

View File

@@ -0,0 +1,16 @@
:- use_module(library(os)).
:- use_module(library(ffi)).
init :-
read(Body),
term_variables(Body, [LIB]),
Body,
use_foreign_module(LIB, [
'ffi_invalid_utf8_cstr'([], cstr)
]).
test :-
ffi:'ffi_invalid_utf8_cstr'(Str),
write(Str), nl.
:- initialization((init,test)).

View File

@@ -302,3 +302,24 @@ fn ffi_heap() {
r#"133742"#,
);
}
#[test]
#[cfg_attr(miri, ignore = "ffi")]
fn ffi_utf8_panic() {
let dynlib_path = build_dynamic_library(
"ffi_utf8_panic",
r##"
#[unsafe(no_mangle)]
extern "C" fn ffi_invalid_utf8_cstr() -> *const core::ffi::c_char {
c"Invalid\xFFUTF8".as_ptr()
}
"##,
);
load_module_test_with_input(
"tests-pl/ffi_utf8_panic.pl",
format!("LIB={dynlib_path:?}."),
// Evaluates to: 'Invalid\xFFUTF8\n'
"[73,110,118,97,108,105,100,255,85,84,70,56]\n",
);
}