Skip to content

Commit 9467024

Browse files
authored
Linux: DXC BSTR now resembles its Windows counterpart with explicit size (#11)
We [recently] fixed memory leaks when calling into the intellisense part of DXC and made the decision shortly after merging to [implement null-invariant `BSTR`s properly] in DXC's shimmed type for non-Windows, which was promptly accepted. Currently our implementation of `SysFreeString` together with that PR segfaults because it frees the pointer at offset `+4` of the allocation. That is corrected together with `SysStringLen` now reading the size of the string from this prefix instead of looking for a (wrong!) null termination character. At least our Windows and Linux implementation `utils.rs` is the same again. [recently]: #10 [implement null-invariant `BSTR`s properly]: microsoft/DirectXShaderCompiler#3250
1 parent e691ed1 commit 9467024

File tree

2 files changed

+47
-27
lines changed

2 files changed

+47
-27
lines changed

src/os.rs

+46-8
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,14 @@ mod os_defs {
99
};
1010

1111
pub use winapi::um::combaseapi::CoTaskMemFree;
12-
pub use winapi::um::oleauto::SysFreeString;
12+
pub use winapi::um::oleauto::{SysFreeString, SysStringLen};
1313
}
1414

1515
#[cfg(not(windows))]
1616
mod os_defs {
1717
pub type CHAR = i8;
18-
pub type WCHAR = u32;
18+
pub type UINT = u32;
19+
pub type WCHAR = widestring::WideChar;
1920
pub type OLECHAR = WCHAR;
2021
pub type LPSTR = *mut CHAR;
2122
pub type LPWSTR = *mut WCHAR;
@@ -25,20 +26,57 @@ mod os_defs {
2526
pub type LPBSTR = *mut BSTR;
2627
pub type HRESULT = i32;
2728

29+
/// Returns a mutable pointer to the length prefix of the string
30+
fn len_ptr(p: BSTR) -> *mut UINT {
31+
// The first four bytes before the pointer contain the length prefix:
32+
// https://docs.microsoft.com/en-us/previous-versions/windows/desktop/automat/bstr#remarks
33+
unsafe { p.cast::<UINT>().offset(-1) }
34+
}
35+
2836
#[allow(non_snake_case)]
2937
/// # Safety
30-
/// `p` must be a valid pointer to an allocation made with `malloc`
38+
/// `p` must be a valid pointer to an allocation made with `malloc`, or null.
3139
pub unsafe fn CoTaskMemFree(p: *mut libc::c_void) {
32-
// https://github.com/microsoft/DirectXShaderCompiler/blob/a8d9780046cb64a1cea842fa6fc28a250e3e2c09/include/dxc/Support/WinAdapter.h#L46
33-
libc::free(p)
40+
// https://github.com/microsoft/DirectXShaderCompiler/blob/56e22b30c5e43632f56a1f97865f37108ec35463/include/dxc/Support/WinAdapter.h#L46
41+
if !p.is_null() {
42+
libc::free(p)
43+
}
3444
}
3545

3646
#[allow(non_snake_case)]
3747
/// # Safety
38-
/// `p` must be a valid pointer to an allocation made with `malloc`
48+
/// `p` must be a valid pointer to an allocation made with `malloc`, or null.
3949
pub unsafe fn SysFreeString(p: BSTR) {
40-
// https://github.com/microsoft/DirectXShaderCompiler/blob/a8d9780046cb64a1cea842fa6fc28a250e3e2c09/include/dxc/Support/WinAdapter.h#L48-L50
41-
libc::free(p as _)
50+
// https://github.com/microsoft/DirectXShaderCompiler/blob/56e22b30c5e43632f56a1f97865f37108ec35463/lib/DxcSupport/WinAdapter.cpp#L50-L53
51+
if !p.is_null() {
52+
libc::free(len_ptr(p).cast::<_>())
53+
}
54+
}
55+
56+
#[allow(non_snake_case)]
57+
/// Returns the size of `p` in bytes, excluding terminating NULL character
58+
///
59+
/// # Safety
60+
/// `p` must be a valid pointer to a [`BSTR`] with size-prefix in the `4` leading bytes, or null.
61+
///
62+
/// https://docs.microsoft.com/en-us/previous-versions/windows/desktop/automat/bstr#remarks
63+
pub unsafe fn SysStringByteLen(p: BSTR) -> UINT {
64+
if p.is_null() {
65+
0
66+
} else {
67+
*len_ptr(p)
68+
}
69+
}
70+
71+
#[allow(non_snake_case)]
72+
/// Returns the size of `p` in characters, excluding terminating NULL character
73+
///
74+
/// # Safety
75+
/// `p` must be a valid pointer to a [`BSTR`] with size-prefix in the `4` leading bytes, or null.
76+
///
77+
/// https://docs.microsoft.com/en-us/previous-versions/windows/desktop/automat/bstr#remarks
78+
pub unsafe fn SysStringLen(p: BSTR) -> UINT {
79+
SysStringByteLen(p) / std::mem::size_of::<OLECHAR>() as UINT
4280
}
4381
}
4482

src/utils.rs

+1-19
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
use std::path::PathBuf;
22

3-
use crate::os::{SysFreeString, BSTR, HRESULT, LPSTR, LPWSTR, WCHAR};
3+
use crate::os::{SysFreeString, SysStringLen, BSTR, HRESULT, LPSTR, LPWSTR, WCHAR};
44
use crate::wrapper::*;
55
use thiserror::Error;
66

7-
#[cfg(windows)]
8-
use winapi::um::oleauto::SysStringLen;
9-
107
pub(crate) fn to_wide(msg: &str) -> Vec<WCHAR> {
118
widestring::WideCString::from_str(msg)
129
.unwrap()
@@ -21,7 +18,6 @@ pub(crate) fn from_wide(wide: LPWSTR) -> String {
2118
}
2219
}
2320

24-
#[cfg(windows)]
2521
pub(crate) fn from_bstr(string: BSTR) -> String {
2622
unsafe {
2723
let len = SysStringLen(string) as usize;
@@ -35,20 +31,6 @@ pub(crate) fn from_bstr(string: BSTR) -> String {
3531
}
3632
}
3733

38-
#[cfg(not(windows))]
39-
pub(crate) fn from_bstr(string: BSTR) -> String {
40-
// TODO (Marijn): This does NOT cover embedded NULLs
41-
42-
// BSTR contains its size in the four bytes preceding the pointer, in order to contain NULL bytes:
43-
// https://docs.microsoft.com/en-us/previous-versions/windows/desktop/automat/bstr
44-
// DXC on non-Windows does not adhere to that and simply allocates a buffer without prepending the size:
45-
// https://github.com/microsoft/DirectXShaderCompiler/blob/a8d9780046cb64a1cea842fa6fc28a250e3e2c09/include/dxc/Support/WinAdapter.h#L49-L50
46-
let result = from_wide(string as LPWSTR);
47-
48-
unsafe { SysFreeString(string) };
49-
result
50-
}
51-
5234
pub(crate) fn from_lpstr(string: LPSTR) -> String {
5335
unsafe {
5436
let len = (0..).take_while(|&i| *string.offset(i) != 0).count();

0 commit comments

Comments
 (0)