Skip to content

Commit 3293c33

Browse files
committed
Auto merge of rust-lang#139714 - tamird:cfi-fmt-thunk, r=<try>
fmt: do not transmute function pointers (alternative) Store a &dyn Trait object rather than a pair of pointers. This avoids almost all the unsafe code in exchange for a `repr(transparent)` reference transmute. This retains the single indirect call of the previous implementation while avoiding violating CFI.
2 parents 1bc5618 + e5761eb commit 3293c33

File tree

2 files changed

+56
-73
lines changed

2 files changed

+56
-73
lines changed

library/core/src/fmt/rt.rs

+56-72
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
66
use super::*;
77
use crate::hint::unreachable_unchecked;
8-
use crate::ptr::NonNull;
98

109
#[lang = "format_placeholder"]
1110
#[derive(Copy, Clone)]
@@ -37,15 +36,13 @@ pub enum Count {
3736
Implied,
3837
}
3938

39+
trait FormatThunk {
40+
fn fmt(&self, f: &mut Formatter<'_>) -> Result;
41+
}
42+
4043
#[derive(Copy, Clone)]
4144
enum ArgumentType<'a> {
42-
Placeholder {
43-
// INVARIANT: `formatter` has type `fn(&T, _) -> _` for some `T`, and `value`
44-
// was derived from a `&'a T`.
45-
value: NonNull<()>,
46-
formatter: unsafe fn(NonNull<()>, &mut Formatter<'_>) -> Result,
47-
_lifetime: PhantomData<&'a ()>,
48-
},
45+
Placeholder(&'a dyn FormatThunk),
4946
Count(u16),
5047
}
5148

@@ -65,62 +62,61 @@ pub struct Argument<'a> {
6562
ty: ArgumentType<'a>,
6663
}
6764

65+
macro_rules! define_argument_constructor {
66+
($method:ident, $trait:ident, $fmt:item) => {
67+
#[inline]
68+
pub fn $method<T: $trait>(x: &T) -> Argument<'_> {
69+
#[repr(transparent)]
70+
struct Wrapper<T>(T);
71+
72+
// SAFETY: `Wrapper<T>` has the same memory layout as `T` due to #[repr(transparent)].
73+
let thunk = unsafe { mem::transmute::<&T, &Wrapper<T>>(x) };
74+
75+
impl<T: $trait> FormatThunk for Wrapper<T> {
76+
#[inline]
77+
$fmt
78+
}
79+
80+
Self::new(thunk)
81+
}
82+
};
83+
84+
($method:ident, $trait:ident) => {
85+
define_argument_constructor!(
86+
$method,
87+
$trait,
88+
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
89+
let Self(inner) = self;
90+
inner.fmt(f)
91+
}
92+
);
93+
};
94+
}
95+
6896
#[rustc_diagnostic_item = "ArgumentMethods"]
6997
impl Argument<'_> {
7098
#[inline]
71-
const fn new<'a, T>(x: &'a T, f: fn(&T, &mut Formatter<'_>) -> Result) -> Argument<'a> {
72-
Argument {
73-
// INVARIANT: this creates an `ArgumentType<'a>` from a `&'a T` and
74-
// a `fn(&T, ...)`, so the invariant is maintained.
75-
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) },
79-
_lifetime: PhantomData,
80-
},
81-
}
99+
fn new<'a>(x: &'a dyn FormatThunk) -> Argument<'a> {
100+
Argument { ty: ArgumentType::Placeholder(x) }
82101
}
83102

84-
#[inline]
85-
pub fn new_display<T: Display>(x: &T) -> Argument<'_> {
86-
Self::new(x, Display::fmt)
87-
}
88-
#[inline]
89-
pub fn new_debug<T: Debug>(x: &T) -> Argument<'_> {
90-
Self::new(x, Debug::fmt)
91-
}
92-
#[inline]
93-
pub fn new_debug_noop<T: Debug>(x: &T) -> Argument<'_> {
94-
Self::new(x, |_, _| Ok(()))
95-
}
96-
#[inline]
97-
pub fn new_octal<T: Octal>(x: &T) -> Argument<'_> {
98-
Self::new(x, Octal::fmt)
99-
}
100-
#[inline]
101-
pub fn new_lower_hex<T: LowerHex>(x: &T) -> Argument<'_> {
102-
Self::new(x, LowerHex::fmt)
103-
}
104-
#[inline]
105-
pub fn new_upper_hex<T: UpperHex>(x: &T) -> Argument<'_> {
106-
Self::new(x, UpperHex::fmt)
107-
}
108-
#[inline]
109-
pub fn new_pointer<T: Pointer>(x: &T) -> Argument<'_> {
110-
Self::new(x, Pointer::fmt)
111-
}
112-
#[inline]
113-
pub fn new_binary<T: Binary>(x: &T) -> Argument<'_> {
114-
Self::new(x, Binary::fmt)
115-
}
116-
#[inline]
117-
pub fn new_lower_exp<T: LowerExp>(x: &T) -> Argument<'_> {
118-
Self::new(x, LowerExp::fmt)
119-
}
120-
#[inline]
121-
pub fn new_upper_exp<T: UpperExp>(x: &T) -> Argument<'_> {
122-
Self::new(x, UpperExp::fmt)
123-
}
103+
define_argument_constructor!(new_display, Display);
104+
define_argument_constructor!(new_debug, Debug);
105+
define_argument_constructor!(
106+
new_debug_noop,
107+
Debug,
108+
fn fmt(&self, _: &mut Formatter<'_>) -> Result {
109+
Ok(())
110+
}
111+
);
112+
define_argument_constructor!(new_octal, Octal);
113+
define_argument_constructor!(new_lower_hex, LowerHex);
114+
define_argument_constructor!(new_upper_hex, UpperHex);
115+
define_argument_constructor!(new_pointer, Pointer);
116+
define_argument_constructor!(new_binary, Binary);
117+
define_argument_constructor!(new_lower_exp, LowerExp);
118+
define_argument_constructor!(new_upper_exp, UpperExp);
119+
124120
#[inline]
125121
#[track_caller]
126122
pub const fn from_usize(x: &usize) -> Argument<'_> {
@@ -135,22 +131,10 @@ impl Argument<'_> {
135131
/// # Safety
136132
///
137133
/// 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)]
143134
#[inline]
144135
pub(super) unsafe fn fmt(&self, f: &mut Formatter<'_>) -> Result {
145136
match self.ty {
146-
// SAFETY:
147-
// Because of the invariant that if `formatter` had the type
148-
// `fn(&T, _) -> _` then `value` has type `&'b T` where `'b` is
149-
// the lifetime of the `ArgumentType`, and because references
150-
// and `NonNull` are ABI-compatible, this is completely equivalent
151-
// to calling the original function passed to `new` with the
152-
// original reference, which is sound.
153-
ArgumentType::Placeholder { formatter, value, .. } => unsafe { formatter(value, f) },
137+
ArgumentType::Placeholder(thunk) => thunk.fmt(f),
154138
// SAFETY: the caller promised this.
155139
ArgumentType::Count(_) => unsafe { unreachable_unchecked() },
156140
}

library/core/src/lib.rs

-1
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,6 @@
169169
#![feature(negative_impls)]
170170
#![feature(never_type)]
171171
#![feature(no_core)]
172-
#![feature(no_sanitize)]
173172
#![feature(optimize_attribute)]
174173
#![feature(prelude_import)]
175174
#![feature(repr_simd)]

0 commit comments

Comments
 (0)