Skip to content

Commit

Permalink
Fix segfault on tagged output (#175)
Browse files Browse the repository at this point in the history
Currently, record_output() segfaults when passed a pointer to a tag.
This IR, for example, segfaults on my system:

    @example = internal constant [3 x i8] c"hi\00"

    define i64 @kernel() #0 {
    ...
      call void @__quantum__rt__tuple_record_output(i64 4, ptr @example)
    ...

This appears to be because record_output() calls CString.from_raw() on a
pointer not managed by CString[1]. This commit uses CStr.from_ptr()
instead and adds some tests to catch future regressions (hopefully).

[1]: https://doc.rust-lang.org/std/ffi/struct.CString.html#method.from_raw

Co-authored-by: Stefan J. Wernli <swernli@microsoft.com>
  • Loading branch information
ausbin and swernli authored May 21, 2024
1 parent 54f2ffc commit aa5d39b
Showing 1 changed file with 24 additions and 3 deletions.
27 changes: 24 additions & 3 deletions stdlib/src/output_recording.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Licensed under the MIT License.

use std::{
ffi::{c_char, c_double, CString},
ffi::{c_char, c_double, CStr, CString},
fmt::Display,
io::{Read, Write},
};
Expand Down Expand Up @@ -118,7 +118,7 @@ pub unsafe fn record_output(ty: &str, val: &dyn Display, tag: *mut c_char) -> st
if !tag.is_null() {
output.write_all(b"\t").expect("Failed to write output");
output
.write_all(CString::from_raw(tag).as_bytes())
.write_all(CStr::from_ptr(tag).to_bytes())
.expect("Failed to write output");
}
output.write_newline();
Expand Down Expand Up @@ -268,10 +268,31 @@ mod tests {
let val: i64 = 42;
assert_untagged_output_match("ARRAY", &val, "OUTPUT\tARRAY\t42");
}
#[test]
fn test_output_bool_true_tagged_from_cstring() {
let val: bool = true;
let tag = CString::new("YEEHAW").unwrap().into_raw();
assert_output_match("BOOL", &val, tag, "OUTPUT\tBOOL\ttrue\tYEEHAW");
// Avoid memory leak
unsafe {
let _ = CString::from_raw(tag);
}
}
#[test]
fn test_output_bool_true_tagged_not_from_cstring() {
let val: bool = true;
let mut tag: [c_char; 3] = [0x68, 0x69, 0];
// With any luck, this will segfault if the tag pointer is incorrectly
// passed to CString::from_raw(). (Thankfully, it does on my system.)
assert_output_match("BOOL", &val, tag.as_mut_ptr(), "OUTPUT\tBOOL\ttrue\thi");
}
fn assert_untagged_output_match(ty: &str, val: &dyn Display, expected_str: &str) {
assert_output_match(ty, val, null_mut(), expected_str);
}
fn assert_output_match(ty: &str, val: &dyn Display, tag: *mut c_char, expected_str: &str) {
OUTPUT.with(|output| output.borrow_mut().use_std_out(false));
unsafe {
record_output(ty, &val, null_mut()).expect("Failed to write output");
record_output(ty, &val, tag).expect("Failed to write output");
}

let actual = OUTPUT.with(|output| {
Expand Down

0 comments on commit aa5d39b

Please sign in to comment.