thindx 0.0.0-unsound.5

Thin DirectX wrappers
Documentation
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)) } }
}