Skip to content

Commit 020d008

Browse files
committed
Auto merge of #114534 - niluxv:strict_prov_unwind, r=cuviper,workingjubilee
Strict provenance unwind 1. Turned many `usize`s in the personality/unwind code that are actually pointers into `*const u8`. 2. Rewrote `read_encoded_pointer` to conform to strict-provenance, along the lines as described by `@eddyb` [in zulip some time ago](https://rust-lang.zulipchat.com/#narrow/stream/136281-t-opsem/topic/strict.20provenance.20in.20dwarf.3A.3Aeh/near/276197290). This should make supporting CHERI in the future easier (but they use a [slightly modified format in the callsite table](https://cheri-compiler-explorer.cl.cam.ac.uk/z/n6GhhW), which requires a CHERI specific modification to `find_eh_action`).
2 parents 89432aa + b48039f commit 020d008

File tree

5 files changed

+103
-54
lines changed

5 files changed

+103
-54
lines changed

library/panic_unwind/src/gcc.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
6363
_uwe: uw::_Unwind_Exception {
6464
exception_class: rust_exception_class(),
6565
exception_cleanup,
66-
private: [0; uw::unwinder_private_data_size],
66+
private: [core::ptr::null(); uw::unwinder_private_data_size],
6767
},
6868
canary: &CANARY,
6969
cause: data,

library/std/src/sys/personality/dwarf/eh.rs

+90-41
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! Parsing of GCC-style Language-Specific Data Area (LSDA)
22
//! For details see:
33
//! * <https://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-PDA/LSB-PDA/ehframechpt.html>
4+
//! * <https://refspecs.linuxfoundation.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/dwarfext.html>
45
//! * <https://itanium-cxx-abi.github.io/cxx-abi/exceptions.pdf>
56
//! * <https://www.airs.com/blog/archives/460>
67
//! * <https://www.airs.com/blog/archives/464>
@@ -37,17 +38,19 @@ pub const DW_EH_PE_indirect: u8 = 0x80;
3738

3839
#[derive(Copy, Clone)]
3940
pub struct EHContext<'a> {
40-
pub ip: usize, // Current instruction pointer
41-
pub func_start: usize, // Address of the current function
42-
pub get_text_start: &'a dyn Fn() -> usize, // Get address of the code section
43-
pub get_data_start: &'a dyn Fn() -> usize, // Get address of the data section
41+
pub ip: *const u8, // Current instruction pointer
42+
pub func_start: *const u8, // Pointer to the current function
43+
pub get_text_start: &'a dyn Fn() -> *const u8, // Get pointer to the code section
44+
pub get_data_start: &'a dyn Fn() -> *const u8, // Get pointer to the data section
4445
}
4546

47+
/// Landing pad.
48+
type LPad = *const u8;
4649
pub enum EHAction {
4750
None,
48-
Cleanup(usize),
49-
Catch(usize),
50-
Filter(usize),
51+
Cleanup(LPad),
52+
Catch(LPad),
53+
Filter(LPad),
5154
Terminate,
5255
}
5356

@@ -81,22 +84,24 @@ pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext<'_>) -> Result
8184
let ip = context.ip;
8285

8386
if !USING_SJLJ_EXCEPTIONS {
87+
// read the callsite table
8488
while reader.ptr < action_table {
85-
let cs_start = read_encoded_pointer(&mut reader, context, call_site_encoding)?;
86-
let cs_len = read_encoded_pointer(&mut reader, context, call_site_encoding)?;
87-
let cs_lpad = read_encoded_pointer(&mut reader, context, call_site_encoding)?;
89+
// these are offsets rather than pointers;
90+
let cs_start = read_encoded_offset(&mut reader, call_site_encoding)?;
91+
let cs_len = read_encoded_offset(&mut reader, call_site_encoding)?;
92+
let cs_lpad = read_encoded_offset(&mut reader, call_site_encoding)?;
8893
let cs_action_entry = reader.read_uleb128();
8994
// Callsite table is sorted by cs_start, so if we've passed the ip, we
9095
// may stop searching.
91-
if ip < func_start + cs_start {
96+
if ip < func_start.wrapping_add(cs_start) {
9297
break;
9398
}
94-
if ip < func_start + cs_start + cs_len {
99+
if ip < func_start.wrapping_add(cs_start + cs_len) {
95100
if cs_lpad == 0 {
96101
return Ok(EHAction::None);
97102
} else {
98-
let lpad = lpad_base + cs_lpad;
99-
return Ok(interpret_cs_action(action_table as *mut u8, cs_action_entry, lpad));
103+
let lpad = lpad_base.wrapping_add(cs_lpad);
104+
return Ok(interpret_cs_action(action_table, cs_action_entry, lpad));
100105
}
101106
}
102107
}
@@ -106,30 +111,31 @@ pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext<'_>) -> Result
106111
// SjLj version:
107112
// The "IP" is an index into the call-site table, with two exceptions:
108113
// -1 means 'no-action', and 0 means 'terminate'.
109-
match ip as isize {
114+
match ip.addr() as isize {
110115
-1 => return Ok(EHAction::None),
111116
0 => return Ok(EHAction::Terminate),
112117
_ => (),
113118
}
114-
let mut idx = ip;
119+
let mut idx = ip.addr();
115120
loop {
116121
let cs_lpad = reader.read_uleb128();
117122
let cs_action_entry = reader.read_uleb128();
118123
idx -= 1;
119124
if idx == 0 {
120125
// Can never have null landing pad for sjlj -- that would have
121126
// been indicated by a -1 call site index.
122-
let lpad = (cs_lpad + 1) as usize;
123-
return Ok(interpret_cs_action(action_table as *mut u8, cs_action_entry, lpad));
127+
// FIXME(strict provenance)
128+
let lpad = ptr::from_exposed_addr((cs_lpad + 1) as usize);
129+
return Ok(interpret_cs_action(action_table, cs_action_entry, lpad));
124130
}
125131
}
126132
}
127133
}
128134

129135
unsafe fn interpret_cs_action(
130-
action_table: *mut u8,
136+
action_table: *const u8,
131137
cs_action_entry: u64,
132-
lpad: usize,
138+
lpad: LPad,
133139
) -> EHAction {
134140
if cs_action_entry == 0 {
135141
// If cs_action_entry is 0 then this is a cleanup (Drop::drop). We run these
@@ -138,7 +144,7 @@ unsafe fn interpret_cs_action(
138144
} else {
139145
// If lpad != 0 and cs_action_entry != 0, we have to check ttype_index.
140146
// If ttype_index == 0 under the condition, we take cleanup action.
141-
let action_record = (action_table as *mut u8).offset(cs_action_entry as isize - 1);
147+
let action_record = action_table.offset(cs_action_entry as isize - 1);
142148
let mut action_reader = DwarfReader::new(action_record);
143149
let ttype_index = action_reader.read_sleb128();
144150
if ttype_index == 0 {
@@ -157,22 +163,24 @@ fn round_up(unrounded: usize, align: usize) -> Result<usize, ()> {
157163
if align.is_power_of_two() { Ok((unrounded + align - 1) & !(align - 1)) } else { Err(()) }
158164
}
159165

160-
unsafe fn read_encoded_pointer(
161-
reader: &mut DwarfReader,
162-
context: &EHContext<'_>,
163-
encoding: u8,
164-
) -> Result<usize, ()> {
165-
if encoding == DW_EH_PE_omit {
166+
/// Read a offset (`usize`) from `reader` whose encoding is described by `encoding`.
167+
///
168+
/// `encoding` must be a [DWARF Exception Header Encoding as described by the LSB spec][LSB-dwarf-ext].
169+
/// In addition the upper ("application") part must be zero.
170+
///
171+
/// # Errors
172+
/// Returns `Err` if `encoding`
173+
/// * is not a valid DWARF Exception Header Encoding,
174+
/// * is `DW_EH_PE_omit`, or
175+
/// * has a non-zero application part.
176+
///
177+
/// [LSB-dwarf-ext]: https://refspecs.linuxfoundation.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/dwarfext.html
178+
unsafe fn read_encoded_offset(reader: &mut DwarfReader, encoding: u8) -> Result<usize, ()> {
179+
if encoding == DW_EH_PE_omit || encoding & 0xF0 != 0 {
166180
return Err(());
167181
}
168-
169-
// DW_EH_PE_aligned implies it's an absolute pointer value
170-
if encoding == DW_EH_PE_aligned {
171-
reader.ptr = reader.ptr.with_addr(round_up(reader.ptr.addr(), mem::size_of::<usize>())?);
172-
return Ok(reader.read::<usize>());
173-
}
174-
175-
let mut result = match encoding & 0x0F {
182+
let result = match encoding & 0x0F {
183+
// despite the name, LLVM also uses absptr for offsets instead of pointers
176184
DW_EH_PE_absptr => reader.read::<usize>(),
177185
DW_EH_PE_uleb128 => reader.read_uleb128() as usize,
178186
DW_EH_PE_udata2 => reader.read::<u16>() as usize,
@@ -184,25 +192,66 @@ unsafe fn read_encoded_pointer(
184192
DW_EH_PE_sdata8 => reader.read::<i64>() as usize,
185193
_ => return Err(()),
186194
};
195+
Ok(result)
196+
}
197+
198+
/// Read a pointer from `reader` whose encoding is described by `encoding`.
199+
///
200+
/// `encoding` must be a [DWARF Exception Header Encoding as described by the LSB spec][LSB-dwarf-ext].
201+
///
202+
/// # Errors
203+
/// Returns `Err` if `encoding`
204+
/// * is not a valid DWARF Exception Header Encoding,
205+
/// * is `DW_EH_PE_omit`, or
206+
/// * combines `DW_EH_PE_absptr` or `DW_EH_PE_aligned` application part with an integer encoding
207+
/// (not `DW_EH_PE_absptr`) in the value format part.
208+
///
209+
/// [LSB-dwarf-ext]: https://refspecs.linuxfoundation.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/dwarfext.html
210+
unsafe fn read_encoded_pointer(
211+
reader: &mut DwarfReader,
212+
context: &EHContext<'_>,
213+
encoding: u8,
214+
) -> Result<*const u8, ()> {
215+
if encoding == DW_EH_PE_omit {
216+
return Err(());
217+
}
187218

188-
result += match encoding & 0x70 {
189-
DW_EH_PE_absptr => 0,
219+
let base_ptr = match encoding & 0x70 {
220+
DW_EH_PE_absptr => core::ptr::null(),
190221
// relative to address of the encoded value, despite the name
191-
DW_EH_PE_pcrel => reader.ptr.expose_addr(),
222+
DW_EH_PE_pcrel => reader.ptr,
192223
DW_EH_PE_funcrel => {
193-
if context.func_start == 0 {
224+
if context.func_start.is_null() {
194225
return Err(());
195226
}
196227
context.func_start
197228
}
198229
DW_EH_PE_textrel => (*context.get_text_start)(),
199230
DW_EH_PE_datarel => (*context.get_data_start)(),
231+
// aligned means the value is aligned to the size of a pointer
232+
DW_EH_PE_aligned => {
233+
reader.ptr =
234+
reader.ptr.with_addr(round_up(reader.ptr.addr(), mem::size_of::<*const u8>())?);
235+
core::ptr::null()
236+
}
200237
_ => return Err(()),
201238
};
202239

240+
let mut ptr = if base_ptr.is_null() {
241+
// any value encoding other than absptr would be nonsensical here;
242+
// there would be no source of pointer provenance
243+
if encoding & 0x0F != DW_EH_PE_absptr {
244+
return Err(());
245+
}
246+
reader.read::<*const u8>()
247+
} else {
248+
let offset = read_encoded_offset(reader, encoding & 0x0F)?;
249+
base_ptr.wrapping_add(offset)
250+
};
251+
203252
if encoding & DW_EH_PE_indirect != 0 {
204-
result = *ptr::from_exposed_addr::<usize>(result);
253+
ptr = *(ptr.cast::<*const u8>());
205254
}
206255

207-
Ok(result)
256+
Ok(ptr)
208257
}

library/std/src/sys/personality/gcc.rs

+4-5
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
3939
use super::dwarf::eh::{self, EHAction, EHContext};
4040
use crate::ffi::c_int;
41-
use libc::uintptr_t;
4241
use unwind as uw;
4342

4443
// Register ids were lifted from LLVM's TargetLowering::getExceptionPointerRegister()
@@ -160,9 +159,9 @@ cfg_if::cfg_if! {
160159
uw::_Unwind_SetGR(
161160
context,
162161
UNWIND_DATA_REG.0,
163-
exception_object as uintptr_t,
162+
exception_object as uw::_Unwind_Ptr,
164163
);
165-
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, 0);
164+
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, core::ptr::null());
166165
uw::_Unwind_SetIP(context, lpad);
167166
return uw::_URC_INSTALL_CONTEXT;
168167
}
@@ -222,9 +221,9 @@ cfg_if::cfg_if! {
222221
uw::_Unwind_SetGR(
223222
context,
224223
UNWIND_DATA_REG.0,
225-
exception_object as uintptr_t,
224+
exception_object.cast(),
226225
);
227-
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, 0);
226+
uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, core::ptr::null());
228227
uw::_Unwind_SetIP(context, lpad);
229228
uw::_URC_INSTALL_CONTEXT
230229
}

library/unwind/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#![feature(staged_api)]
55
#![feature(c_unwind)]
66
#![feature(cfg_target_abi)]
7+
#![feature(strict_provenance)]
78
#![cfg_attr(not(target_env = "msvc"), feature(libc))]
89
#![allow(internal_features)]
910

library/unwind/src/libunwind.rs

+7-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#![allow(nonstandard_style)]
22

3-
use libc::{c_int, c_void, uintptr_t};
3+
use libc::{c_int, c_void};
44

55
#[repr(C)]
66
#[derive(Debug, Copy, Clone, PartialEq)]
@@ -19,8 +19,8 @@ pub enum _Unwind_Reason_Code {
1919
pub use _Unwind_Reason_Code::*;
2020

2121
pub type _Unwind_Exception_Class = u64;
22-
pub type _Unwind_Word = uintptr_t;
23-
pub type _Unwind_Ptr = uintptr_t;
22+
pub type _Unwind_Word = *const u8;
23+
pub type _Unwind_Ptr = *const u8;
2424
pub type _Unwind_Trace_Fn =
2525
extern "C" fn(ctx: *mut _Unwind_Context, arg: *mut c_void) -> _Unwind_Reason_Code;
2626

@@ -214,7 +214,7 @@ if #[cfg(any(target_os = "ios", target_os = "tvos", target_os = "watchos", targe
214214
// On Android or ARM/Linux, these are implemented as macros:
215215

216216
pub unsafe fn _Unwind_GetGR(ctx: *mut _Unwind_Context, reg_index: c_int) -> _Unwind_Word {
217-
let mut val: _Unwind_Word = 0;
217+
let mut val: _Unwind_Word = core::ptr::null();
218218
_Unwind_VRS_Get(ctx, _UVRSC_CORE, reg_index as _Unwind_Word, _UVRSD_UINT32,
219219
&mut val as *mut _ as *mut c_void);
220220
val
@@ -229,14 +229,14 @@ if #[cfg(any(target_os = "ios", target_os = "tvos", target_os = "watchos", targe
229229
pub unsafe fn _Unwind_GetIP(ctx: *mut _Unwind_Context)
230230
-> _Unwind_Word {
231231
let val = _Unwind_GetGR(ctx, UNWIND_IP_REG);
232-
(val & !1) as _Unwind_Word
232+
val.map_addr(|v| v & !1)
233233
}
234234

235235
pub unsafe fn _Unwind_SetIP(ctx: *mut _Unwind_Context,
236236
value: _Unwind_Word) {
237237
// Propagate thumb bit to instruction pointer
238-
let thumb_state = _Unwind_GetGR(ctx, UNWIND_IP_REG) & 1;
239-
let value = value | thumb_state;
238+
let thumb_state = _Unwind_GetGR(ctx, UNWIND_IP_REG).addr() & 1;
239+
let value = value.map_addr(|v| v | thumb_state);
240240
_Unwind_SetGR(ctx, UNWIND_IP_REG, value);
241241
}
242242

0 commit comments

Comments
 (0)