Skip to content

Commit 040ab35

Browse files
committed
Fix a memory leak in SEH unwinding if a Rust panic is caught by C++ and discarded
1 parent 1b98124 commit 040ab35

File tree

3 files changed

+73
-12
lines changed

3 files changed

+73
-12
lines changed

src/libpanic_unwind/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#![feature(staged_api)]
2727
#![feature(std_internals)]
2828
#![feature(unwind_attributes)]
29+
#![feature(abi_thiscall)]
2930
#![panic_runtime]
3031
#![feature(panic_runtime)]
3132

src/libpanic_unwind/seh.rs

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,11 @@ use libc::{c_int, c_uint, c_void};
7777
// #include <stdint.h>
7878
//
7979
// struct rust_panic {
80+
// rust_panic(const rust_panic&);
81+
// ~rust_panic();
82+
//
8083
// uint64_t x[2];
81-
// }
84+
// };
8285
//
8386
// void foo() {
8487
// rust_panic a = {0, 1};
@@ -128,7 +131,7 @@ mod imp {
128131
#[repr(C)]
129132
pub struct _ThrowInfo {
130133
pub attributes: c_uint,
131-
pub pnfnUnwind: imp::ptr_t,
134+
pub pmfnUnwind: imp::ptr_t,
132135
pub pForwardCompat: imp::ptr_t,
133136
pub pCatchableTypeArray: imp::ptr_t,
134137
}
@@ -145,7 +148,7 @@ pub struct _CatchableType {
145148
pub pType: imp::ptr_t,
146149
pub thisDisplacement: _PMD,
147150
pub sizeOrOffset: c_int,
148-
pub copy_function: imp::ptr_t,
151+
pub copyFunction: imp::ptr_t,
149152
}
150153

151154
#[repr(C)]
@@ -168,7 +171,7 @@ const TYPE_NAME: [u8; 11] = *b"rust_panic\0";
168171

169172
static mut THROW_INFO: _ThrowInfo = _ThrowInfo {
170173
attributes: 0,
171-
pnfnUnwind: ptr!(0),
174+
pmfnUnwind: ptr!(0),
172175
pForwardCompat: ptr!(0),
173176
pCatchableTypeArray: ptr!(0),
174177
};
@@ -181,7 +184,7 @@ static mut CATCHABLE_TYPE: _CatchableType = _CatchableType {
181184
pType: ptr!(0),
182185
thisDisplacement: _PMD { mdisp: 0, pdisp: -1, vdisp: 0 },
183186
sizeOrOffset: mem::size_of::<[u64; 2]>() as c_int,
184-
copy_function: ptr!(0),
187+
copyFunction: ptr!(0),
185188
};
186189

187190
extern "C" {
@@ -208,6 +211,39 @@ static mut TYPE_DESCRIPTOR: _TypeDescriptor = _TypeDescriptor {
208211
name: TYPE_NAME,
209212
};
210213

214+
// Destructor used if the C++ code decides to capture the exception and drop it
215+
// without propagating it. The catch part of the try intrinsic will set the
216+
// first word of the exception object to 0 so that it is skipped by the
217+
// destructor.
218+
//
219+
// Note that x86 Windows uses the "thiscall" calling convention for C++ member
220+
// functions instead of the default "C" calling convention.
221+
cfg_if::cfg_if! {
222+
if #[cfg(target_arch = "x86")] {
223+
unsafe extern "thiscall" fn exception_cleanup(e: *mut [u64; 2]) {
224+
if (*e)[0] != 0 {
225+
cleanup(*e);
226+
}
227+
}
228+
unsafe extern "thiscall" fn exception_copy(_dest: *mut [u64; 2],
229+
_src: *mut [u64; 2])
230+
-> *mut [u64; 2] {
231+
panic!("Rust panics cannot be copied");
232+
}
233+
} else {
234+
unsafe extern "C" fn exception_cleanup(e: *mut [u64; 2]) {
235+
if (*e)[0] != 0 {
236+
cleanup(*e);
237+
}
238+
}
239+
unsafe extern "C" fn exception_copy(_dest: *mut [u64; 2],
240+
_src: *mut [u64; 2])
241+
-> *mut [u64; 2] {
242+
panic!("Rust panics cannot be copied");
243+
}
244+
}
245+
}
246+
211247
pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
212248
use core::intrinsics::atomic_store;
213249

@@ -220,8 +256,7 @@ pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
220256
// exception (constructed above).
221257
let ptrs = mem::transmute::<_, raw::TraitObject>(data);
222258
let mut ptrs = [ptrs.data as u64, ptrs.vtable as u64];
223-
let ptrs_ptr = ptrs.as_mut_ptr();
224-
let throw_ptr = ptrs_ptr as *mut _;
259+
let throw_ptr = ptrs.as_mut_ptr() as *mut _;
225260

226261
// This... may seems surprising, and justifiably so. On 32-bit MSVC the
227262
// pointers between these structure are just that, pointers. On 64-bit MSVC,
@@ -243,6 +278,12 @@ pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
243278
//
244279
// In any case, we basically need to do something like this until we can
245280
// express more operations in statics (and we may never be able to).
281+
if !cfg!(bootstrap) {
282+
atomic_store(
283+
&mut THROW_INFO.pmfnUnwind as *mut _ as *mut u32,
284+
ptr!(exception_cleanup) as u32,
285+
);
286+
}
246287
atomic_store(
247288
&mut THROW_INFO.pCatchableTypeArray as *mut _ as *mut u32,
248289
ptr!(&CATCHABLE_TYPE_ARRAY as *const _) as u32,
@@ -255,6 +296,12 @@ pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
255296
&mut CATCHABLE_TYPE.pType as *mut _ as *mut u32,
256297
ptr!(&TYPE_DESCRIPTOR as *const _) as u32,
257298
);
299+
if !cfg!(bootstrap) {
300+
atomic_store(
301+
&mut CATCHABLE_TYPE.copyFunction as *mut _ as *mut u32,
302+
ptr!(exception_copy) as u32,
303+
);
304+
}
258305

259306
extern "system" {
260307
#[unwind(allowed)]

src/librustc_codegen_llvm/intrinsic.rs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -922,24 +922,29 @@ fn codegen_msvc_try(
922922
// #include <stdint.h>
923923
//
924924
// struct rust_panic {
925+
// rust_panic(const rust_panic&);
926+
// ~rust_panic();
927+
//
925928
// uint64_t x[2];
926929
// }
927930
//
928931
// int bar(void (*foo)(void), uint64_t *ret) {
929932
// try {
930933
// foo();
931934
// return 0;
932-
// } catch(rust_panic a) {
935+
// } catch(rust_panic& a) {
933936
// ret[0] = a.x[0];
934937
// ret[1] = a.x[1];
938+
// a.x[0] = 0;
935939
// return 1;
936940
// }
937941
// }
938942
//
939943
// More information can be found in libstd's seh.rs implementation.
940944
let i64_2 = bx.type_array(bx.type_i64(), 2);
941-
let i64_align = bx.tcx().data_layout.i64_align.abi;
942-
let slot = bx.alloca(i64_2, i64_align);
945+
let i64_2_ptr = bx.type_ptr_to(i64_2);
946+
let ptr_align = bx.tcx().data_layout.pointer_align.abi;
947+
let slot = bx.alloca(i64_2_ptr, ptr_align);
943948
bx.invoke(func, &[data], normal.llbb(), catchswitch.llbb(), None);
944949

945950
normal.ret(bx.const_i32(0));
@@ -951,11 +956,19 @@ fn codegen_msvc_try(
951956
Some(did) => bx.get_static(did),
952957
None => bug!("eh_catch_typeinfo not defined, but needed for SEH unwinding"),
953958
};
954-
let funclet = catchpad.catch_pad(cs, &[tydesc, bx.const_i32(0), slot]);
959+
let flags = bx.const_i32(8); // Catch by reference
960+
let funclet = catchpad.catch_pad(cs, &[tydesc, flags, slot]);
955961

956-
let payload = catchpad.load(slot, i64_align);
962+
let i64_align = bx.tcx().data_layout.i64_align.abi;
963+
let payload_ptr = catchpad.load(slot, ptr_align);
964+
let payload = catchpad.load(payload_ptr, i64_align);
957965
let local_ptr = catchpad.bitcast(local_ptr, bx.type_ptr_to(i64_2));
958966
catchpad.store(payload, local_ptr, i64_align);
967+
968+
// Clear the first word of the exception so avoid double-dropping it.
969+
let payload_0_ptr = catchpad.inbounds_gep(payload_ptr, &[bx.const_i32(0), bx.const_i32(0)]);
970+
catchpad.store(bx.const_u64(0), payload_0_ptr, i64_align);
971+
959972
catchpad.catch_ret(&funclet, caught.llbb());
960973

961974
caught.ret(bx.const_i32(1));

0 commit comments

Comments
 (0)