Commit a2923b2a authored by Rui Ueyama's avatar Rui Ueyama
Browse files

Implement CET Shadow Stack (Intel Controlflow Enforcement Technology) support on Windows

Patch by Petr Penzin.

Windows support for CET is limited to shadow stack, which is enabled
by setting a PE bit in the linker.

Docs:

MSVC linker flag:
https://docs.microsoft.com/en-us/cpp/build/reference/cetcompat?view=vs-2019

IMAGE_DLLCHARACTERISTICS_EX_CET_COMPAT PE bit:
https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#extended-dll-characteristics

Differential Revision: https://reviews.llvm.org/D70606
parent 2005c60a
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -211,6 +211,7 @@ struct Configuration {
  uint32_t functionPadMin = 0;
  bool dynamicBase = true;
  bool allowBind = true;
  bool cetCompat = false;
  bool nxCompat = true;
  bool allowIsolation = true;
  bool terminalServerAware = true;
+1 −0
Original line number Diff line number Diff line
@@ -1549,6 +1549,7 @@ void LinkerDriver::link(ArrayRef<const char *> argsArr) {
                       !args.hasArg(OPT_profile));
  config->integrityCheck =
      args.hasFlag(OPT_integritycheck, OPT_integritycheck_no, false);
  config->cetCompat = args.hasFlag(OPT_cetcompat, OPT_cetcompat_no, false);
  config->nxCompat = args.hasFlag(OPT_nxcompat, OPT_nxcompat_no, true);
  for (auto *arg : args.filtered(OPT_swaprun))
    parseSwaprun(arg->getValue());
+2 −0
Original line number Diff line number Diff line
@@ -148,6 +148,8 @@ defm allowisolation : B<"allowisolation", "Enable DLL isolation (default)",
defm appcontainer : B<"appcontainer",
                      "Image can only be run in an app container",
                      "Image can run outside an app container (default)">;
defm cetcompat : B<"cetcompat", "Mark executable image as compatible with Control-flow Enforcement Technology (CET) Shadow Stack",
                   "Don't mark executable image as compatible with Control-flow Enforcement Technology (CET) Shadow Stack (default)">;
defm dynamicbase : B<"dynamicbase", "Enable ASLR (default unless /fixed)",
                     "Disable ASLR (default when /fixed)">;
defm fixed : B<"fixed", "Disable base relocations",
+35 −12
Original line number Diff line number Diff line
@@ -91,7 +91,8 @@ namespace {

class DebugDirectoryChunk : public NonSectionChunk {
public:
  DebugDirectoryChunk(const std::vector<Chunk *> &r, bool writeRepro)
  DebugDirectoryChunk(const std::vector<std::pair<COFF::DebugType, Chunk *>> &r,
                      bool writeRepro)
      : records(r), writeRepro(writeRepro) {}

  size_t getSize() const override {
@@ -101,11 +102,11 @@ public:
  void writeTo(uint8_t *b) const override {
    auto *d = reinterpret_cast<debug_directory *>(b);

    for (const Chunk *record : records) {
      OutputSection *os = record->getOutputSection();
      uint64_t offs = os->getFileOff() + (record->getRVA() - os->getRVA());
      fillEntry(d, COFF::IMAGE_DEBUG_TYPE_CODEVIEW, record->getSize(),
                record->getRVA(), offs);
    for (const std::pair<COFF::DebugType, Chunk *> record : records) {
      Chunk *c = record.second;
      OutputSection *os = c->getOutputSection();
      uint64_t offs = os->getFileOff() + (c->getRVA() - os->getRVA());
      fillEntry(d, record.first, c->getSize(), c->getRVA(), offs);
      ++d;
    }

@@ -140,7 +141,7 @@ private:
  }

  mutable std::vector<support::ulittle32_t *> timeDateStamps;
  const std::vector<Chunk *> &records;
  const std::vector<std::pair<COFF::DebugType, Chunk *>> &records;
  bool writeRepro;
};

@@ -165,6 +166,17 @@ public:
  mutable codeview::DebugInfo *buildId = nullptr;
};

class ExtendedDllCharacteristicsChunk : public NonSectionChunk {
public:
  ExtendedDllCharacteristicsChunk(uint32_t c) : characteristics(c) {}

  size_t getSize() const override { return 4; }

  void writeTo(uint8_t *buf) const override { write32le(buf, characteristics); }

  uint32_t characteristics = 0;
};

// PartialSection represents a group of chunks that contribute to an
// OutputSection. Collating a collection of PartialSections of same name and
// characteristics constitutes the OutputSection.
@@ -250,7 +262,7 @@ private:
  bool setNoSEHCharacteristic = false;

  DebugDirectoryChunk *debugDirectory = nullptr;
  std::vector<Chunk *> debugRecords;
  std::vector<std::pair<COFF::DebugType, Chunk *>> debugRecords;
  CVDebugRecordChunk *buildId = nullptr;
  ArrayRef<uint8_t> sectionTable;

@@ -920,8 +932,9 @@ void Writer::createMiscChunks() {

  // Create Debug Information Chunks
  OutputSection *debugInfoSec = config->mingw ? buildidSec : rdataSec;
  if (config->debug || config->repro) {
  if (config->debug || config->repro || config->cetCompat) {
    debugDirectory = make<DebugDirectoryChunk>(debugRecords, config->repro);
    debugDirectory->setAlignment(4);
    debugInfoSec->addChunk(debugDirectory);
  }

@@ -931,10 +944,20 @@ void Writer::createMiscChunks() {
    // allowing a debugger to match a PDB and an executable.  So we need it even
    // if we're ultimately not going to write CodeView data to the PDB.
    buildId = make<CVDebugRecordChunk>();
    debugRecords.push_back(buildId);
    debugRecords.push_back({COFF::IMAGE_DEBUG_TYPE_CODEVIEW, buildId});
  }

  if (config->cetCompat) {
    ExtendedDllCharacteristicsChunk *extendedDllChars =
        make<ExtendedDllCharacteristicsChunk>(
            IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT);
    debugRecords.push_back(
        {COFF::IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS, extendedDllChars});
  }

    for (Chunk *c : debugRecords)
      debugInfoSec->addChunk(c);
  if (debugRecords.size() > 0) {
    for (std::pair<COFF::DebugType, Chunk *> r : debugRecords)
      debugInfoSec->addChunk(r.second);
  }

  // Create SEH table. x86-only.
+10 −0
Original line number Diff line number Diff line
@@ -50,6 +50,16 @@ NXCOMPAT: IMAGE_DLL_CHARACTERISTICS_NX_COMPAT
# RUN: llvm-readobj --file-headers %t.exe | FileCheck -check-prefix=NONXCOMPAT %s
NONXCOMPAT-NOT: IMAGE_DLL_CHARACTERISTICS_NX_COMPAT

# RUN: lld-link /out:%t.exe /entry:main /cetcompat %t.obj
# RUN: llvm-readobj --coff-debug-directory %t.exe | FileCheck -check-prefix=CETCOMPAT %s
CETCOMPAT: IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT

# RUN: lld-link /out:%t.exe /entry:main %t.obj
# RUN: llvm-readobj --coff-debug-directory %t.exe | FileCheck -check-prefix=NONCETCOMPAT %s
# RUN: lld-link /out:%t.exe /entry:main /cetcompat:no %t.obj
# RUN: llvm-readobj --coff-debug-directory %t.exe | FileCheck -check-prefix=NONCETCOMPAT %s
NONCETCOMPAT-NOT: IMAGE_DLL_CHARACTERISTICS_EX_CET_COMPAT

# RUN: lld-link /out:%t.exe /entry:main /swaprun:CD %t.obj
# RUN: llvm-readobj --file-headers %t.exe | FileCheck -check-prefix=SWAPCD %s
# RUN: lld-link /out:%t.exe /entry:main /swaprun:cd,net %t.obj
Loading