1From 290b3379f60c0578174861c6ac8fb93f1ce83a3f Mon Sep 17 00:00:00 2001
2From: Matthew Maurer <[email protected]>
3Date: Tue, 17 Aug 2021 10:50:56 -0700
4Subject: [PATCH] Adjust API to match __cxa_demangle
5
6Bug: 178565008
7Test: cargo test + generate tombstone
8Change-Id: Iaf76af67a7f3a6323e926075a8ecd08b1d381db0
9---
10 include/rustc_demangle.h |   9 +-
11 src/lib.rs               | 339 +++++++++++++++++++++++++++------------
12 2 files changed, 238 insertions(+), 110 deletions(-)
13
14diff --git a/include/rustc_demangle.h b/include/rustc_demangle.h
15index 61c4aa1..e7ee2ca 100644
16--- a/include/rustc_demangle.h
17+++ b/include/rustc_demangle.h
18@@ -5,11 +5,10 @@
19 extern "C" {
20 #endif
21
22-// Demangles symbol given in `mangled` argument into `out` buffer
23-//
24-// Returns 0 if `mangled` is not Rust symbol or if `out` buffer is too small
25-// Returns 1 otherwise
26-int rustc_demangle(const char *mangled, char *out, size_t out_size);
27+// For size_t
28+#include <stddef.h>
29+
30+char *rustc_demangle(const char *mangled, char *out, size_t *len, int *status);
31
32 #ifdef __cplusplus
33 }
34diff --git a/src/lib.rs b/src/lib.rs
35index 51e3103..7610145 100644
36--- a/src/lib.rs
37+++ b/src/lib.rs
38@@ -1,160 +1,289 @@
39 extern crate rustc_demangle;
40
41+use std::alloc::{GlobalAlloc, Layout, System};
42 use std::io::Write;
43 use std::os::raw::{c_char, c_int};
44+use std::ptr;
45+use std::result;
46+
47+type Result<T> = result::Result<T, Status>;
48+
49+/// Convenience function to set return status if a location was provided.
50+unsafe fn set_status(status: *mut c_int, val: c_int) {
51+    if !status.is_null() {
52+        *status = val;
53+    }
54+}
55+
56+/// Region from the system allocator for demangler output. We use the
57+/// system allocator because the intended client is C/C++ code which
58+/// may not be using the Rust allocator.
59+struct SystemBuffer {
60+    buf: *mut u8,
61+    size: usize,
62+    size_out: *mut usize,
63+}
64+
65+impl SystemBuffer {
66+    const DEFAULT_BUFFER_SIZE: usize = 1024;
67+    fn new(size: usize) -> Result<Self> {
68+        let buf = unsafe { System.alloc_zeroed(Layout::from_size_align_unchecked(size, 1)) };
69+        if buf.is_null() {
70+            Err(Status::AllocFailure)
71+        } else {
72+            Ok(Self {
73+                buf,
74+                size,
75+                size_out: ptr::null_mut(),
76+            })
77+        }
78+    }
79+    /// Safety: If buf is non-null, size must be non-null and point to the
80+    /// non-zero size of the buffer provided in buf.
81+    /// Takes ownership of the buffer passed in (and may reallocate it).
82+    /// size must outlive the resulting buffer if non-null.
83+    unsafe fn from_raw(buf: *mut c_char, size: *mut usize) -> Result<Self> {
84+        if buf.is_null() {
85+            if !size.is_null() {
86+                *size = Self::DEFAULT_BUFFER_SIZE;
87+            }
88+            let fresh = Self::new(Self::DEFAULT_BUFFER_SIZE)?;
89+            Ok(Self {
90+                size_out: size,
91+                ..fresh
92+            })
93+        } else {
94+            Ok(Self {
95+                buf: buf as *mut u8,
96+                size: *size,
97+                size_out: size,
98+            })
99+        }
100+    }
101+    fn as_mut_slice(&mut self) -> &mut [u8] {
102+        unsafe { std::slice::from_raw_parts_mut(self.buf, self.size) }
103+    }
104+    fn resize(&mut self) -> Result<()> {
105+        let new_size = self.size * 2;
106+        let new_buf = unsafe {
107+            System.realloc(
108+                self.buf,
109+                Layout::from_size_align_unchecked(self.size, 1),
110+                new_size,
111+            )
112+        };
113+        if new_buf.is_null() {
114+            Err(Status::AllocFailure)
115+        } else {
116+            self.buf = new_buf;
117+            self.size = new_size;
118+            if !self.size_out.is_null() {
119+                unsafe {
120+                    *self.size_out = new_size;
121+                }
122+            }
123+            Ok(())
124+        }
125+    }
126+}
127
128 /// C-style interface for demangling.
129-/// Demangles symbol given in `mangled` argument into `out` buffer
130+/// Demangles symbol given in `mangled` argument into `out` buffer.
131+///
132+/// This interface is a drop-in replacement for `__cxa_demangle`, but for
133+/// Rust demangling.
134+///
135+/// If `out` is null, a buffer will be allocated using the system allocator
136+/// to contain the results.
137+/// If `out` is non-null, `out_size` must be a pointer to the current size
138+/// of the buffer, and `out` must come from the system allocator.
139+/// If `out_size` is non-null, the size of the output buffer will be written
140+/// to it.
141+///
142+/// If `status` is non-null, it will be set to one of the following values:
143+/// * 0: Demangling succeeded
144+/// * -1: Allocation failure
145+/// * -2: Name did not demangle
146+/// * -3: Invalid arguments
147+///
148+/// Returns null if `mangled` is not Rust symbol or demangling failed.
149+/// Returns the buffer containing the demangled symbol name otherwise.
150 ///
151 /// Unsafe as it handles buffers by raw pointers.
152 ///
153-/// Returns 0 if `mangled` is not Rust symbol or if `out` buffer is too small
154-/// Returns 1 otherwise
155+/// For non-null `out`, `out_size` represents a slight deviation from the
156+/// `__cxa_demangle` behavior. For `__cxa_demangle`, the buffer must be at
157+/// *least* the provided size. For `rustc_demangle`, it must be the exact
158+/// buffer size because it is used in the reconstruction of the `Layout`
159+/// for use with `::realloc`.
160 #[no_mangle]
161 pub unsafe extern "C" fn rustc_demangle(
162     mangled: *const c_char,
163     out: *mut c_char,
164-    out_size: usize,
165-) -> c_int {
166+    out_size: *mut usize,
167+    status: *mut c_int,
168+) -> *mut c_char {
169+    match rustc_demangle_native(mangled, out, out_size) {
170+        Ok(demangled) => {
171+            set_status(status, 0);
172+            demangled
173+        }
174+        Err(e) => {
175+            set_status(status, e as c_int);
176+            ptr::null_mut()
177+        }
178+    }
179+}
180+
181+enum Status {
182+    AllocFailure = -1,
183+    DemangleFailure = -2,
184+    InvalidArgs = -3,
185+}
186+
187+unsafe fn rustc_demangle_native(
188+    mangled: *const c_char,
189+    out: *mut c_char,
190+    out_size: *mut usize,
191+) -> Result<*mut c_char> {
192+    if mangled.is_null() {
193+        return Err(Status::InvalidArgs);
194+    }
195     let mangled_str = match std::ffi::CStr::from_ptr(mangled).to_str() {
196         Ok(s) => s,
197-        Err(_) => return 0,
198+        Err(_) => return Err(Status::InvalidArgs),
199     };
200+
201+    if !out.is_null() {
202+        if out_size.is_null() {
203+            return Err(Status::InvalidArgs);
204+        }
205+        if *out_size == 0 {
206+            return Err(Status::InvalidArgs);
207+        }
208+    }
209+
210+    let mut out_buf = SystemBuffer::from_raw(out, out_size)?;
211+
212     match rustc_demangle::try_demangle(mangled_str) {
213         Ok(demangle) => {
214-            let mut out_slice = std::slice::from_raw_parts_mut(out as *mut u8, out_size);
215-            match write!(out_slice, "{:#}\0", demangle) {
216-                Ok(_) => return 1,
217-                Err(_) => return 0,
218+            while write!(out_buf.as_mut_slice(), "{:#}\0", demangle).is_err() {
219+                out_buf.resize()?;
220             }
221+            Ok(out_buf.as_mut_slice().as_mut_ptr() as *mut c_char)
222         }
223-        Err(_) => return 0,
224+        Err(_) => Err(Status::DemangleFailure),
225     }
226 }
227
228 #[cfg(test)]
229 mod tests {
230-    use std;
231-    use std::os::raw::c_char;
232+    use std::alloc::{GlobalAlloc, Layout, System};
233+    use std::os::raw::{c_char, c_int};
234+    use std::ptr;
235+
236+    struct DemangleResult {
237+        out_buf: *mut u8,
238+        out_size: usize,
239+        status: c_int,
240+    }
241+
242+    impl Drop for DemangleResult {
243+        fn drop(&mut self) {
244+            if !self.out_buf.is_null() {
245+                unsafe {
246+                    System.dealloc(
247+                        self.out_buf,
248+                        Layout::from_size_align_unchecked(self.out_size, 1),
249+                    );
250+                }
251+            }
252+        }
253+    }
254+
255+    impl DemangleResult {
256+        fn as_slice(&self) -> &[u8] {
257+            unsafe { std::slice::from_raw_parts(self.out_buf, self.out_size) }
258+        }
259+    }
260+
261+    fn demangle(mangled: &str, alloc_size: usize) -> DemangleResult {
262+        unsafe { raw_demangle(mangled.as_ptr() as *const c_char, alloc_size) }
263+    }
264+
265+    unsafe fn raw_demangle(mangled: *const c_char, alloc_size: usize) -> DemangleResult {
266+        let mut out_size: usize = alloc_size;
267+        let mut status: c_int = 0;
268+        let out_buf: *mut c_char = if out_size != 0 {
269+            System.alloc(Layout::from_size_align_unchecked(out_size, 1)) as *mut c_char
270+        } else {
271+            ptr::null_mut()
272+        };
273+        ptr::write_bytes(out_buf, '*' as u8, out_size);
274+
275+        let res = super::rustc_demangle(mangled, out_buf, &mut out_size, &mut status);
276+        DemangleResult {
277+            out_buf: res as *mut u8,
278+            out_size,
279+            status,
280+        }
281+    }
282+
283     #[test]
284     fn demangle_c_str_large() {
285-        let mangled = "_ZN4testE\0";
286-        let mut out_buf: Vec<u8> = vec![42; 8];
287-        let res = unsafe {
288-            super::rustc_demangle(
289-                mangled.as_ptr() as *const c_char,
290-                out_buf.as_mut_ptr() as *mut c_char,
291-                8,
292-            )
293-        };
294-        assert_eq!(res, 1);
295-        let out_str = std::str::from_utf8(&out_buf[..5]).unwrap();
296+        let res = demangle("_ZN4testE\0", 8);
297+        assert_eq!(res.status, 0);
298+        let out_str = core::str::from_utf8(&res.as_slice()[..5]).unwrap();
299         assert_eq!(out_str, "test\0");
300     }
301
302     #[test]
303     fn demangle_c_str_exact() {
304-        let mangled = "_ZN4testE\0";
305-        let mut out_buf: Vec<u8> = vec![42; 8];
306-        let res = unsafe {
307-            super::rustc_demangle(
308-                mangled.as_ptr() as *const c_char,
309-                out_buf.as_mut_ptr() as *mut c_char,
310-                5,
311-            )
312-        };
313-        assert_eq!(res, 1);
314-        let out_str = std::str::from_utf8(&out_buf).unwrap();
315+        let res = demangle("_ZN4testE\0", 8);
316+        assert_eq!(res.status, 0);
317+        // No reallocation necessary, so our * fill should be present
318+        let out_str = core::str::from_utf8(res.as_slice()).unwrap();
319         assert_eq!(out_str, "test\0***");
320     }
321
322     #[test]
323     fn demangle_c_str_small() {
324-        let mangled = "_ZN4testE\0";
325-        let mut out_buf: Vec<u8> = vec![42; 8];
326-        let res = unsafe {
327-            super::rustc_demangle(
328-                mangled.as_ptr() as *const c_char,
329-                out_buf.as_mut_ptr() as *mut c_char,
330-                4,
331-            )
332-        };
333-        assert_eq!(res, 0);
334-        let out_str = std::str::from_utf8(&out_buf[4..]).unwrap();
335-        assert_eq!(out_str, "****");
336-    }
337-
338-    #[test]
339-    fn demangle_c_str_smaller() {
340-        let mangled = "_ZN4testE\0";
341-        let mut out_buf: Vec<u8> = vec![42; 8];
342-        let res = unsafe {
343-            super::rustc_demangle(
344-                mangled.as_ptr() as *const c_char,
345-                out_buf.as_mut_ptr() as *mut c_char,
346-                3,
347-            )
348-        };
349-        assert_eq!(res, 0);
350-        let out_str = std::str::from_utf8(&out_buf[3..]).unwrap();
351-        assert_eq!(out_str, "*****");
352+        let res = demangle("_ZN4testE\0", 4);
353+        assert_eq!(res.status, 0);
354+        // demangle should have realloced
355+        assert_ne!(res.out_size, 4);
356+        // Only check the start, since the reallocation means our * fill may
357+        // be absent.
358+        let out_str = core::str::from_utf8(&res.as_slice()[..5]).unwrap();
359+        assert_eq!(out_str, "test\0");
360     }
361
362     #[test]
363-    fn demangle_c_str_zero() {
364-        let mangled = "_ZN4testE\0";
365-        let mut out_buf: Vec<u8> = vec![42; 8];
366-        let res = unsafe {
367-            super::rustc_demangle(
368-                mangled.as_ptr() as *const c_char,
369-                out_buf.as_mut_ptr() as *mut c_char,
370-                0,
371-            )
372-        };
373-        assert_eq!(res, 0);
374-        let out_str = std::str::from_utf8(&out_buf).unwrap();
375-        assert_eq!(out_str, "********");
376+    fn demangle_c_str_alloc() {
377+        let res = demangle("_ZN4testE\0", 0);
378+        assert_eq!(res.status, 0);
379+        // demangle should have allocated
380+        assert_ne!(res.out_size, 0);
381+        let out_str = core::str::from_utf8(&res.as_slice()[..5]).unwrap();
382+        assert_eq!(out_str, "test\0");
383     }
384
385     #[test]
386     fn demangle_c_str_not_rust_symbol() {
387-        let mangled = "la la la\0";
388-        let mut out_buf: Vec<u8> = vec![42; 8];
389-        let res = unsafe {
390-            super::rustc_demangle(
391-                mangled.as_ptr() as *const c_char,
392-                out_buf.as_mut_ptr() as *mut c_char,
393-                8,
394-            )
395-        };
396-        assert_eq!(res, 0);
397+        let res = demangle("la la la\0", 8);
398+        assert_eq!(res.status, -2);
399     }
400
401     #[test]
402     fn demangle_c_str_null() {
403-        let mangled = "\0";
404-        let mut out_buf: Vec<u8> = vec![42; 8];
405-        let res = unsafe {
406-            super::rustc_demangle(
407-                mangled.as_ptr() as *const c_char,
408-                out_buf.as_mut_ptr() as *mut c_char,
409-                8,
410-            )
411-        };
412-        assert_eq!(res, 0);
413+        let res = demangle("\0", 8);
414+        assert_eq!(res.status, -2);
415     }
416
417     #[test]
418     fn demangle_c_str_invalid_utf8() {
419         let mangled = [116, 101, 115, 116, 165, 0];
420-        let mut out_buf: Vec<u8> = vec![42; 8];
421-        let res = unsafe {
422-            super::rustc_demangle(
423-                mangled.as_ptr() as *const c_char,
424-                out_buf.as_mut_ptr() as *mut c_char,
425-                8,
426-            )
427-        };
428-        assert_eq!(res, 0);
429+        let res = unsafe { raw_demangle(mangled.as_ptr() as *const c_char, 8) };
430+        assert_eq!(res.status, -2);
431     }
432 }
433--
4342.33.0.rc1.237.g0d66db33f3-goog
435
436