@@ -65,61 +65,92 @@ pub struct Argument<'a> {
65
65
ty: ArgumentType<'a>,
66
66
}
67
67
68
- #[rustc_diagnostic_item = "ArgumentMethods"]
69
- impl Argument<'_> {
70
- #[inline]
71
- const fn new<'a, T>(x: &'a T, f: fn(&T, &mut Formatter<'_>) -> Result) -> Argument<'a> {
68
+ macro_rules! argument_new {
69
+ ($t:ty, $x:expr, $f:expr) => {
72
70
Argument {
73
71
// INVARIANT: this creates an `ArgumentType<'a>` from a `&'a T` and
74
72
// a `fn(&T, ...)`, so the invariant is maintained.
75
73
ty: ArgumentType::Placeholder {
76
- value: NonNull::from_ref(x).cast(),
77
- // SAFETY: function pointers always have the same layout.
78
- formatter: unsafe { mem::transmute(f) },
74
+ value: NonNull::<$t>::from_ref($x).cast(),
75
+ // The Rust ABI considers all pointers to be equivalent, so transmuting a fn(&T) to
76
+ // fn(NonNull<()>) and calling it with a NonNull<()> that points at a T is allowed.
77
+ // However, the CFI sanitizer does not allow this, and triggers a crash when it
78
+ // happens.
79
+ //
80
+ // To avoid this crash, we use a helper function when CFI is enabled. To avoid the
81
+ // cost of this helper function (mainly code-size) when it is not needed, we
82
+ // transmute the function pointer otherwise.
83
+ //
84
+ // This is similar to what the Rust compiler does internally with vtables when KCFI
85
+ // is enabled, where it generates trampoline functions that only serve to adjust the
86
+ // expected type of the argument. `ArgumentType::Placeholder` is a bit like a
87
+ // manually constructed trait object, so it is not surprising that the same approach
88
+ // has to be applied here as well.
89
+ //
90
+ // It is still considered problematic (from the Rust side) that CFI rejects entirely
91
+ // legal Rust programs, so we do not consider anything done here a stable guarantee,
92
+ // but meanwhile we carry this work-around to keep Rust compatible with CFI and
93
+ // KCFI.
94
+ #[cfg(not(any(sanitize = "cfi", sanitize = "kcfi")))]
95
+ formatter: {
96
+ let f: fn(&$t, &mut Formatter<'_>) -> Result = $f;
97
+ // SAFETY: This is only called with `value`, which has the right type.
98
+ unsafe { core::mem::transmute(f) }
99
+ },
100
+ #[cfg(any(sanitize = "cfi", sanitize = "kcfi"))]
101
+ formatter: |ptr: NonNull<()>, fmt: &mut Formatter<'_>| {
102
+ let func = $f;
103
+ // SAFETY: This is the same type as the `value` field.
104
+ let r = unsafe { ptr.cast::<$t>().as_ref() };
105
+ (func)(r, fmt)
106
+ },
79
107
_lifetime: PhantomData,
80
108
},
81
109
}
82
- }
110
+ };
111
+ }
83
112
113
+ #[rustc_diagnostic_item = "ArgumentMethods"]
114
+ impl Argument<'_> {
84
115
#[inline]
85
116
pub fn new_display<T: Display>(x: &T) -> Argument<'_> {
86
- Self::new( x, Display::fmt)
117
+ argument_new!(T, x, <T as Display> ::fmt)
87
118
}
88
119
#[inline]
89
120
pub fn new_debug<T: Debug>(x: &T) -> Argument<'_> {
90
- Self::new( x, Debug::fmt)
121
+ argument_new!(T, x, <T as Debug> ::fmt)
91
122
}
92
123
#[inline]
93
124
pub fn new_debug_noop<T: Debug>(x: &T) -> Argument<'_> {
94
- Self::new( x, |_, _| Ok(()))
125
+ argument_new!(T, x, |_: &T , _| Ok(()))
95
126
}
96
127
#[inline]
97
128
pub fn new_octal<T: Octal>(x: &T) -> Argument<'_> {
98
- Self::new( x, Octal::fmt)
129
+ argument_new!(T, x, <T as Octal> ::fmt)
99
130
}
100
131
#[inline]
101
132
pub fn new_lower_hex<T: LowerHex>(x: &T) -> Argument<'_> {
102
- Self::new( x, LowerHex::fmt)
133
+ argument_new!(T, x, <T as LowerHex> ::fmt)
103
134
}
104
135
#[inline]
105
136
pub fn new_upper_hex<T: UpperHex>(x: &T) -> Argument<'_> {
106
- Self::new( x, UpperHex::fmt)
137
+ argument_new!(T, x, <T as UpperHex> ::fmt)
107
138
}
108
139
#[inline]
109
140
pub fn new_pointer<T: Pointer>(x: &T) -> Argument<'_> {
110
- Self::new( x, Pointer::fmt)
141
+ argument_new!(T, x, <T as Pointer> ::fmt)
111
142
}
112
143
#[inline]
113
144
pub fn new_binary<T: Binary>(x: &T) -> Argument<'_> {
114
- Self::new( x, Binary::fmt)
145
+ argument_new!(T, x, <T as Binary> ::fmt)
115
146
}
116
147
#[inline]
117
148
pub fn new_lower_exp<T: LowerExp>(x: &T) -> Argument<'_> {
118
- Self::new( x, LowerExp::fmt)
149
+ argument_new!(T, x, <T as LowerExp> ::fmt)
119
150
}
120
151
#[inline]
121
152
pub fn new_upper_exp<T: UpperExp>(x: &T) -> Argument<'_> {
122
- Self::new( x, UpperExp::fmt)
153
+ argument_new!(T, x, <T as UpperExp> ::fmt)
123
154
}
124
155
#[inline]
125
156
#[track_caller]
@@ -135,11 +166,6 @@ impl Argument<'_> {
135
166
/// # Safety
136
167
///
137
168
/// This argument must actually be a placeholder argument.
138
- ///
139
- // FIXME: Transmuting formatter in new and indirectly branching to/calling
140
- // it here is an explicit CFI violation.
141
- #[allow(inline_no_sanitize)]
142
- #[no_sanitize(cfi, kcfi)]
143
169
#[inline]
144
170
pub(super) unsafe fn fmt(&self, f: &mut Formatter<'_>) -> Result {
145
171
match self.ty {
0 commit comments