[vm] Mark ELF libraries produced directly by the VM as not requiring an executable stack.

Even if the main program disables executable stacks, dlopen'ing a library that doesn't itself disable executable stack will switch the stack to executable. (Presumably in the name of compatability with GNU nested functions.)

Dart does not need executable stacks, and executable stacks are an additional attack surface.

TEST=readelf
Bug: b/229648756
Change-Id: Ia8c234ebc6178a26528d37b1359a10dd42039a9b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/241540
Reviewed-by: Tess Strickland <sstrickl@google.com>
Commit-Queue: Ryan Macnak <rmacnak@google.com>
This commit is contained in:
Ryan Macnak 2022-04-19 18:51:47 +00:00 committed by Commit Bot
parent d2eb2197a3
commit 32715d1d5b
2 changed files with 26 additions and 5 deletions

View file

@ -41,6 +41,7 @@ enum class ProgramHeaderType : uint32_t {
PT_DYNAMIC = 2,
PT_NOTE = 4,
PT_PHDR = 6,
PT_GNU_STACK = 0x6474e551,
};
struct ProgramHeader {

View file

@ -229,12 +229,10 @@ class Section : public ZoneAllocated {
private:
static intptr_t EncodeFlags(bool allocate, bool executable, bool writable) {
// Executable and writable only make sense if this is an allocated section.
ASSERT(allocate || (!executable && !writable));
if (!allocate) return 0;
intptr_t flags = elf::SHF_ALLOC;
// We currently don't allow sections that are both executable and writable.
ASSERT(!executable || !writable);
intptr_t flags = 0;
if (allocate) flags |= elf::SHF_ALLOC;
if (executable) flags |= elf::SHF_EXECINSTR;
if (writable) flags |= elf::SHF_WRITE;
return flags;
@ -265,7 +263,6 @@ class Segment : public ZoneAllocated {
ASSERT(segment_type != elf::ProgramHeaderType::PT_NULL);
// All segments should have at least one section.
ASSERT(initial_section != nullptr);
ASSERT(initial_section->IsAllocated());
sections_.Add(initial_section);
}
@ -282,6 +279,8 @@ class Segment : public ZoneAllocated {
return compiler::target::kWordSize;
case elf::ProgramHeaderType::PT_NOTE:
return kNoteAlignment;
case elf::ProgramHeaderType::PT_GNU_STACK:
return 1;
default:
UNREACHABLE();
return 0;
@ -379,6 +378,21 @@ class ReservedSection : public Section {
intptr_t FileSize() const { return 0; }
};
// Specifies the permissions used for the stack, notably whether the stack
// should be executable. If absent, the stack will be executable.
class GnuStackSection : public Section {
public:
GnuStackSection()
: Section(elf::SectionHeaderType::SHT_NULL,
/*allocate=*/false,
/*executable=*/false,
/*writable=*/true) {
set_file_offset(0);
}
intptr_t FileSize() const { return 0; }
};
class StringTable : public Section {
public:
explicit StringTable(Zone* zone, bool allocate)
@ -1658,6 +1672,12 @@ ProgramTable* SectionTable::CreateProgramTable(SymbolTable* symtab) {
program_table->Add(
new (zone_) Segment(zone_, dynamic, elf::ProgramHeaderType::PT_DYNAMIC));
// Add a PT_GNU_STACK segment to prevent the loading of our snapshot from
// switch the stack to be executable.
auto* const gnu_stack = new (zone_) GnuStackSection();
program_table->Add(new (zone_) Segment(zone_, gnu_stack,
elf::ProgramHeaderType::PT_GNU_STACK));
return program_table;
}