Skip to content

Commit

Permalink
test cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
Ariel Ben-Yehuda committed Dec 3, 2024
1 parent c645629 commit 7b34f77
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 14 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ rustc-dep-of-std = ['core', 'compiler_builtins']
std = []

[profile.release]
lto = true
#lto = true

[package.metadata.docs.rs]
features = ["std"]
Expand Down
4 changes: 3 additions & 1 deletion crates/native-c/README
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
A portable native C demangler, which should mostly have byte-for-byte identical outputs to the Rust one.
A portable native C demangler, which should mostly have byte-for-byte identical outputs to the Rust one, including in error cases.

This code is intended to be safe to run on untrusted inputs and has been fuzzed, but only it's author has tried to find security issues in it so a security review is probably wise before using it as a serious security barrier.

The only difference is that since it's hard to include up-to-date unicode tables in portable C code, strings in constants (do you know that feature exists?) have all non-ASCII characters escaped (as `\u{ABCD}`) rather than having only non-printable characters escaped. Unicode in identifiers is still translated as-is, allowing non-printable characters just like rustc. If you care, the code intentionally includes `unicode_isprint` and `unicode_isgraphemextend` that can be replaced with actual Unicode tables.

Expand Down
Empty file removed crates/native-c/src/build.rs
Empty file.
68 changes: 68 additions & 0 deletions crates/native-c/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,71 @@ extern "C" {
alternate: bool,
) -> c_int;
}

#[test]
fn smoke_test() {
fn test_single(input: &str, expected: &str, alternate: bool) {
use std::ffi::{CStr, CString};

let mut buf = [0u8; 4096];
unsafe {
let mut demangle = CDemangle::zero();
let cs = CString::new(input).unwrap();
for output_len in 0..4096 {
rust_demangle_demangle(cs.as_ptr(), &mut demangle);
if rust_demangle_display_demangle(
&demangle,
buf.as_mut_ptr().cast(),
output_len,
alternate,
) != 0 {
continue; // buffer is not big enough
}
let output = CStr::from_bytes_until_nul(&buf[..])
.expect("nul")
.to_str()
.expect("utf-8");
assert_eq!(output, expected);
// test overflow margin
assert_eq!(output_len, output.len() + 4);
return;
}
panic!("overflow");
}
}
for (input, normal, alternate) in [
// test empty string
("", "", ""),
// just a path
("_RNvC6_123foo3bar", "123foo::bar", "123foo::bar"),
// more complex paths
("_RNCNCNgCs6DXkGYLi8lr_2cc5spawn00B5_", "cc[4d6468d6c9fd4bb3]::spawn::{closure#0}::{closure#0}", "cc::spawn::{closure#0}::{closure#0}"),
("_RINbNbCskIICzLVDPPb_5alloc5alloc8box_freeDINbNiB4_5boxed5FnBoxuEp6OutputuEL_ECs1iopQbuBiw2_3std", "alloc[f15a878b47eb696b]::alloc::box_free::<dyn alloc[f15a878b47eb696b]::boxed::FnBox<(), Output = ()>>", "alloc::alloc::box_free::<dyn alloc::boxed::FnBox<(), Output = ()>>"),
("_RMC0INtC8arrayvec8ArrayVechKj7b_E", "<arrayvec::ArrayVec<u8, 123usize>>", "<arrayvec::ArrayVec<u8, 123>>"),
// punycode
("_RNqCs4fqI2P2rA04_11utf8_identsu30____7hkackfecea1cbdathfdh9hlq6y", "utf8_idents[317d481089b8c8fe]::საჭმელად_გემრიელი_სადილი", "utf8_idents::საჭმელად_გემრიელი_სადილი"),
// string with non-utf8 characters
("_RIC0Kef09f908af09fa688f09fa686f09f90ae20c2a720f09f90b6f09f9192e29895f09f94a520c2a720f09fa7a1f09f929bf09f929af09f9299f09f929c_E",
"::<{*\"\\u{1f40a}\\u{1f988}\\u{1f986}\\u{1f42e} \\u{a7} \\u{1f436}\\u{1f452}\\u{2615}\\u{1f525} \\u{a7} \\u{1f9e1}\\u{1f49b}\\u{1f49a}\\u{1f499}\\u{1f49c}\"}>",
"::<{*\"\\u{1f40a}\\u{1f988}\\u{1f986}\\u{1f42e} \\u{a7} \\u{1f436}\\u{1f452}\\u{2615}\\u{1f525} \\u{a7} \\u{1f9e1}\\u{1f49b}\\u{1f49a}\\u{1f499}\\u{1f49c}\"}>"
),
// invalid syntax via backref
("_RNvNvB0_1x1y", "{invalid syntax}::x::y", "{invalid syntax}::x::y"),
// overflow via backref
("_RNvNvB1_1x1y",
"{recursion limit reached}::?::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::y",
"{recursion limit reached}::?::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::x::y",
),
// native
("_ZN9backtrace3foo17hbb467fcdaea5d79bE.llvm.A5310EB9", "backtrace::foo::hbb467fcdaea5d79b", "backtrace::foo"),
// LLVM suffix
("_RNvC6_123foo3bar.llvm.A5310EB9", "123foo::bar", "123foo::bar"),
("_ZN9backtrace3foo17hbb467fcdaea5d79bE.llvm.A5310EB9", "backtrace::foo::hbb467fcdaea5d79b", "backtrace::foo"),
// other suffix
("_RNvC6_123foo3bar.i", "123foo::bar.i", "123foo::bar.i"),
("_ZN9backtrace3foo17hbb467fcdaea5d79bE.i", "backtrace::foo::hbb467fcdaea5d79b.i", "backtrace::foo.i"),
] {
test_single(input, normal, false);
test_single(input, alternate, true);
}
}
2 changes: 1 addition & 1 deletion fuzz/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name = "rustc-demangle-fuzz"
version = "0.0.0"
authors = ["Automatically generated"]
publish = false
edition = "2018"
edition = "2021"

[package.metadata]
cargo-fuzz = true
Expand Down
35 changes: 24 additions & 11 deletions fuzz/fuzz_targets/native_c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,22 @@ fn asciify(x: &str) -> String {
result
}

fuzz_target!(|data: &[u8]| {
if data.len() == 0 {
return;
}
let alternate = data[0] % 2 == 0;
let data = &data[1..];
fn fuzz(data: &[u8], alternate: bool) {
let mut str_buf = String::with_capacity(16384);
let mut buf = [0u8; 4096];
let mut demangle = rustc_demangle_native_c::CDemangle::zero();
// We want to allow for easy overflow checking. The C output
// can be longer than the Rust output by a factor of up to *7/2,
// since e.g. an 'α' (U+03b1) in a constant string will be encoded
// as [b'\xce' b'\xb1'] in the C output (2 bytes) but as
// [b'\\' b'u' b'{' b'3' b'b' b'1' '}'] (7 bytes). The
// other factors are smaller than that (4 hex digits = 3 utf-8 bytes,
// leading to a lower expansion factor of *8/3, and so on).
//
// Also, to make the fuzzer more easily encounter overflow conditions
// in the C code, and since for most outputs the output lengths is the
// same, starting with a similar output length makes it easier.
let starting_buf_len = buf.len() / 4;
let state;
if let Ok(s) = std::str::from_utf8(data) {
if let Ok(cs) = CString::new(data) {
Expand All @@ -55,7 +62,7 @@ fuzz_target!(|data: &[u8]| {
match rustc_demangle_native_c::rust_demangle_display_demangle(
&demangle,
buf.as_mut_ptr().cast(),
buf.len() / 4,
starting_buf_len,
alternate,
) {
0 => {
Expand All @@ -77,7 +84,7 @@ fuzz_target!(|data: &[u8]| {
str_buf.clear();
let fmt_buf = &mut FormatBuf {
buf: &mut str_buf,
max_len: buf.len() / 4 - 4,
max_len: starting_buf_len - 4,
};
let rust_overflowed = if alternate {
write!(fmt_buf, "{:#}", rdemangle)
Expand Down Expand Up @@ -105,21 +112,21 @@ fuzz_target!(|data: &[u8]| {
}
};
assert_eq!(asciify(&str_buf), asciify(c_demangled));
if c_demangled.len() < buf.len() / 4 - 3 {
if c_demangled.len() < starting_buf_len - 3 {
panic!(
"spurious overflow {} {:?} {:?} {:?} {}",
c_demangled.len(),
alternate,
asciify(&str_buf),
asciify(c_demangled),
buf.len() / 4
starting_buf_len
)
}
}
State::Ok(demangled) => {
let fmt_buf = &mut FormatBuf {
buf: &mut str_buf,
max_len: buf.len() / 4 - 4,
max_len: starting_buf_len - 4,
};
let rust_overflowed = if alternate {
write!(fmt_buf, "{:#}", rdemangle)
Expand All @@ -137,4 +144,10 @@ fuzz_target!(|data: &[u8]| {
}
}
}
}

fuzz_target!(|data: &[u8]| {
// fuzz both normal and alternate modes.
fuzz(data, false);
fuzz(data, true);
});

0 comments on commit 7b34f77

Please sign in to comment.