]> granicus.if.org Git - llvm/commitdiff
[llvm-ar] Fix support for archives with members larger than 4GB
authorOwen Reynolds <gbreynoo@gmail.com>
Tue, 23 Jul 2019 14:44:21 +0000 (14:44 +0000)
committerOwen Reynolds <gbreynoo@gmail.com>
Tue, 23 Jul 2019 14:44:21 +0000 (14:44 +0000)
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

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@366813 91177308-0d34-0410-b5e6-96231b3b80d8

include/llvm/Object/Archive.h
lib/Object/Archive.cpp
lib/Object/ArchiveWriter.cpp

index c40278a4f923b72b9f46695add95e5268991f587..8b58eaeff418cb472278769fd34f52292da6f930 100644 (file)
@@ -48,8 +48,7 @@ public:
   /// Get the name looking up long names.
   Expected<StringRef> getName(uint64_t Size) const;
 
-  /// Members are not larger than 4GB.
-  Expected<uint32_t> getSize() const;
+  Expected<uint64_t> getSize() const;
 
   Expected<sys::fs::perms> getAccessMode() const;
   Expected<sys::TimePoint<std::chrono::seconds>> getLastModified() const;
@@ -221,6 +220,9 @@ public:
   Archive(MemoryBufferRef Source, Error &Err);
   static Expected<std::unique_ptr<Archive>> create(MemoryBufferRef Source);
 
+  /// Size field is 10 decimal digits long
+  static const uint64_t MaxMemberSize = 9999999999;
+
   enum Kind {
     K_GNU,
     K_GNU64,
index 49e66f46ab3f249eff64ff27233297d9201c5dd3..fd9609f65bc0bfa83eba1ebfd65881c09d6d5016 100644 (file)
@@ -223,8 +223,8 @@ Expected<StringRef> ArchiveMemberHeader::getName(uint64_t Size) const {
   return Name.drop_back(1);
 }
 
-Expected<uint32_t> ArchiveMemberHeader::getSize() const {
-  uint32_t Ret;
+Expected<uint64_t> ArchiveMemberHeader::getSize() const {
+  uint64_t Ret;
   if (StringRef(ArMemHdr->Size,
                 sizeof(ArMemHdr->Size)).rtrim(" ").getAsInteger(10, Ret)) {
     std::string Buf;
index 228f6b40c5ecd7431f5c94c51b9d9b672d5d0d8b..f12f1e521a9c02acfcb23324d5a9d88206e1eca7 100644 (file)
@@ -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<std::chrono::seconds> &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<std::chrono::seconds> &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<std::chrono::seconds> &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<uint64_t> &MemberNames, object::Archive::Kind Kind,
                   bool Thin, const NewArchiveMember &M,
-                  sys::TimePoint<std::chrono::seconds> ModTime, unsigned Size) {
+                  sys::TimePoint<std::chrono::seconds> 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<object::GenericBinaryError>(
+          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<std::vector<unsigned>> Symbols =