Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 36 additions & 3 deletions src/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ use {
object::{
Architecture, Endianness, FileKind, Object, SectionIndex, SymbolScope,
elf::{
ET_DYN, ET_EXEC, FileHeader32, FileHeader64, SHN_UNDEF, STB_GLOBAL, STB_WEAK,
STV_DEFAULT, STV_HIDDEN,
ET_DYN, ET_EXEC, FileHeader32, FileHeader64, PF_X, PT_GNU_STACK, SHN_UNDEF, STB_GLOBAL,
STB_WEAK, STV_DEFAULT, STV_HIDDEN,
},
macho::{LC_CODE_SIGNATURE, MH_OBJECT, MH_TWOLEVEL, MachHeader32, MachHeader64},
read::{
elf::{Dyn, FileHeader, SectionHeader, Sym},
elf::{Dyn, FileHeader, ProgramHeader, SectionHeader, Sym},
macho::{LoadCommandVariant, MachHeader, Nlist, Section, Segment},
pe::{ImageNtHeaders, PeFile, PeFile32, PeFile64},
},
Expand Down Expand Up @@ -1161,6 +1161,39 @@ fn validate_elf<Elf: FileHeader<Endian = Endianness>>(
}
}

// Verify that objects are not requesting an executable stack. For backwards compatibility,
// Linux (the kernel when loading an executable, and glibc when loading a shared library)
// assumes you need an executable stack unless you request otherwise. In linked outputs
// (executables and shared libraries) this is in the program header: the flags of a
// PT_GNU_STACK entry specify stack permissions, and the default if unspecified is RWX. In
// intermediate objects (.o files) this is conveyed via the presence of an empty-length
// .note.GNU-stack, which is marked as an executable section (SHF_EXECINSTR) if the object
// needs an executable stack.
//
// For now we only check binaries because of an LLVM bug that causes .o files to be missing a
// .note.GNU-stack section, which we are overriding with -Wl,-z,noexecstack.

if matches!(elf.e_type(endian), ET_EXEC | ET_DYN) {
let mut found_pt_gnu_stack = false;
for phdr in elf.program_headers(endian, data)? {
if phdr.p_type(endian) != PT_GNU_STACK {
continue;
}
found_pt_gnu_stack = true;
if (phdr.p_flags(endian) & PF_X) != 0 {
context
.errors
.push(format!("{} requests executable stack", path.display()));
}
}
if !found_pt_gnu_stack {
context.errors.push(format!(
"{} missing PT_GNU_STACK header (defaults to executable stack)",
path.display(),
));
}
}

Ok(())
}

Expand Down