Skip to content

Commit 0d840e1

Browse files
committed
avoid leaking host details in proc macro metadata decoding
proc macro crates are essentially implemented as dynamic libraries using a dlopen-based ABI. They are also Rust crates, so they have 2 worlds - the "host" world in which they are defined, and the "target" world in which they are used. For all the "target" world knows, the proc macro crate might not even be implemented in Rust, so leaks of details from the host to the target must be avoided for correctness. Because the "host" DefId space is different from the "target" DefId space, any leak involving a DefId will have a nonsensical or out-of-bounds DefKey, and will cause all sorts of crashes. This PR fixes all leaks I have found in `decoder`. In particular, #54059 was caused by host native libraries leaking into the target, which feels like it might even be a correctness issue if it doesn't cause an ICE. Fixes #54059
1 parent cba0fdf commit 0d840e1

File tree

4 files changed

+130
-24
lines changed

4 files changed

+130
-24
lines changed

Diff for: src/librustc_metadata/cstore_impl.rs

-6
Original file line numberDiff line numberDiff line change
@@ -259,12 +259,6 @@ provide! { <'tcx> tcx, def_id, other, cdata,
259259
let cnum = cdata.cnum;
260260
assert!(cnum != LOCAL_CRATE);
261261

262-
// If this crate is a custom derive crate, then we're not even going to
263-
// link those in so we skip those crates.
264-
if cdata.root.macro_derive_registrar.is_some() {
265-
return Arc::new(Vec::new())
266-
}
267-
268262
Arc::new(cdata.exported_symbols(tcx))
269263
}
270264
}

Diff for: src/librustc_metadata/decoder.rs

+53-18
Original file line numberDiff line numberDiff line change
@@ -692,6 +692,8 @@ impl<'a, 'tcx> CrateMetadata {
692692

693693
/// Iterates over all the stability attributes in the given crate.
694694
pub fn get_lib_features(&self) -> Vec<(ast::Name, Option<ast::Name>)> {
695+
// FIXME: For a proc macro crate, not sure whether we should return the "host"
696+
// features or an empty Vec. Both don't cause ICEs.
695697
self.root
696698
.lib_features
697699
.decode(self)
@@ -700,11 +702,16 @@ impl<'a, 'tcx> CrateMetadata {
700702

701703
/// Iterates over the language items in the given crate.
702704
pub fn get_lang_items(&self) -> Vec<(DefId, usize)> {
703-
self.root
704-
.lang_items
705-
.decode(self)
706-
.map(|(def_index, index)| (self.local_def_id(def_index), index))
707-
.collect()
705+
if self.proc_macros.is_some() {
706+
// Proc macro crates do not export any lang-items to the target.
707+
vec![]
708+
} else {
709+
self.root
710+
.lang_items
711+
.decode(self)
712+
.map(|(def_index, index)| (self.local_def_id(def_index), index))
713+
.collect()
714+
}
708715
}
709716

710717
/// Iterates over each child of the given item.
@@ -978,12 +985,16 @@ impl<'a, 'tcx> CrateMetadata {
978985
pub fn get_implementations_for_trait(&self,
979986
filter: Option<DefId>,
980987
result: &mut Vec<DefId>) {
988+
if self.proc_macros.is_some() {
989+
// proc-macro crates export no trait impls.
990+
return
991+
}
992+
981993
// Do a reverse lookup beforehand to avoid touching the crate_num
982994
// hash map in the loop below.
983995
let filter = match filter.map(|def_id| self.reverse_translate_def_id(def_id)) {
984996
Some(Some(def_id)) => Some((def_id.krate.as_u32(), def_id.index)),
985997
Some(None) => return,
986-
None if self.proc_macros.is_some() => return,
987998
None => None,
988999
};
9891000

@@ -1016,11 +1027,21 @@ impl<'a, 'tcx> CrateMetadata {
10161027

10171028

10181029
pub fn get_native_libraries(&self, sess: &Session) -> Vec<NativeLibrary> {
1019-
self.root.native_libraries.decode((self, sess)).collect()
1030+
if self.proc_macros.is_some() {
1031+
// Proc macro crates do not have any *target* native libraries.
1032+
vec![]
1033+
} else {
1034+
self.root.native_libraries.decode((self, sess)).collect()
1035+
}
10201036
}
10211037

10221038
pub fn get_foreign_modules(&self, sess: &Session) -> Vec<ForeignModule> {
1023-
self.root.foreign_modules.decode((self, sess)).collect()
1039+
if self.proc_macros.is_some() {
1040+
// Proc macro crates do not have any *target* foreign modules.
1041+
vec![]
1042+
} else {
1043+
self.root.foreign_modules.decode((self, sess)).collect()
1044+
}
10241045
}
10251046

10261047
pub fn get_dylib_dependency_formats(&self) -> Vec<(CrateNum, LinkagePreference)> {
@@ -1036,10 +1057,15 @@ impl<'a, 'tcx> CrateMetadata {
10361057
}
10371058

10381059
pub fn get_missing_lang_items(&self) -> Vec<lang_items::LangItem> {
1039-
self.root
1040-
.lang_items_missing
1041-
.decode(self)
1042-
.collect()
1060+
if self.proc_macros.is_some() {
1061+
// Proc macro crates do not depend on any target weak lang-items.
1062+
vec![]
1063+
} else {
1064+
self.root
1065+
.lang_items_missing
1066+
.decode(self)
1067+
.collect()
1068+
}
10431069
}
10441070

10451071
pub fn get_fn_arg_names(&self, id: DefIndex) -> Vec<ast::Name> {
@@ -1055,10 +1081,16 @@ impl<'a, 'tcx> CrateMetadata {
10551081
pub fn exported_symbols(&self,
10561082
tcx: TyCtxt<'a, 'tcx, 'tcx>)
10571083
-> Vec<(ExportedSymbol<'tcx>, SymbolExportLevel)> {
1058-
let lazy_seq: LazySeq<(ExportedSymbol<'tcx>, SymbolExportLevel)> =
1059-
LazySeq::with_position_and_length(self.root.exported_symbols.position,
1060-
self.root.exported_symbols.len);
1061-
lazy_seq.decode((self, tcx)).collect()
1084+
if self.proc_macros.is_some() {
1085+
// If this crate is a custom derive crate, then we're not even going to
1086+
// link those in so we skip those crates.
1087+
vec![]
1088+
} else {
1089+
let lazy_seq: LazySeq<(ExportedSymbol<'tcx>, SymbolExportLevel)> =
1090+
LazySeq::with_position_and_length(self.root.exported_symbols.position,
1091+
self.root.exported_symbols.len);
1092+
lazy_seq.decode((self, tcx)).collect()
1093+
}
10621094
}
10631095

10641096
pub fn get_rendered_const(&self, id: DefIndex) -> String {
@@ -1149,9 +1181,12 @@ impl<'a, 'tcx> CrateMetadata {
11491181
/// file they represent, just information about length, line breaks, and
11501182
/// multibyte characters. This information is enough to generate valid debuginfo
11511183
/// for items inlined from other crates.
1184+
///
1185+
/// Proc macro crates don't currently export spans, so this function does not have
1186+
/// to work for them.
11521187
pub fn imported_source_files(&'a self,
1153-
local_source_map: &source_map::SourceMap)
1154-
-> ReadGuard<'a, Vec<cstore::ImportedSourceFile>> {
1188+
local_source_map: &source_map::SourceMap)
1189+
-> ReadGuard<'a, Vec<cstore::ImportedSourceFile>> {
11551190
{
11561191
let source_files = self.source_map_import_info.borrow();
11571192
if !source_files.is_empty() {
+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// no-prefer-dynamic
12+
13+
// check that having extern "C" functions in a proc macro doesn't crash.
14+
15+
#![crate_type="proc-macro"]
16+
#![allow(non_snake_case)]
17+
18+
extern crate proc_macro;
19+
20+
macro_rules! proc_macro_tokenstream {
21+
() => {
22+
::proc_macro::TokenStream
23+
};
24+
}
25+
26+
macro_rules! proc_macro_expr_impl {
27+
($(
28+
$( #[$attr:meta] )*
29+
pub fn $func:ident($input:ident: &str) -> String $body:block
30+
)+) => {
31+
$(
32+
// Parses an input that looks like:
33+
//
34+
// ```
35+
// #[allow(unused)]
36+
// enum ProcMacroHack {
37+
// Input = (stringify!(ARGS), 0).1,
38+
// }
39+
// ```
40+
$( #[$attr] )*
41+
#[proc_macro_derive($func)]
42+
pub fn $func(input: proc_macro_tokenstream!()) -> proc_macro_tokenstream!() {
43+
unsafe { rust_dbg_extern_identity_u64(0); }
44+
panic!()
45+
}
46+
)+
47+
};
48+
}
49+
50+
proc_macro_expr_impl! {
51+
pub fn base2_impl(input: &str) -> String {
52+
panic!()
53+
}
54+
}
55+
56+
#[link(name="rust_test_helpers")]
57+
extern "C" {
58+
pub fn rust_dbg_extern_identity_u64(v: u64) -> u64;
59+
}

Diff for: src/test/incremental-fulldeps/issue-54059.rs

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// aux-build:issue_54059.rs
12+
// ignore-stage1
13+
// ignore-wasm32-bare no libc for ffi testing
14+
// revisions: rpass1
15+
16+
extern crate issue_54059;
17+
18+
fn main() {}

0 commit comments

Comments
 (0)