Skip to content

Commit 7b34f77

Browse files
author
Ariel Ben-Yehuda
committed
test cleanup
1 parent c645629 commit 7b34f77

File tree

6 files changed

+97
-14
lines changed

6 files changed

+97
-14
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ rustc-dep-of-std = ['core', 'compiler_builtins']
2323
std = []
2424

2525
[profile.release]
26-
lto = true
26+
#lto = true
2727

2828
[package.metadata.docs.rs]
2929
features = ["std"]

crates/native-c/README

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
A portable native C demangler, which should mostly have byte-for-byte identical outputs to the Rust one.
1+
A portable native C demangler, which should mostly have byte-for-byte identical outputs to the Rust one, including in error cases.
2+
3+
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.
24

35
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.
46

crates/native-c/src/build.rs

Whitespace-only changes.

crates/native-c/src/lib.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,71 @@ extern "C" {
4242
alternate: bool,
4343
) -> c_int;
4444
}
45+
46+
#[test]
47+
fn smoke_test() {
48+
fn test_single(input: &str, expected: &str, alternate: bool) {
49+
use std::ffi::{CStr, CString};
50+
51+
let mut buf = [0u8; 4096];
52+
unsafe {
53+
let mut demangle = CDemangle::zero();
54+
let cs = CString::new(input).unwrap();
55+
for output_len in 0..4096 {
56+
rust_demangle_demangle(cs.as_ptr(), &mut demangle);
57+
if rust_demangle_display_demangle(
58+
&demangle,
59+
buf.as_mut_ptr().cast(),
60+
output_len,
61+
alternate,
62+
) != 0 {
63+
continue; // buffer is not big enough
64+
}
65+
let output = CStr::from_bytes_until_nul(&buf[..])
66+
.expect("nul")
67+
.to_str()
68+
.expect("utf-8");
69+
assert_eq!(output, expected);
70+
// test overflow margin
71+
assert_eq!(output_len, output.len() + 4);
72+
return;
73+
}
74+
panic!("overflow");
75+
}
76+
}
77+
for (input, normal, alternate) in [
78+
// test empty string
79+
("", "", ""),
80+
// just a path
81+
("_RNvC6_123foo3bar", "123foo::bar", "123foo::bar"),
82+
// more complex paths
83+
("_RNCNCNgCs6DXkGYLi8lr_2cc5spawn00B5_", "cc[4d6468d6c9fd4bb3]::spawn::{closure#0}::{closure#0}", "cc::spawn::{closure#0}::{closure#0}"),
84+
("_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 = ()>>"),
85+
("_RMC0INtC8arrayvec8ArrayVechKj7b_E", "<arrayvec::ArrayVec<u8, 123usize>>", "<arrayvec::ArrayVec<u8, 123>>"),
86+
// punycode
87+
("_RNqCs4fqI2P2rA04_11utf8_identsu30____7hkackfecea1cbdathfdh9hlq6y", "utf8_idents[317d481089b8c8fe]::საჭმელად_გემრიელი_სადილი", "utf8_idents::საჭმელად_გემრიელი_სადილი"),
88+
// string with non-utf8 characters
89+
("_RIC0Kef09f908af09fa688f09fa686f09f90ae20c2a720f09f90b6f09f9192e29895f09f94a520c2a720f09fa7a1f09f929bf09f929af09f9299f09f929c_E",
90+
"::<{*\"\\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}\"}>",
91+
"::<{*\"\\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}\"}>"
92+
),
93+
// invalid syntax via backref
94+
("_RNvNvB0_1x1y", "{invalid syntax}::x::y", "{invalid syntax}::x::y"),
95+
// overflow via backref
96+
("_RNvNvB1_1x1y",
97+
"{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",
98+
"{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",
99+
),
100+
// native
101+
("_ZN9backtrace3foo17hbb467fcdaea5d79bE.llvm.A5310EB9", "backtrace::foo::hbb467fcdaea5d79b", "backtrace::foo"),
102+
// LLVM suffix
103+
("_RNvC6_123foo3bar.llvm.A5310EB9", "123foo::bar", "123foo::bar"),
104+
("_ZN9backtrace3foo17hbb467fcdaea5d79bE.llvm.A5310EB9", "backtrace::foo::hbb467fcdaea5d79b", "backtrace::foo"),
105+
// other suffix
106+
("_RNvC6_123foo3bar.i", "123foo::bar.i", "123foo::bar.i"),
107+
("_ZN9backtrace3foo17hbb467fcdaea5d79bE.i", "backtrace::foo::hbb467fcdaea5d79b.i", "backtrace::foo.i"),
108+
] {
109+
test_single(input, normal, false);
110+
test_single(input, alternate, true);
111+
}
112+
}

fuzz/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name = "rustc-demangle-fuzz"
33
version = "0.0.0"
44
authors = ["Automatically generated"]
55
publish = false
6-
edition = "2018"
6+
edition = "2021"
77

88
[package.metadata]
99
cargo-fuzz = true

fuzz/fuzz_targets/native_c.rs

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,22 @@ fn asciify(x: &str) -> String {
3838
result
3939
}
4040

41-
fuzz_target!(|data: &[u8]| {
42-
if data.len() == 0 {
43-
return;
44-
}
45-
let alternate = data[0] % 2 == 0;
46-
let data = &data[1..];
41+
fn fuzz(data: &[u8], alternate: bool) {
4742
let mut str_buf = String::with_capacity(16384);
4843
let mut buf = [0u8; 4096];
4944
let mut demangle = rustc_demangle_native_c::CDemangle::zero();
45+
// We want to allow for easy overflow checking. The C output
46+
// can be longer than the Rust output by a factor of up to *7/2,
47+
// since e.g. an 'α' (U+03b1) in a constant string will be encoded
48+
// as [b'\xce' b'\xb1'] in the C output (2 bytes) but as
49+
// [b'\\' b'u' b'{' b'3' b'b' b'1' '}'] (7 bytes). The
50+
// other factors are smaller than that (4 hex digits = 3 utf-8 bytes,
51+
// leading to a lower expansion factor of *8/3, and so on).
52+
//
53+
// Also, to make the fuzzer more easily encounter overflow conditions
54+
// in the C code, and since for most outputs the output lengths is the
55+
// same, starting with a similar output length makes it easier.
56+
let starting_buf_len = buf.len() / 4;
5057
let state;
5158
if let Ok(s) = std::str::from_utf8(data) {
5259
if let Ok(cs) = CString::new(data) {
@@ -55,7 +62,7 @@ fuzz_target!(|data: &[u8]| {
5562
match rustc_demangle_native_c::rust_demangle_display_demangle(
5663
&demangle,
5764
buf.as_mut_ptr().cast(),
58-
buf.len() / 4,
65+
starting_buf_len,
5966
alternate,
6067
) {
6168
0 => {
@@ -77,7 +84,7 @@ fuzz_target!(|data: &[u8]| {
7784
str_buf.clear();
7885
let fmt_buf = &mut FormatBuf {
7986
buf: &mut str_buf,
80-
max_len: buf.len() / 4 - 4,
87+
max_len: starting_buf_len - 4,
8188
};
8289
let rust_overflowed = if alternate {
8390
write!(fmt_buf, "{:#}", rdemangle)
@@ -105,21 +112,21 @@ fuzz_target!(|data: &[u8]| {
105112
}
106113
};
107114
assert_eq!(asciify(&str_buf), asciify(c_demangled));
108-
if c_demangled.len() < buf.len() / 4 - 3 {
115+
if c_demangled.len() < starting_buf_len - 3 {
109116
panic!(
110117
"spurious overflow {} {:?} {:?} {:?} {}",
111118
c_demangled.len(),
112119
alternate,
113120
asciify(&str_buf),
114121
asciify(c_demangled),
115-
buf.len() / 4
122+
starting_buf_len
116123
)
117124
}
118125
}
119126
State::Ok(demangled) => {
120127
let fmt_buf = &mut FormatBuf {
121128
buf: &mut str_buf,
122-
max_len: buf.len() / 4 - 4,
129+
max_len: starting_buf_len - 4,
123130
};
124131
let rust_overflowed = if alternate {
125132
write!(fmt_buf, "{:#}", rdemangle)
@@ -137,4 +144,10 @@ fuzz_target!(|data: &[u8]| {
137144
}
138145
}
139146
}
147+
}
148+
149+
fuzz_target!(|data: &[u8]| {
150+
// fuzz both normal and alternate modes.
151+
fuzz(data, false);
152+
fuzz(data, true);
140153
});

0 commit comments

Comments
 (0)