Skip to content

Commit bed4e92

Browse files
zmodemtgross35
authored andcommitted
Check for symbols with default visibility in symbol-check
to ensure that compiler-builtins doesn't expose any symbols with default visibility.
1 parent de45e6a commit bed4e92

File tree

2 files changed

+62
-7
lines changed

2 files changed

+62
-7
lines changed

crates/symbol-check/src/main.rs

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ fn main() {
5858
"The binaries will not be checked for executable stacks. Used for embedded targets which \
5959
don't set `.note.GNU-stack` since there is no protection.",
6060
);
61+
opts.optflag("", "no-visibility", "Don't check visibility.");
6162

6263
let print_usage_and_exit = |code: i32| -> ! {
6364
eprintln!("{}", opts.usage(USAGE));
@@ -74,6 +75,7 @@ fn main() {
7475
}
7576

7677
let no_os_target = m.opt_present("no-os");
78+
let check_visibility = !m.opt_present("no-visibility");
7779
let free_args = m.free.iter().map(String::as_str).collect::<Vec<_>>();
7880
for arg in &free_args {
7981
assert!(
@@ -85,18 +87,18 @@ fn main() {
8587
if m.opt_present("build-and-check") {
8688
let target = m.opt_str("target").unwrap_or(env!("HOST").to_string());
8789
let paths = exec_cargo_with_args(&target, &free_args);
88-
check_paths(&paths, no_os_target);
90+
check_paths(&paths, no_os_target, check_visibility);
8991
} else if m.opt_present("check") {
9092
if free_args.is_empty() {
9193
print_usage_and_exit(1);
9294
}
93-
check_paths(&free_args, no_os_target);
95+
check_paths(&free_args, no_os_target, check_visibility);
9496
} else {
9597
print_usage_and_exit(1);
9698
}
9799
}
98100

99-
fn check_paths<P: AsRef<Path>>(paths: &[P], no_os_target: bool) {
101+
fn check_paths<P: AsRef<Path>>(paths: &[P], no_os_target: bool, check_visibility: bool) {
100102
for path in paths {
101103
let path = path.as_ref();
102104
println!("Checking {}", path.display());
@@ -105,6 +107,9 @@ fn check_paths<P: AsRef<Path>>(paths: &[P], no_os_target: bool) {
105107
verify_no_duplicates(&archive);
106108
verify_core_symbols(&archive);
107109
verify_no_exec_stack(&archive, no_os_target);
110+
if check_visibility {
111+
verify_hidden_visibility(&archive);
112+
}
108113
}
109114
}
110115

@@ -329,6 +334,38 @@ fn verify_core_symbols(archive: &BinFile) {
329334
println!(" success: no undefined references to core found");
330335
}
331336

337+
/// Check for symbols with default visibility.
338+
fn verify_hidden_visibility(archive: &BinFile) {
339+
let mut visible = Vec::new();
340+
let mut found_any = false;
341+
342+
archive.for_each_symbol(|symbol, obj, member| {
343+
// Only check defined globals.
344+
if !symbol.is_global() || symbol.is_undefined() {
345+
return;
346+
}
347+
348+
let sym = SymInfo::new(&symbol, obj, member);
349+
if sym.scope == SymbolScope::Dynamic {
350+
visible.push(sym);
351+
}
352+
353+
found_any = true
354+
});
355+
356+
if archive.has_symbol_tables() {
357+
assert!(found_any, "no symbols found");
358+
}
359+
360+
if !visible.is_empty() {
361+
visible.sort_unstable_by(|a, b| a.name.cmp(&b.name));
362+
let num = visible.len();
363+
panic!("found {num:#?} visible symbols: {visible:#?}");
364+
}
365+
366+
println!(" success: no visible symbols found");
367+
}
368+
332369
/// Reasons a binary is considered to have an executable stack.
333370
enum ExeStack {
334371
MissingGnuStackSec,

crates/symbol-check/tests/all.rs

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,20 @@ fn test_core_symbols() {
7575
.stderr_contains("from_utf8");
7676
}
7777

78+
#[test]
79+
fn test_visible_symbols() {
80+
let t = TestTarget::from_env();
81+
if t.is_windows() {
82+
eprintln!("windows does not have visibility, skipping");
83+
return;
84+
}
85+
let dir = tempdir().unwrap();
86+
let lib_out = dir.path().join("libfoo.rlib");
87+
t.rustc_build(&input_dir().join("good_lib.rs"), &lib_out, |cmd| cmd);
88+
let assert = t.symcheck_exe().arg(&lib_out).assert();
89+
assert.failure().stderr_contains("found 1 visible symbols"); // good is visible.
90+
}
91+
7892
mod exe_stack {
7993
use super::*;
8094

@@ -95,7 +109,7 @@ mod exe_stack {
95109
let objs = t.cc_build().file(src).out_dir(&dir).compile_intermediates();
96110
let [obj] = objs.as_slice() else { panic!() };
97111

98-
let assert = t.symcheck_exe().arg(obj).assert();
112+
let assert = t.symcheck_exe().arg(obj).arg("--no-visibility").assert();
99113

100114
if t.is_ppc64be() || t.no_os() || t.binary_obj_format() != BinaryFormat::Elf {
101115
// Ppc64be doesn't emit `.note.GNU-stack`, not relevant without an OS, and non-elf
@@ -127,7 +141,7 @@ mod exe_stack {
127141
.compile_intermediates();
128142
let [obj] = objs.as_slice() else { panic!() };
129143

130-
let assert = t.symcheck_exe().arg(obj).assert();
144+
let assert = t.symcheck_exe().arg(obj).arg("--no-visibility").assert();
131145

132146
if t.is_ppc64be() || t.no_os() {
133147
// Ppc64be doesn't emit `.note.GNU-stack`, not relevant without an OS.
@@ -179,7 +193,11 @@ fn test_good_lib() {
179193
let dir = tempdir().unwrap();
180194
let lib_out = dir.path().join("libfoo.rlib");
181195
t.rustc_build(&input_dir().join("good_lib.rs"), &lib_out, |cmd| cmd);
182-
let assert = t.symcheck_exe().arg(&lib_out).assert();
196+
let assert = t
197+
.symcheck_exe()
198+
.arg(&lib_out)
199+
.arg("--no-visibility")
200+
.assert();
183201
assert.success();
184202
}
185203

@@ -199,7 +217,7 @@ fn test_good_bin() {
199217
t.set_bin_out_path(&mut cmd, &out);
200218
run(cmd.arg(input_dir().join("good_bin.c")));
201219

202-
let assert = t.symcheck_exe().arg(&out).assert();
220+
let assert = t.symcheck_exe().arg(&out).arg("--no-visibility").assert();
203221
assert.success();
204222
}
205223

0 commit comments

Comments
 (0)