From 24f3e102a678e2d102ac5660371fba1c2cfccd25 Mon Sep 17 00:00:00 2001 From: Owen Reynolds Date: Tue, 23 Jul 2019 14:44:21 +0000 Subject: [PATCH] [llvm-ar] Fix support for archives with members larger than 4GB llvm-ar outputs a strange error message when handling archives with members larger than 4GB due to not checking file size when passing the value as an unsigned 32 bit integer. This overflow issue caused malformed archives to be created.: https://bugs.llvm.org/show_bug.cgi?id=38058 This change allows for members above 4GB and will error in a case that is over the formats size limit, a 10 digit decimal integer. Differential Revision: https://reviews.llvm.org/D65093 llvm-svn: 366813 --- llvm/include/llvm/Object/Archive.h | 6 ++++-- llvm/lib/Object/Archive.cpp | 4 ++-- llvm/lib/Object/ArchiveWriter.cpp | 21 +++++++++++++++------ 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/llvm/include/llvm/Object/Archive.h b/llvm/include/llvm/Object/Archive.h index c40278a..8b58eae 100644 --- a/llvm/include/llvm/Object/Archive.h +++ b/llvm/include/llvm/Object/Archive.h @@ -48,8 +48,7 @@ public: /// Get the name looking up long names. Expected getName(uint64_t Size) const; - /// Members are not larger than 4GB. - Expected getSize() const; + Expected getSize() const; Expected getAccessMode() const; Expected> getLastModified() const; @@ -221,6 +220,9 @@ public: Archive(MemoryBufferRef Source, Error &Err); static Expected> create(MemoryBufferRef Source); + /// Size field is 10 decimal digits long + static const uint64_t MaxMemberSize = 9999999999; + enum Kind { K_GNU, K_GNU64, diff --git a/llvm/lib/Object/Archive.cpp b/llvm/lib/Object/Archive.cpp index 49e66f4..fd9609f 100644 --- a/llvm/lib/Object/Archive.cpp +++ b/llvm/lib/Object/Archive.cpp @@ -223,8 +223,8 @@ Expected ArchiveMemberHeader::getName(uint64_t Size) const { return Name.drop_back(1); } -Expected ArchiveMemberHeader::getSize() const { - uint32_t Ret; +Expected ArchiveMemberHeader::getSize() const { + uint64_t Ret; if (StringRef(ArMemHdr->Size, sizeof(ArMemHdr->Size)).rtrim(" ").getAsInteger(10, Ret)) { std::string Buf; diff --git a/llvm/lib/Object/ArchiveWriter.cpp b/llvm/lib/Object/ArchiveWriter.cpp index 228f6b4..f12f1e5 100644 --- a/llvm/lib/Object/ArchiveWriter.cpp +++ b/llvm/lib/Object/ArchiveWriter.cpp @@ -16,6 +16,7 @@ #include "llvm/BinaryFormat/Magic.h" #include "llvm/IR/LLVMContext.h" #include "llvm/Object/Archive.h" +#include "llvm/Object/Error.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Object/SymbolicFile.h" #include "llvm/Support/EndianStream.h" @@ -147,7 +148,7 @@ static void print(raw_ostream &Out, object::Archive::Kind Kind, T Val) { static void printRestOfMemberHeader( raw_ostream &Out, const sys::TimePoint &ModTime, - unsigned UID, unsigned GID, unsigned Perms, unsigned Size) { + unsigned UID, unsigned GID, unsigned Perms, uint64_t Size) { printWithSpacePadding(Out, sys::toTimeT(ModTime), 12); // The format has only 6 chars for uid and gid. Truncate if the provided @@ -164,7 +165,7 @@ static void printGNUSmallMemberHeader(raw_ostream &Out, StringRef Name, const sys::TimePoint &ModTime, unsigned UID, unsigned GID, unsigned Perms, - unsigned Size) { + uint64_t Size) { printWithSpacePadding(Out, Twine(Name) + "/", 16); printRestOfMemberHeader(Out, ModTime, UID, GID, Perms, Size); } @@ -172,8 +173,7 @@ printGNUSmallMemberHeader(raw_ostream &Out, StringRef Name, static void printBSDMemberHeader(raw_ostream &Out, uint64_t Pos, StringRef Name, const sys::TimePoint &ModTime, - unsigned UID, unsigned GID, unsigned Perms, - unsigned Size) { + unsigned UID, unsigned GID, unsigned Perms, uint64_t Size) { uint64_t PosAfterHeader = Pos + 60 + Name.size(); // Pad so that even 64 bit object files are aligned. unsigned Pad = OffsetToAlignment(PosAfterHeader, 8); @@ -208,7 +208,7 @@ static void printMemberHeader(raw_ostream &Out, uint64_t Pos, raw_ostream &StringTable, StringMap &MemberNames, object::Archive::Kind Kind, bool Thin, const NewArchiveMember &M, - sys::TimePoint ModTime, unsigned Size) { + sys::TimePoint ModTime, uint64_t Size) { if (isBSDLike(Kind)) return printBSDMemberHeader(Out, Pos, M.MemberName, ModTime, M.UID, M.GID, M.Perms, Size); @@ -474,8 +474,17 @@ computeMemberData(raw_ostream &StringTable, raw_ostream &SymNames, ModTime = sys::toTimePoint(FilenameCount[M.MemberName]++); else ModTime = M.ModTime; + + uint64_t Size = Buf.getBufferSize() + MemberPadding; + if (Size > object::Archive::MaxMemberSize) { + std::string StringMsg = + "File " + M.MemberName.str() + " exceeds size limit"; + return make_error( + std::move(StringMsg), object::object_error::parse_failed); + } + printMemberHeader(Out, Pos, StringTable, MemberNames, Kind, Thin, M, - ModTime, Buf.getBufferSize() + MemberPadding); + ModTime, Size); Out.flush(); Expected> Symbols = -- 2.7.4