1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
use crate::d3d::*;
use std::path::*;
/// <h1 id="constructors" class="section-header"><a href="#constructors">Constructors</a></h1>
impl Compiler {
/// Attempt to load d3dcompiler_NN.dll
///
/// ### ⚠️ Safety ⚠️
///
/// **Prefer [Compiler::load_system].**
///
/// The possibility of DLL preloading attacks makes this function insecure.
/// To be fair, the security of using d3dcompiler at all is questionable.
/// If your data is untrusted, should you really be using this API at all?
/// This is a developer focused, unfuzzed, native C++ parser and deserializer, with all the edge cases that entails.
///
/// Recommended reading, to secure more than just this call:
///
/// * Dynamic-Link Library Security<br>
/// <https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-security>
/// * Secure loading of libraries to prevent DLL preloading attacks<br>
/// <https://support.microsoft.com/en-us/topic/secure-loading-of-libraries-to-prevent-dll-preloading-attacks-d41303ec-0748-9211-f317-2edc819682e1>
///
/// Additionally, one can argue about if it's sound to consider this function memory safe (e.g. missing the `unsafe` keyword.)
/// A user-provided custom `d3dcompiler_NN.dll` could relax soundness guarantees vs a Microsoft version.
/// However, taking that argument to the extreme, one could argue a user-provided custom kernel32.dll breaks rust's entire stdlib.
/// While Windows attempts to guard against that somewhat, the interpid *could* be running this executable on redox. Or on linux with wine. Or...
///
/// So, instead, I assert the following:
/// * Functions named `D3DCompile`, `D3DCreateBlob`, etc. have an implicit - if incredibly ill defined - contract.
/// * It should be "sound" to rely on that contract. Violations of the contract are bugs in the DLLs, not the consuming Rust code.
/// * That contract is narrowed by bugs in known/common d3dcompiler versions (including 3rd party ones), so please [file issues](https://github.com/MaulingMonkey/thindx/issues/new) if you can crash it!
/// * Working around *intentional* undefined behavior - by e.g. defining D3DCompiler with an incorrect function signature, or to immediately dereference null with any/all args just to prove a point, is not.
/// * If in doubt, file an issue! I can simply close it if I disagree.
///
/// ### Arguments
/// * `version` - the d3dcompiler.dll version to load
/// * [i32], [u32], [usize], [u64] - load `d3dcompiler_{version}.dll`
/// * &[str], &[String], &[Path], or &[PathBuf] - load `{version}`
///
/// ### Returns
/// * Err([std::io::Error]) - if `d3dcompiler_{version}.dll` could not be loaded
/// * Ok([Compiler]) - `d3dcompiler_{version}.dll` was found
///
/// ### Example
/// ```rust
/// use thindx::d3d;
/// let d3dc = d3d::Compiler::load_insecure(47).unwrap();
/// let d3dc = d3d::Compiler::load_insecure("d3dcompiler_47.dll").unwrap();
/// ```
pub fn load_insecure(version: impl CompilerLoadInsecure) -> Result<Self, std::io::Error> {
Self::load_impl(version.try_load()?)
}
/// Attempt to load d3dcompiler_NN.dll via LOAD_LIBRARY_SEARCH_SYSTEM32 (e.g. `%WINDOWS%\System32`)
///
/// ### ⚠️ Safety ⚠️
///
/// The use of LOAD_LIBRARY_SEARCH_SYSTEM32 should guard somewhat against DLL preloading attacks.
///
/// However, using d3dcompiler at all on untrusted data is questionable.
/// If your data is untrusted, should you really be using this API at all?
/// This is a developer focused, unfuzzed, native C++ parser and deserializer, with all the edge cases that entails.
///
/// ### Arguments
/// * `version` - the d3dcompiler.dll version to load
/// * [i32], [u32], [usize], [u64] - load `d3dcompiler_{version}.dll`
///
/// ### Returns
/// * Err([std::io::Error]) - if `d3dcompiler_{version}.dll` could not be loaded
/// * Ok([Compiler]) - `d3dcompiler_{version}.dll` was found
///
/// ### Example
/// ```rust
/// use thindx::d3d;
/// let d3dc = d3d::Compiler::load_system(47).unwrap();
/// ```
///
/// ### Remarks
/// | Platform | Support |
/// | ------------------------- | --------- |
/// | Windows 8+ | ✔️ Supported
/// | Windows 7 | ⚠️ Requires [KB2533623](https://support.microsoft.com/kb/2533623)
/// | Windows Vista | ⚠️ Requires [KB2533623](https://support.microsoft.com/kb/2533623)
/// | Windows XP | ❌ Not supported
/// | Windows Server 2012+ | ✔️ Supported
/// | Windows Server 2008 R2 | ⚠️ Requires [KB2533623](https://support.microsoft.com/kb/2533623)
/// | Windows Server 2008 | ⚠️ Requires [KB2533623](https://support.microsoft.com/kb/2533623)
/// | Windows Server 2003 | ❌ Not supported
pub fn load_system(verison: impl CompilerLoadSysemVersion) -> Result<Self, std::io::Error> {
Self::load_impl(verison.try_load()?)
}
fn load_impl(lib: minidl::Library) -> Result<Self, std::io::Error> {
macro_rules! compiler { ( $($ident:ident),* $(,)? ) => {{
#[allow(dead_code, unused_assignments, non_snake_case)]
#[cfg(not(unsafe_unsound_unstable_remove_static_asserts_for_coverage))]
fn static_assert_correct_types(compiler: &Compiler) {
$(
let mut $ident = compiler.$ident.unwrap();
$ident = winapi::um::d3dcompiler::$ident;
let _ = $ident;
)*
}
// SAFETY: ✔️
// * `sym_opt` ✔️ asserts that `$ident` is nul terminated and has no interior nuls
// * `sym_opt` ✔️ is a fairly vanilla ASCII identifier thanks to the `:ident` bound
// * `$ident` ✔️ fn symbols are type-checked against winapi above in static_assert_correct_types
// * `lib` ✔️ is permanently loaded library (per fundamental design of `minidl`)
// * `lib` ⚠️ could be malicious, but it's too late to do anything about it by the time we have a `Library`.
// See more detailed safety section ramblings in `load_*` for details / mitigation.
let compiler = unsafe { Self { $(
$ident : lib.sym_opt(concat!(stringify!($ident), "\0")),
)* } };
compiler
}}}
#[allow(clippy::undocumented_unsafe_blocks)] // there is a safety doc inside the macro... clippy is blind!
Ok(compiler! {
D3DCompile,
D3DCompile2,
D3DCompileFromFile,
D3DCompressShaders,
D3DCreateBlob,
D3DCreateFunctionLinkingGraph,
D3DCreateLinker,
D3DDecompressShaders,
D3DDisassemble,
D3DDisassembleRegion,
D3DGetBlobPart,
D3DGetDebugInfo,
D3DGetInputAndOutputSignatureBlob,
D3DGetInputSignatureBlob,
D3DGetOutputSignatureBlob,
D3DGetTraceInstructionOffsets,
D3DLoadModule,
D3DPreprocess,
D3DReadFileToBlob,
D3DReflect,
D3DReflectLibrary,
D3DSetBlobPart,
D3DStripShader,
D3DWriteBlobToFile,
//UncommentToVerifyStaticAssertCatchesEverything,
})
}
}
#[doc(hidden)] pub trait CompilerLoadInsecure : sealed::CompilerLoadInsecure {}
#[doc(hidden)] pub trait CompilerLoadSysemVersion : sealed::CompilerLoadSysemVersion {}
impl<T: sealed::CompilerLoadInsecure > CompilerLoadInsecure for T {}
impl<T: sealed::CompilerLoadSysemVersion> CompilerLoadSysemVersion for T {}
mod sealed {
use crate::dll;
use super::*;
pub trait CompilerLoadInsecure { fn try_load(self) -> minidl::Result<minidl::Library>; }
impl CompilerLoadInsecure for i32 { fn try_load(self) -> minidl::Result<minidl::Library> { minidl::Library::load(format!("d3dcompiler_{}.dll", self)) } }
impl CompilerLoadInsecure for u32 { fn try_load(self) -> minidl::Result<minidl::Library> { minidl::Library::load(format!("d3dcompiler_{}.dll", self)) } }
impl CompilerLoadInsecure for usize { fn try_load(self) -> minidl::Result<minidl::Library> { minidl::Library::load(format!("d3dcompiler_{}.dll", self)) } }
impl CompilerLoadInsecure for u64 { fn try_load(self) -> minidl::Result<minidl::Library> { minidl::Library::load(format!("d3dcompiler_{}.dll", self)) } }
impl CompilerLoadInsecure for &'_ Path { fn try_load(self) -> minidl::Result<minidl::Library> { minidl::Library::load(self) } }
impl CompilerLoadInsecure for &'_ PathBuf { fn try_load(self) -> minidl::Result<minidl::Library> { minidl::Library::load(self) } }
impl CompilerLoadInsecure for &'_ str { fn try_load(self) -> minidl::Result<minidl::Library> { minidl::Library::load(self) } }
impl CompilerLoadInsecure for &'_ String { fn try_load(self) -> minidl::Result<minidl::Library> { minidl::Library::load(self) } }
pub trait CompilerLoadSysemVersion { fn try_load(self) -> minidl::Result<minidl::Library>; }
impl CompilerLoadSysemVersion for i32 { fn try_load(self) -> minidl::Result<minidl::Library> { dll::load_system_0(&format!("d3dcompiler_{}.dll\0", self)) } }
impl CompilerLoadSysemVersion for u32 { fn try_load(self) -> minidl::Result<minidl::Library> { dll::load_system_0(&format!("d3dcompiler_{}.dll\0", self)) } }
impl CompilerLoadSysemVersion for usize { fn try_load(self) -> minidl::Result<minidl::Library> { dll::load_system_0(&format!("d3dcompiler_{}.dll\0", self)) } }
impl CompilerLoadSysemVersion for u64 { fn try_load(self) -> minidl::Result<minidl::Library> { dll::load_system_0(&format!("d3dcompiler_{}.dll\0", self)) } }
}