From: Georgii Rymar Date: Fri, 5 Jun 2020 12:50:59 +0000 (+0300) Subject: [yaml2obj] - Introduce a 10 Mb limit of the output by default and a --max-size option. X-Git-Tag: llvmorg-12-init~3501 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=3c123acf57cb21faed327d7f36a1146875c67319;p=platform%2Fupstream%2Fllvm.git [yaml2obj] - Introduce a 10 Mb limit of the output by default and a --max-size option. Multiple times we faced an issue of huge outputs due to unexpected behavior or incorrect test cases. The last one was https://reviews.llvm.org/D80629#2073066. This patch limits the output to 10 Mb for ELF and introduces the --max-size to change this limit. I've tried to keep the implementation non-intrusive. The current logic we have is that we prepare section content in a buffer first and write it to the output later. This patch checks the available limit on each writing attempt to this buffer and stops writing when the limit is reached and raises the internal error flag. Later, this flag is is checked before the actual writing to a file happens and an error is reported. Differential revision: https://reviews.llvm.org/D81258 --- diff --git a/llvm/include/llvm/ObjectYAML/yaml2obj.h b/llvm/include/llvm/ObjectYAML/yaml2obj.h index cf8020d..34def36 100644 --- a/llvm/include/llvm/ObjectYAML/yaml2obj.h +++ b/llvm/include/llvm/ObjectYAML/yaml2obj.h @@ -47,14 +47,15 @@ struct YamlObjectFile; using ErrorHandler = llvm::function_ref; bool yaml2coff(COFFYAML::Object &Doc, raw_ostream &Out, ErrorHandler EH); -bool yaml2elf(ELFYAML::Object &Doc, raw_ostream &Out, ErrorHandler EH); +bool yaml2elf(ELFYAML::Object &Doc, raw_ostream &Out, ErrorHandler EH, + uint64_t MaxSize); bool yaml2macho(YamlObjectFile &Doc, raw_ostream &Out, ErrorHandler EH); bool yaml2minidump(MinidumpYAML::Object &Doc, raw_ostream &Out, ErrorHandler EH); bool yaml2wasm(WasmYAML::Object &Doc, raw_ostream &Out, ErrorHandler EH); bool convertYAML(Input &YIn, raw_ostream &Out, ErrorHandler ErrHandler, - unsigned DocNum = 1); + unsigned DocNum = 1, uint64_t MaxSize = UINT64_MAX); /// Convenience function for tests. std::unique_ptr diff --git a/llvm/lib/ObjectYAML/ELFEmitter.cpp b/llvm/lib/ObjectYAML/ELFEmitter.cpp index 63b642e..e6d5edb 100644 --- a/llvm/lib/ObjectYAML/ELFEmitter.cpp +++ b/llvm/lib/ObjectYAML/ELFEmitter.cpp @@ -23,6 +23,7 @@ #include "llvm/ObjectYAML/ELFYAML.h" #include "llvm/ObjectYAML/yaml2obj.h" #include "llvm/Support/EndianStream.h" +#include "llvm/Support/Errc.h" #include "llvm/Support/Error.h" #include "llvm/Support/LEB128.h" #include "llvm/Support/MemoryBuffer.h" @@ -35,30 +36,94 @@ using namespace llvm; // This class is used to build up a contiguous binary blob while keeping // track of an offset in the output (which notionally begins at // `InitialOffset`). +// The blob might be limited to an arbitrary size. All attempts to write data +// are ignored and the error condition is remembered once the limit is reached. +// Such an approach allows us to simplify the code by delaying error reporting +// and doing it at a convenient time. namespace { class ContiguousBlobAccumulator { const uint64_t InitialOffset; + const uint64_t MaxSize; + SmallVector Buf; raw_svector_ostream OS; + Error ReachedLimitErr = Error::success(); + + bool checkLimit(uint64_t Size) { + if (!ReachedLimitErr && getOffset() + Size <= MaxSize) + return true; + if (!ReachedLimitErr) + ReachedLimitErr = createStringError(errc::invalid_argument, + "reached the output size limit"); + return false; + } public: - ContiguousBlobAccumulator(uint64_t InitialOffset_) - : InitialOffset(InitialOffset_), Buf(), OS(Buf) {} + ContiguousBlobAccumulator(uint64_t BaseOffset, uint64_t SizeLimit) + : InitialOffset(BaseOffset), MaxSize(SizeLimit), OS(Buf) {} + uint64_t tell() const { return OS.tell(); } uint64_t getOffset() const { return InitialOffset + OS.tell(); } - raw_ostream &getOS() { return OS; } + void writeBlobToStream(raw_ostream &Out) const { Out << OS.str(); } + + Error takeLimitError() { + // Request to write 0 bytes to check we did not reach the limit. + checkLimit(0); + return std::move(ReachedLimitErr); + } /// \returns The new offset. uint64_t padToAlignment(unsigned Align) { - if (Align == 0) - Align = 1; uint64_t CurrentOffset = getOffset(); - uint64_t AlignedOffset = alignTo(CurrentOffset, Align); - OS.write_zeros(AlignedOffset - CurrentOffset); - return AlignedOffset; // == CurrentOffset; + if (ReachedLimitErr) + return CurrentOffset; + + uint64_t AlignedOffset = alignTo(CurrentOffset, Align == 0 ? 1 : Align); + uint64_t PaddingSize = AlignedOffset - CurrentOffset; + if (!checkLimit(PaddingSize)) + return CurrentOffset; + + writeZeros(PaddingSize); + return AlignedOffset; + } + + raw_ostream *getRawOS(uint64_t Size) { + if (checkLimit(Size)) + return &OS; + return nullptr; + } + + void writeAsBinary(const yaml::BinaryRef &Bin, uint64_t N = UINT64_MAX) { + if (!checkLimit(Bin.binary_size())) + return; + Bin.writeAsBinary(OS, N); + } + + void writeZeros(uint64_t Num) { + if (checkLimit(Num)) + OS.write_zeros(Num); } - void writeBlobToStream(raw_ostream &Out) { Out << OS.str(); } + void write(const char *Ptr, size_t Size) { + if (checkLimit(Size)) + OS.write(Ptr, Size); + } + + void write(unsigned char C) { + if (checkLimit(1)) + OS.write(C); + } + + unsigned writeULEB128(uint64_t Val) { + if (!checkLimit(sizeof(uint64_t))) + return 0; + return encodeULEB128(Val, OS); + } + + template void write(T Val, support::endianness E) { + if (checkLimit(sizeof(T))) + support::endian::write(OS, Val, E); + } }; // Used to keep track of section and symbol names, so that in the YAML file @@ -167,7 +232,7 @@ template class ELFState { ArrayRef SHeaders); void finalizeStrings(); - void writeELFHeader(ContiguousBlobAccumulator &CBA, raw_ostream &OS); + void writeELFHeader(raw_ostream &OS, uint64_t SHOff); void writeSectionContent(Elf_Shdr &SHeader, const ELFYAML::RawContentSection &Section, ContiguousBlobAccumulator &CBA); @@ -238,7 +303,7 @@ template class ELFState { public: static bool writeELF(raw_ostream &OS, ELFYAML::Object &Doc, - yaml::ErrorHandler EH); + yaml::ErrorHandler EH, uint64_t MaxSize); }; } // end anonymous namespace @@ -307,7 +372,7 @@ ELFState::ELFState(ELFYAML::Object &D, yaml::ErrorHandler EH) } template -void ELFState::writeELFHeader(ContiguousBlobAccumulator &CBA, raw_ostream &OS) { +void ELFState::writeELFHeader(raw_ostream &OS, uint64_t SHOff) { using namespace llvm::ELF; Elf_Ehdr Header; @@ -333,10 +398,6 @@ void ELFState::writeELFHeader(ContiguousBlobAccumulator &CBA, raw_ostream Header.e_shentsize = Doc.Header.SHEntSize ? (uint16_t)*Doc.Header.SHEntSize : sizeof(Elf_Shdr); - // Align the start of the section header table, which is written after all - // other sections to the end of the file. - uint64_t SHOff = - alignToOffset(CBA, sizeof(typename ELFT::uint), /*Offset=*/None); if (Doc.Header.SHOff) Header.e_shoff = *Doc.Header.SHOff; @@ -666,19 +727,19 @@ static size_t findFirstNonGlobal(ArrayRef Symbols) { return Symbols.size(); } -static uint64_t writeContent(raw_ostream &OS, +static uint64_t writeContent(ContiguousBlobAccumulator &CBA, const Optional &Content, const Optional &Size) { size_t ContentSize = 0; if (Content) { - Content->writeAsBinary(OS); + CBA.writeAsBinary(*Content); ContentSize = Content->binary_size(); } if (!Size) return ContentSize; - OS.write_zeros(*Size - ContentSize); + CBA.writeZeros(*Size - ContentSize); return *Size; } @@ -790,18 +851,17 @@ void ELFState::initSymtabSectionHeader(Elf_Shdr &SHeader, assignSectionAddress(SHeader, YAMLSec); SHeader.sh_offset = alignToOffset(CBA, SHeader.sh_addralign, /*Offset=*/None); - raw_ostream &OS = CBA.getOS(); if (RawSec && (RawSec->Content || RawSec->Size)) { assert(Symbols.empty()); - SHeader.sh_size = writeContent(OS, RawSec->Content, RawSec->Size); + SHeader.sh_size = writeContent(CBA, RawSec->Content, RawSec->Size); return; } std::vector Syms = toELFSymbols(Symbols, IsStatic ? DotStrtab : DotDynstr); - writeArrayData(OS, makeArrayRef(Syms)); - SHeader.sh_size = arrayDataSize(makeArrayRef(Syms)); + SHeader.sh_size = Syms.size() * sizeof(Elf_Sym); + CBA.write((const char *)Syms.data(), SHeader.sh_size); } template @@ -818,12 +878,12 @@ void ELFState::initStrtabSectionHeader(Elf_Shdr &SHeader, StringRef Name, dyn_cast_or_null(YAMLSec); SHeader.sh_offset = alignToOffset(CBA, SHeader.sh_addralign, /*Offset=*/None); - raw_ostream &OS = CBA.getOS(); if (RawSec && (RawSec->Content || RawSec->Size)) { - SHeader.sh_size = writeContent(OS, RawSec->Content, RawSec->Size); + SHeader.sh_size = writeContent(CBA, RawSec->Content, RawSec->Size); } else { - STB.write(OS); + if (raw_ostream *OS = CBA.getRawOS(STB.getSize())) + STB.write(*OS); SHeader.sh_size = STB.getSize(); } @@ -848,26 +908,34 @@ static bool shouldEmitDWARF(DWARFYAML::Data &DWARF, StringRef Name) { template Expected emitDWARF(typename ELFT::Shdr &SHeader, StringRef Name, - const DWARFYAML::Data &DWARF, raw_ostream &OS) { - uint64_t BeginOffset = OS.tell(); + const DWARFYAML::Data &DWARF, + ContiguousBlobAccumulator &CBA) { + // We are unable to predict the size of debug data, so we request to write 0 + // bytes. This should always return us an output stream unless CBA is already + // in an error state. + raw_ostream *OS = CBA.getRawOS(0); + if (!OS) + return 0; + + uint64_t BeginOffset = CBA.tell(); Error Err = Error::success(); cantFail(std::move(Err)); if (Name == ".debug_str") - Err = DWARFYAML::emitDebugStr(OS, DWARF); + Err = DWARFYAML::emitDebugStr(*OS, DWARF); else if (Name == ".debug_aranges") - Err = DWARFYAML::emitDebugAranges(OS, DWARF); + Err = DWARFYAML::emitDebugAranges(*OS, DWARF); else if (Name == ".debug_ranges") - Err = DWARFYAML::emitDebugRanges(OS, DWARF); + Err = DWARFYAML::emitDebugRanges(*OS, DWARF); else if (Name == ".debug_line") - Err = DWARFYAML::emitDebugLine(OS, DWARF); + Err = DWARFYAML::emitDebugLine(*OS, DWARF); else llvm_unreachable("unexpected emitDWARF() call"); if (Err) return std::move(Err); - return OS.tell() - BeginOffset; + return CBA.tell() - BeginOffset; } template @@ -890,13 +958,13 @@ void ELFState::initDWARFSectionHeader(Elf_Shdr &SHeader, StringRef Name, "or 'Size' in the 'Sections' entry at the same time"); else { if (Expected ShSizeOrErr = - emitDWARF(SHeader, Name, *Doc.DWARF, CBA.getOS())) + emitDWARF(SHeader, Name, *Doc.DWARF, CBA)) SHeader.sh_size = *ShSizeOrErr; else reportError(ShSizeOrErr.takeError()); } } else if (RawSec) - SHeader.sh_size = writeContent(CBA.getOS(), RawSec->Content, RawSec->Size); + SHeader.sh_size = writeContent(CBA, RawSec->Content, RawSec->Size); else llvm_unreachable("debug sections can only be initialized via the 'DWARF' " "entry or a RawContentSection"); @@ -1012,7 +1080,7 @@ template void ELFState::writeSectionContent( Elf_Shdr &SHeader, const ELFYAML::RawContentSection &Section, ContiguousBlobAccumulator &CBA) { - SHeader.sh_size = writeContent(CBA.getOS(), Section.Content, Section.Size); + SHeader.sh_size = writeContent(CBA, Section.Content, Section.Size); if (Section.EntSize) SHeader.sh_entsize = *Section.EntSize; @@ -1052,7 +1120,6 @@ void ELFState::writeSectionContent( if (!Section.RelocatableSec.empty()) SHeader.sh_info = toSectionIndex(Section.RelocatableSec, Section.Name); - raw_ostream &OS = CBA.getOS(); for (const auto &Rel : Section.Relocations) { unsigned SymIdx = Rel.Symbol ? toSymbolIndex(*Rel.Symbol, Section.Name, Section.Link == ".dynsym") @@ -1063,13 +1130,13 @@ void ELFState::writeSectionContent( REntry.r_offset = Rel.Offset; REntry.r_addend = Rel.Addend; REntry.setSymbolAndType(SymIdx, Rel.Type, isMips64EL(Doc)); - OS.write((const char *)&REntry, sizeof(REntry)); + CBA.write((const char *)&REntry, sizeof(REntry)); } else { Elf_Rel REntry; zero(REntry); REntry.r_offset = Rel.Offset; REntry.setSymbolAndType(SymIdx, Rel.Type, isMips64EL(Doc)); - OS.write((const char *)&REntry, sizeof(REntry)); + CBA.write((const char *)&REntry, sizeof(REntry)); } } } @@ -1081,9 +1148,8 @@ void ELFState::writeSectionContent(Elf_Shdr &SHeader, SHeader.sh_entsize = Section.EntSize ? uint64_t(*Section.EntSize) : sizeof(Elf_Relr); - raw_ostream &OS = CBA.getOS(); if (Section.Content) { - SHeader.sh_size = writeContent(OS, Section.Content, None); + SHeader.sh_size = writeContent(CBA, Section.Content, None); return; } @@ -1094,7 +1160,7 @@ void ELFState::writeSectionContent(Elf_Shdr &SHeader, if (!ELFT::Is64Bits && E > UINT32_MAX) reportError(Section.Name + ": the value is too large for 32-bits: 0x" + Twine::utohexstr(E)); - support::endian::write(OS, E, ELFT::TargetEndianness); + CBA.write(E, ELFT::TargetEndianness); } SHeader.sh_size = sizeof(uintX_t) * Section.Entries->size(); @@ -1105,7 +1171,7 @@ void ELFState::writeSectionContent( Elf_Shdr &SHeader, const ELFYAML::SymtabShndxSection &Shndx, ContiguousBlobAccumulator &CBA) { for (uint32_t E : Shndx.Entries) - support::endian::write(CBA.getOS(), E, ELFT::TargetEndianness); + CBA.write(E, ELFT::TargetEndianness); SHeader.sh_entsize = Shndx.EntSize ? (uint64_t)*Shndx.EntSize : 4; SHeader.sh_size = Shndx.Entries.size() * SHeader.sh_entsize; @@ -1130,14 +1196,13 @@ void ELFState::writeSectionContent(Elf_Shdr &SHeader, SHeader.sh_info = toSymbolIndex(*Section.Signature, Section.Name, /*IsDynamic=*/false); - raw_ostream &OS = CBA.getOS(); for (const ELFYAML::SectionOrType &Member : Section.Members) { unsigned int SectionIndex = 0; if (Member.sectionNameOrType == "GRP_COMDAT") SectionIndex = llvm::ELF::GRP_COMDAT; else SectionIndex = toSectionIndex(Member.sectionNameOrType, Section.Name); - support::endian::write(OS, SectionIndex, ELFT::TargetEndianness); + CBA.write(SectionIndex, ELFT::TargetEndianness); } } @@ -1145,9 +1210,8 @@ template void ELFState::writeSectionContent(Elf_Shdr &SHeader, const ELFYAML::SymverSection &Section, ContiguousBlobAccumulator &CBA) { - raw_ostream &OS = CBA.getOS(); for (uint16_t Version : Section.Entries) - support::endian::write(OS, Version, ELFT::TargetEndianness); + CBA.write(Version, ELFT::TargetEndianness); SHeader.sh_entsize = Section.EntSize ? (uint64_t)*Section.EntSize : 2; SHeader.sh_size = Section.Entries.size() * SHeader.sh_entsize; @@ -1157,15 +1221,14 @@ template void ELFState::writeSectionContent( Elf_Shdr &SHeader, const ELFYAML::StackSizesSection &Section, ContiguousBlobAccumulator &CBA) { - raw_ostream &OS = CBA.getOS(); if (Section.Content || Section.Size) { - SHeader.sh_size = writeContent(OS, Section.Content, Section.Size); + SHeader.sh_size = writeContent(CBA, Section.Content, Section.Size); return; } for (const ELFYAML::StackSizeEntry &E : *Section.Entries) { - support::endian::write(OS, E.Address, ELFT::TargetEndianness); - SHeader.sh_size += sizeof(uintX_t) + encodeULEB128(E.Size, OS); + CBA.write(E.Address, ELFT::TargetEndianness); + SHeader.sh_size += sizeof(uintX_t) + CBA.writeULEB128(E.Size); } } @@ -1173,9 +1236,8 @@ template void ELFState::writeSectionContent( Elf_Shdr &SHeader, const ELFYAML::LinkerOptionsSection &Section, ContiguousBlobAccumulator &CBA) { - raw_ostream &OS = CBA.getOS(); if (Section.Content) { - SHeader.sh_size = writeContent(OS, Section.Content, None); + SHeader.sh_size = writeContent(CBA, Section.Content, None); return; } @@ -1183,10 +1245,10 @@ void ELFState::writeSectionContent( return; for (const ELFYAML::LinkerOption &LO : *Section.Options) { - OS.write(LO.Key.data(), LO.Key.size()); - OS.write('\0'); - OS.write(LO.Value.data(), LO.Value.size()); - OS.write('\0'); + CBA.write(LO.Key.data(), LO.Key.size()); + CBA.write('\0'); + CBA.write(LO.Value.data(), LO.Value.size()); + CBA.write('\0'); SHeader.sh_size += (LO.Key.size() + LO.Value.size() + 2); } } @@ -1195,9 +1257,8 @@ template void ELFState::writeSectionContent( Elf_Shdr &SHeader, const ELFYAML::DependentLibrariesSection &Section, ContiguousBlobAccumulator &CBA) { - raw_ostream &OS = CBA.getOS(); if (Section.Content) { - SHeader.sh_size = writeContent(OS, Section.Content, None); + SHeader.sh_size = writeContent(CBA, Section.Content, None); return; } @@ -1205,8 +1266,8 @@ void ELFState::writeSectionContent( return; for (StringRef Lib : *Section.Libs) { - OS.write(Lib.data(), Lib.size()); - OS.write('\0'); + CBA.write(Lib.data(), Lib.size()); + CBA.write('\0'); SHeader.sh_size += Lib.size() + 1; } } @@ -1231,7 +1292,7 @@ ELFState::alignToOffset(ContiguousBlobAccumulator &CBA, uint64_t Align, AlignedOffset = alignTo(CurrentOffset, std::max(Align, (uint64_t)1)); } - CBA.getOS().write_zeros(AlignedOffset - CurrentOffset); + CBA.writeZeros(AlignedOffset - CurrentOffset); return AlignedOffset; } @@ -1249,9 +1310,8 @@ void ELFState::writeSectionContent( SN2I.lookup(".symtab", Link)) SHeader.sh_link = Link; - raw_ostream &OS = CBA.getOS(); if (Section.Content) { - SHeader.sh_size = writeContent(OS, Section.Content, None); + SHeader.sh_size = writeContent(CBA, Section.Content, None); return; } @@ -1262,9 +1322,9 @@ void ELFState::writeSectionContent( unsigned From = toSymbolIndex(E.From, Section.Name, /*IsDynamic=*/false); unsigned To = toSymbolIndex(E.To, Section.Name, /*IsDynamic=*/false); - support::endian::write(OS, From, ELFT::TargetEndianness); - support::endian::write(OS, To, ELFT::TargetEndianness); - support::endian::write(OS, E.Weight, ELFT::TargetEndianness); + CBA.write(From, ELFT::TargetEndianness); + CBA.write(To, ELFT::TargetEndianness); + CBA.write(E.Weight, ELFT::TargetEndianness); SHeader.sh_size += 16; } } @@ -1278,23 +1338,22 @@ void ELFState::writeSectionContent(Elf_Shdr &SHeader, SN2I.lookup(".dynsym", Link)) SHeader.sh_link = Link; - raw_ostream &OS = CBA.getOS(); if (Section.Content || Section.Size) { - SHeader.sh_size = writeContent(OS, Section.Content, Section.Size); + SHeader.sh_size = writeContent(CBA, Section.Content, Section.Size); return; } - support::endian::write( - OS, Section.NBucket.getValueOr(llvm::yaml::Hex64(Section.Bucket->size())), + CBA.write( + Section.NBucket.getValueOr(llvm::yaml::Hex64(Section.Bucket->size())), ELFT::TargetEndianness); - support::endian::write( - OS, Section.NChain.getValueOr(llvm::yaml::Hex64(Section.Chain->size())), + CBA.write( + Section.NChain.getValueOr(llvm::yaml::Hex64(Section.Chain->size())), ELFT::TargetEndianness); for (uint32_t Val : *Section.Bucket) - support::endian::write(OS, Val, ELFT::TargetEndianness); + CBA.write(Val, ELFT::TargetEndianness); for (uint32_t Val : *Section.Chain) - support::endian::write(OS, Val, ELFT::TargetEndianness); + CBA.write(Val, ELFT::TargetEndianness); SHeader.sh_size = (2 + Section.Bucket->size() + Section.Chain->size()) * 4; } @@ -1308,9 +1367,8 @@ void ELFState::writeSectionContent(Elf_Shdr &SHeader, SHeader.sh_info = Section.Info; - raw_ostream &OS = CBA.getOS(); if (Section.Content) { - SHeader.sh_size = writeContent(OS, Section.Content, None); + SHeader.sh_size = writeContent(CBA, Section.Content, None); return; } @@ -1333,7 +1391,7 @@ void ELFState::writeSectionContent(Elf_Shdr &SHeader, else VerDef.vd_next = sizeof(Elf_Verdef) + E.VerNames.size() * sizeof(Elf_Verdaux); - OS.write((const char *)&VerDef, sizeof(Elf_Verdef)); + CBA.write((const char *)&VerDef, sizeof(Elf_Verdef)); for (size_t J = 0; J < E.VerNames.size(); ++J, ++AuxCnt) { Elf_Verdaux VernAux; @@ -1342,7 +1400,7 @@ void ELFState::writeSectionContent(Elf_Shdr &SHeader, VernAux.vda_next = 0; else VernAux.vda_next = sizeof(Elf_Verdaux); - OS.write((const char *)&VernAux, sizeof(Elf_Verdaux)); + CBA.write((const char *)&VernAux, sizeof(Elf_Verdaux)); } } @@ -1359,9 +1417,8 @@ void ELFState::writeSectionContent(Elf_Shdr &SHeader, SHeader.sh_info = Section.Info; - raw_ostream &OS = CBA.getOS(); if (Section.Content) { - SHeader.sh_size = writeContent(OS, Section.Content, None); + SHeader.sh_size = writeContent(CBA, Section.Content, None); return; } @@ -1382,7 +1439,7 @@ void ELFState::writeSectionContent(Elf_Shdr &SHeader, sizeof(Elf_Verneed) + VE.AuxV.size() * sizeof(Elf_Vernaux); VerNeed.vn_cnt = VE.AuxV.size(); VerNeed.vn_aux = sizeof(Elf_Verneed); - OS.write((const char *)&VerNeed, sizeof(Elf_Verneed)); + CBA.write((const char *)&VerNeed, sizeof(Elf_Verneed)); for (size_t J = 0; J < VE.AuxV.size(); ++J, ++AuxCnt) { const ELFYAML::VernauxEntry &VAuxE = VE.AuxV[J]; @@ -1396,7 +1453,7 @@ void ELFState::writeSectionContent(Elf_Shdr &SHeader, VernAux.vna_next = 0; else VernAux.vna_next = sizeof(Elf_Vernaux); - OS.write((const char *)&VernAux, sizeof(Elf_Vernaux)); + CBA.write((const char *)&VernAux, sizeof(Elf_Vernaux)); } } @@ -1427,7 +1484,7 @@ void ELFState::writeSectionContent(Elf_Shdr &SHeader, Flags.ases = Section.ASEs; Flags.flags1 = Section.Flags1; Flags.flags2 = Section.Flags2; - CBA.getOS().write((const char *)&Flags, sizeof(Flags)); + CBA.write((const char *)&Flags, sizeof(Flags)); } template @@ -1451,13 +1508,12 @@ void ELFState::writeSectionContent(Elf_Shdr &SHeader, else SHeader.sh_entsize = sizeof(Elf_Dyn); - raw_ostream &OS = CBA.getOS(); for (const ELFYAML::DynamicEntry &DE : Section.Entries) { - support::endian::write(OS, DE.Tag, ELFT::TargetEndianness); - support::endian::write(OS, DE.Val, ELFT::TargetEndianness); + CBA.write(DE.Tag, ELFT::TargetEndianness); + CBA.write(DE.Val, ELFT::TargetEndianness); } if (Section.Content) - Section.Content->writeAsBinary(OS); + CBA.writeAsBinary(*Section.Content); } template @@ -1469,62 +1525,57 @@ void ELFState::writeSectionContent(Elf_Shdr &SHeader, SN2I.lookup(".symtab", Link)) SHeader.sh_link = Link; - raw_ostream &OS = CBA.getOS(); if (Section.Content || Section.Size) { - SHeader.sh_size = writeContent(OS, Section.Content, Section.Size); + SHeader.sh_size = writeContent(CBA, Section.Content, Section.Size); return; } for (StringRef Sym : *Section.Symbols) - SHeader.sh_size += encodeULEB128( - toSymbolIndex(Sym, Section.Name, /*IsDynamic=*/false), OS); + SHeader.sh_size += + CBA.writeULEB128(toSymbolIndex(Sym, Section.Name, /*IsDynamic=*/false)); } template void ELFState::writeSectionContent(Elf_Shdr &SHeader, const ELFYAML::NoteSection &Section, ContiguousBlobAccumulator &CBA) { - raw_ostream &OS = CBA.getOS(); - uint64_t Offset = OS.tell(); + uint64_t Offset = CBA.tell(); if (Section.Content || Section.Size) { - SHeader.sh_size = writeContent(OS, Section.Content, Section.Size); + SHeader.sh_size = writeContent(CBA, Section.Content, Section.Size); return; } for (const ELFYAML::NoteEntry &NE : *Section.Notes) { // Write name size. if (NE.Name.empty()) - support::endian::write(OS, 0, ELFT::TargetEndianness); + CBA.write(0, ELFT::TargetEndianness); else - support::endian::write(OS, NE.Name.size() + 1, - ELFT::TargetEndianness); + CBA.write(NE.Name.size() + 1, ELFT::TargetEndianness); // Write description size. if (NE.Desc.binary_size() == 0) - support::endian::write(OS, 0, ELFT::TargetEndianness); + CBA.write(0, ELFT::TargetEndianness); else - support::endian::write(OS, NE.Desc.binary_size(), - ELFT::TargetEndianness); + CBA.write(NE.Desc.binary_size(), ELFT::TargetEndianness); // Write type. - support::endian::write(OS, NE.Type, ELFT::TargetEndianness); + CBA.write(NE.Type, ELFT::TargetEndianness); // Write name, null terminator and padding. if (!NE.Name.empty()) { - support::endian::write(OS, arrayRefFromStringRef(NE.Name), - ELFT::TargetEndianness); - support::endian::write(OS, 0, ELFT::TargetEndianness); + CBA.write(NE.Name.data(), NE.Name.size()); + CBA.write('\0'); CBA.padToAlignment(4); } // Write description and padding. if (NE.Desc.binary_size() != 0) { - NE.Desc.writeAsBinary(OS); + CBA.writeAsBinary(NE.Desc); CBA.padToAlignment(4); } } - SHeader.sh_size = OS.tell() - Offset; + SHeader.sh_size = CBA.tell() - Offset; } template @@ -1536,9 +1587,8 @@ void ELFState::writeSectionContent(Elf_Shdr &SHeader, SN2I.lookup(".dynsym", Link)) SHeader.sh_link = Link; - raw_ostream &OS = CBA.getOS(); if (Section.Content) { - SHeader.sh_size = writeContent(OS, Section.Content, None); + SHeader.sh_size = writeContent(CBA, Section.Content, None); return; } @@ -1547,42 +1597,35 @@ void ELFState::writeSectionContent(Elf_Shdr &SHeader, // be used to override this field, which is useful for producing broken // objects. if (Section.Header->NBuckets) - support::endian::write(OS, *Section.Header->NBuckets, - ELFT::TargetEndianness); + CBA.write(*Section.Header->NBuckets, ELFT::TargetEndianness); else - support::endian::write(OS, Section.HashBuckets->size(), - ELFT::TargetEndianness); + CBA.write(Section.HashBuckets->size(), ELFT::TargetEndianness); // Write the index of the first symbol in the dynamic symbol table accessible // via the hash table. - support::endian::write(OS, Section.Header->SymNdx, - ELFT::TargetEndianness); + CBA.write(Section.Header->SymNdx, ELFT::TargetEndianness); // Write the number of words in the Bloom filter. As above, the "MaskWords" // property can be used to set this field to any value. if (Section.Header->MaskWords) - support::endian::write(OS, *Section.Header->MaskWords, - ELFT::TargetEndianness); + CBA.write(*Section.Header->MaskWords, ELFT::TargetEndianness); else - support::endian::write(OS, Section.BloomFilter->size(), - ELFT::TargetEndianness); + CBA.write(Section.BloomFilter->size(), ELFT::TargetEndianness); // Write the shift constant used by the Bloom filter. - support::endian::write(OS, Section.Header->Shift2, - ELFT::TargetEndianness); + CBA.write(Section.Header->Shift2, ELFT::TargetEndianness); // We've finished writing the header. Now write the Bloom filter. for (llvm::yaml::Hex64 Val : *Section.BloomFilter) - support::endian::write(OS, Val, - ELFT::TargetEndianness); + CBA.write(Val, ELFT::TargetEndianness); // Write an array of hash buckets. for (llvm::yaml::Hex32 Val : *Section.HashBuckets) - support::endian::write(OS, Val, ELFT::TargetEndianness); + CBA.write(Val, ELFT::TargetEndianness); // Write an array of hash values. for (llvm::yaml::Hex32 Val : *Section.HashValues) - support::endian::write(OS, Val, ELFT::TargetEndianness); + CBA.write(Val, ELFT::TargetEndianness); SHeader.sh_size = 16 /*Header size*/ + Section.BloomFilter->size() * sizeof(typename ELFT::uint) + @@ -1593,18 +1636,17 @@ void ELFState::writeSectionContent(Elf_Shdr &SHeader, template void ELFState::writeFill(ELFYAML::Fill &Fill, ContiguousBlobAccumulator &CBA) { - raw_ostream &OS = CBA.getOS(); size_t PatternSize = Fill.Pattern ? Fill.Pattern->binary_size() : 0; if (!PatternSize) { - OS.write_zeros(Fill.Size); + CBA.writeZeros(Fill.Size); return; } // Fill the content with the specified pattern. uint64_t Written = 0; for (; Written + PatternSize <= Fill.Size; Written += PatternSize) - Fill.Pattern->writeAsBinary(OS); - Fill.Pattern->writeAsBinary(OS, Fill.Size - Written); + CBA.writeAsBinary(*Fill.Pattern); + CBA.writeAsBinary(*Fill.Pattern, Fill.Size - Written); } template @@ -1726,7 +1768,7 @@ template void ELFState::finalizeStrings() { template bool ELFState::writeELF(raw_ostream &OS, ELFYAML::Object &Doc, - yaml::ErrorHandler EH) { + yaml::ErrorHandler EH, uint64_t MaxSize) { ELFState State(Doc, EH); if (State.HasError) return false; @@ -1749,7 +1791,11 @@ bool ELFState::writeELF(raw_ostream &OS, ELFYAML::Object &Doc, // things to `OS`. const size_t SectionContentBeginOffset = sizeof(Elf_Ehdr) + sizeof(Elf_Phdr) * Doc.ProgramHeaders.size(); - ContiguousBlobAccumulator CBA(SectionContentBeginOffset); + // It is quite easy to accidentally create output with yaml2obj that is larger + // than intended, for example, due to an issue in the YAML description. + // We limit the maximum allowed output size, but also provide a command line + // option to change this limitation. + ContiguousBlobAccumulator CBA(SectionContentBeginOffset, MaxSize); std::vector SHeaders; State.initSectionHeaders(SHeaders, CBA); @@ -1757,10 +1803,26 @@ bool ELFState::writeELF(raw_ostream &OS, ELFYAML::Object &Doc, // Now we can decide segment offsets. State.setProgramHeaderLayout(PHeaders, SHeaders); + // Align the start of the section header table, which is written after all + // section data. + uint64_t SHOff = + State.alignToOffset(CBA, sizeof(typename ELFT::uint), /*Offset=*/None); + bool ReachedLimit = SHOff + arrayDataSize(makeArrayRef(SHeaders)) > MaxSize; + if (Error E = CBA.takeLimitError()) { + // We report a custom error message instead below. + consumeError(std::move(E)); + ReachedLimit = true; + } + + if (ReachedLimit) + State.reportError( + "the desired output size is greater than permitted. Use the " + "--max-size option to change the limit"); + if (State.HasError) return false; - State.writeELFHeader(CBA, OS); + State.writeELFHeader(OS, SHOff); writeArrayData(OS, makeArrayRef(PHeaders)); CBA.writeBlobToStream(OS); writeArrayData(OS, makeArrayRef(SHeaders)); @@ -1770,17 +1832,18 @@ bool ELFState::writeELF(raw_ostream &OS, ELFYAML::Object &Doc, namespace llvm { namespace yaml { -bool yaml2elf(llvm::ELFYAML::Object &Doc, raw_ostream &Out, ErrorHandler EH) { +bool yaml2elf(llvm::ELFYAML::Object &Doc, raw_ostream &Out, ErrorHandler EH, + uint64_t MaxSize) { bool IsLE = Doc.Header.Data == ELFYAML::ELF_ELFDATA(ELF::ELFDATA2LSB); bool Is64Bit = Doc.Header.Class == ELFYAML::ELF_ELFCLASS(ELF::ELFCLASS64); if (Is64Bit) { if (IsLE) - return ELFState::writeELF(Out, Doc, EH); - return ELFState::writeELF(Out, Doc, EH); + return ELFState::writeELF(Out, Doc, EH, MaxSize); + return ELFState::writeELF(Out, Doc, EH, MaxSize); } if (IsLE) - return ELFState::writeELF(Out, Doc, EH); - return ELFState::writeELF(Out, Doc, EH); + return ELFState::writeELF(Out, Doc, EH, MaxSize); + return ELFState::writeELF(Out, Doc, EH, MaxSize); } } // namespace yaml diff --git a/llvm/lib/ObjectYAML/yaml2obj.cpp b/llvm/lib/ObjectYAML/yaml2obj.cpp index c18fa5c..a04345f 100644 --- a/llvm/lib/ObjectYAML/yaml2obj.cpp +++ b/llvm/lib/ObjectYAML/yaml2obj.cpp @@ -19,7 +19,7 @@ namespace llvm { namespace yaml { bool convertYAML(yaml::Input &YIn, raw_ostream &Out, ErrorHandler ErrHandler, - unsigned DocNum) { + unsigned DocNum, uint64_t MaxSize) { unsigned CurDocNum = 0; do { if (++CurDocNum != DocNum) @@ -33,7 +33,7 @@ bool convertYAML(yaml::Input &YIn, raw_ostream &Out, ErrorHandler ErrHandler, } if (Doc.Elf) - return yaml2elf(*Doc.Elf, Out, ErrHandler); + return yaml2elf(*Doc.Elf, Out, ErrHandler, MaxSize); if (Doc.Coff) return yaml2coff(*Doc.Coff, Out, ErrHandler); if (Doc.MachO || Doc.FatMachO) diff --git a/llvm/test/tools/yaml2obj/ELF/output-limit.yaml b/llvm/test/tools/yaml2obj/ELF/output-limit.yaml new file mode 100644 index 0000000..74ca14e --- /dev/null +++ b/llvm/test/tools/yaml2obj/ELF/output-limit.yaml @@ -0,0 +1,55 @@ +## Check that yaml2obj limits the output size by default to 10 MB. +## Check it is possible to change this limit using the +## --max-size command line option. + +## One of the often cases to reach the limit is to create a section with a +## large portion of data. Check this case is handled properly. + +## 0x9FFEC0 = 0xA00000 (10 MB) - sizeof(Elf_Ehdr) - sizeof(Elf_Shdr) * 4. +# RUN: yaml2obj %s -DSIZE=0x9FFEC0 --docnum=1 -o /dev/null 2>&1 +# RUN: not yaml2obj %s -DSIZE=0x9FFEC1 --docnum=1 -o /dev/null 2>&1 | \ +# RUN: FileCheck %s --check-prefix=ERROR + +# ERROR: error: the desired output size is greater than permitted. Use the --max-size option to change the limit + +## We use 0xA00008 instead of 0xA00001 here because the section header table +## offset is aligned to 8 bytes, so we need to request 7 more bytes for it. +# RUN: yaml2obj %s -DSIZE=0x9FFEC1 --docnum=1 --max-size=0xA00008 -o /dev/null + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .section + Type: SHT_PROGBITS + Size: [[SIZE]] + - Name: .strtab + Type: SHT_PROGBITS + Size: 0x0 + - Name: .shstrtab + Type: SHT_PROGBITS + Size: 0x0 + +## Another possible case is when an alignment gap inserted +## is too large because of overaligning. Check it is also handled properly. +# RUN: not yaml2obj %s --docnum=2 -o /dev/null 2>&1 | FileCheck %s --check-prefix=ERROR + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .foo + Type: SHT_PROGBITS + - Name: .bar + Type: SHT_PROGBITS + AddressAlign: 0xA00100 + Size: 0x0 + +## Check that we can drop the limit with the use of --max-size=0. +# RUN: yaml2obj --max-size=0 %s --docnum=2 -o /dev/null diff --git a/llvm/tools/yaml2obj/yaml2obj.cpp b/llvm/tools/yaml2obj/yaml2obj.cpp index 8a23b22..214416d 100644 --- a/llvm/tools/yaml2obj/yaml2obj.cpp +++ b/llvm/tools/yaml2obj/yaml2obj.cpp @@ -44,6 +44,11 @@ cl::opt cl::desc("Read specified document from input (default = 1)"), cl::cat(Cat)); +static cl::opt MaxSize( + "max-size", cl::init(10 * 1024 * 1024), + cl::desc( + "Sets the maximum allowed output size (0 means no limit) [ELF only]")); + cl::opt OutputFilename("o", cl::desc("Output filename"), cl::value_desc("filename"), cl::init("-"), cl::Prefix, cl::cat(Cat)); @@ -115,7 +120,9 @@ int main(int argc, char **argv) { if (!Buffer) return 1; yaml::Input YIn(*Buffer); - if (!convertYAML(YIn, Out->os(), ErrHandler, DocNum)) + + if (!convertYAML(YIn, Out->os(), ErrHandler, DocNum, + MaxSize == 0 ? UINT64_MAX : MaxSize)) return 1; Out->keep();