Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit b6e06fd

Browse filesBrowse files
committed
Use symcheck to locate writeable+executable object files
From what I have been able to find, compilers that try to emit object files compatible with a GNU linker appear to add a `.note.GNU-stack` section if the stack should not be writeable (this section is empty). We never want a writeable stack, so extend the object file check to verify that object files with any executable sections also have this `.note.GNU-stack` section. This appears to match what is done by `scanelf` to emit `!WX` [2], which is the tool used to create the output in the issue. Closes: #183 [1]: https://github.com/gentoo/pax-utils/blob/9ef54b472e42ba2c5479fbd86b8be2275724b064/scanelf.c#L428-L512
1 parent aef8a1e commit b6e06fd
Copy full SHA for b6e06fd

File tree

Expand file treeCollapse file tree

4 files changed

+86
-1
lines changed
Filter options
Expand file treeCollapse file tree

4 files changed

+86
-1
lines changed

‎crates/symbol-check/Cargo.toml

Copy file name to clipboardExpand all lines: crates/symbol-check/Cargo.toml
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,6 @@ serde_json = "1.0.140"
1111

1212
[features]
1313
wasm = ["object/wasm"]
14+
15+
[build-dependencies]
16+
cc = "1.2.25"

‎crates/symbol-check/build.rs

Copy file name to clipboard
+9Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
fn main() {
2+
let intermediates = cc::Build::new()
3+
.file("has_wx.c")
4+
.try_compile_intermediates();
5+
if let Ok(list) = intermediates {
6+
let [obj] = list.as_slice() else { panic!() };
7+
println!("cargo::rustc-env=HAS_WX_OBJ={}", obj.display());
8+
}
9+
}

‎crates/symbol-check/has_wx.c

Copy file name to clipboard
+9Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
void intermediate(void (*)(int, int), int);
2+
3+
int hack(int *array, int size) {
4+
void store (int index, int value) {
5+
array[index] = value;
6+
}
7+
8+
intermediate(store, size);
9+
}

‎crates/symbol-check/src/main.rs

Copy file name to clipboardExpand all lines: crates/symbol-check/src/main.rs
+65-1Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ use std::io::{BufRead, BufReader};
77
use std::path::{Path, PathBuf};
88
use std::process::{Command, Stdio};
99

10+
use object::elf::SHF_EXECINSTR;
1011
use object::read::archive::{ArchiveFile, ArchiveMember};
1112
use object::{
12-
File as ObjFile, Object, ObjectSymbol, Symbol, SymbolKind, SymbolScope, SymbolSection,
13+
File as ObjFile, Object, ObjectSection, ObjectSymbol, SectionFlags, Symbol, SymbolKind,
14+
SymbolScope, SymbolSection,
1315
};
1416
use serde_json::Value;
1517

@@ -51,6 +53,7 @@ fn run_build_and_check(target: Option<&str>, args: &[&str]) {
5153

5254
verify_no_duplicates(&archive);
5355
verify_core_symbols(&archive);
56+
verify_no_exec_stack(&archive);
5457
}
5558
}
5659

@@ -249,6 +252,56 @@ fn verify_core_symbols(archive: &Archive) {
249252
println!(" success: no undefined references to core found");
250253
}
251254

255+
/// Check that all object files contain a section named `.note.GNU-stack`, indicating a
256+
/// nonexecutable stack.
257+
fn verify_no_exec_stack(archive: &Archive) {
258+
let mut problem_objfiles = Vec::new();
259+
260+
archive.for_each_object(|obj, member| {
261+
if obj_has_exe_stack(&obj) {
262+
problem_objfiles.push(String::from_utf8_lossy(member.name()).into_owned());
263+
}
264+
});
265+
266+
if !problem_objfiles.is_empty() {
267+
panic!(
268+
"the following archive members have executable sections but no \
269+
`.note.GNU-stack` section: {problem_objfiles:#?}"
270+
);
271+
}
272+
273+
println!(" success: no writeable-executable sections found");
274+
}
275+
276+
fn obj_has_exe_stack(obj: &ObjFile) -> bool {
277+
// Files other than elf likely do not use the same convention.
278+
if !matches!(obj, ObjFile::Elf32(_) | ObjFile::Elf64(_)) {
279+
return false;
280+
}
281+
282+
let mut has_exe_sections = false;
283+
for sec in obj.sections() {
284+
let SectionFlags::Elf { sh_flags } = sec.flags() else {
285+
unreachable!("only elf files are being checked");
286+
};
287+
288+
let exe = (sh_flags & SHF_EXECINSTR as u64) != 0;
289+
has_exe_sections |= exe;
290+
291+
// Located a GNU-stack section, nothing else to do
292+
if sec.name().unwrap_or_default() == ".note.GNU-stack" {
293+
return false;
294+
}
295+
}
296+
297+
// Ignore object files that have no executable sections, like rmeta
298+
if !has_exe_sections {
299+
return false;
300+
}
301+
302+
true
303+
}
304+
252305
/// Thin wrapper for owning data used by `object`.
253306
struct Archive {
254307
data: Vec<u8>,
@@ -286,3 +339,14 @@ impl Archive {
286339
});
287340
}
288341
}
342+
343+
#[test]
344+
fn check_obj() {
345+
let Some(p) = option_env!("HAS_WX_OBJ") else {
346+
return;
347+
};
348+
349+
let f = fs::read(p).unwrap();
350+
let obj = ObjFile::parse(f.as_slice()).unwrap();
351+
assert!(obj_has_exe_stack(&obj));
352+
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.