upgrayedd/lib.rs
1//! # `upgrayedd`
2//!
3//! [*"Two Ds for a double dose of debugging."*](https://www.youtube.com/watch?v=Gf9i23KPGKk)
4//!
5//! WARNING: You do not need this library. Do not use this library, especially
6//! in production systems. It is **not safe**, *even if* you only use safe
7//! Rust with it. It **cannot** be made safe.
8//!
9//! `upgrayedd` is a convenience proc macro for building function interposition
10//! tools in Rust. When used in a shared library build (which is the only
11//! way you should be using it), it makes `LD_PRELOAD`-style interposition
12//! look like (relatively) idiomatic Rust.
13//!
14//! See the [macro docs](`macro@upgrayedd`) for more details.
15
16/// The `#[upgrayedd]` attribute macro.
17///
18/// This macro can be used to simultaneously define a *target* (C) function
19/// to wrap *and* the handler that will wrap it.
20///
21/// ## Basic usage
22///
23/// A simple example:
24///
25/// ```no_run
26/// use upgrayedd::upgrayedd;
27///
28/// #[upgrayedd]
29/// fn X509_VERIFY_PARAM_set_auth_level(param: *mut std::ffi::c_void, level: std::ffi::c_int) {
30/// eprintln!("before!");
31/// unsafe { upgrayedd(param, level) };
32/// eprintln!("after!");
33/// }
34/// ```
35///
36/// In this case, `upgrayedd` is targeting OpenSSL's [`X509_VERIFY_PARAM_set_auth_level`] API,
37/// and the wrapper does some printing before and after calling the underlying wrapped
38/// function, which is exposed through the injected `upgrayedd` local variable.
39///
40/// ## Advanced usage
41///
42/// There isn't that much else to do. However, a few extra things to know:
43///
44/// You can change the local binding from `upgrayedd` to whatever you please
45/// by passing a different binding in via `#[upgrayedd(my_binding)]`. For example:
46///
47/// ```no_run
48/// use upgrayedd::upgrayedd;
49///
50/// #[upgrayedd(real_frobulate)]
51/// fn frobulate(size: std::ffi::c_int) -> *mut std::ffi::c_void {
52/// eprintln!("frobulating {size} bytes");
53///
54/// unsafe { real_frobulate(size) }
55/// }
56/// ```
57///
58/// You can, of course, modify parameters before passing them into the wrapped function:
59///
60/// ```no_run
61/// use upgrayedd::upgrayedd;
62///
63/// #[upgrayedd(real_frobulate)]
64/// fn frobulate(size: std::ffi::c_int) -> *mut std::ffi::c_void {
65/// unsafe { real_frobulate(size + 42) }
66/// }
67/// ```
68///
69/// Or choose not to call the wrapped function at all:
70///
71/// ```no_run
72/// use upgrayedd::upgrayedd;
73///
74/// #[upgrayedd]
75/// fn check_password(passwd: *const std::ffi::c_char) -> std::ffi::c_int {
76/// 1
77/// }
78/// ```
79///
80/// ## Safety
81///
82/// Despite exposing a safe Rust wrapper function, `upgrayedd`'s behavior is **fundamentally unsafe
83/// and unsound**. This is by design: dynamic function interposition *intentionally* violates
84/// program invariants, especially if used to modify parameters in between function calls. It
85/// also violates Rust's runtime invariants.
86///
87/// [`X509_VERIFY_PARAM_set_auth_level`]: https://www.openssl.org/docs/man1.1.1/man3/X509_VERIFY_PARAM_set_auth_level.html
88pub use upgrayedd_macros::upgrayedd;
89
90#[doc(hidden)]
91pub use ::libc;