diff --git a/llvm/include/llvm/Object/Archive.h b/llvm/include/llvm/Object/Archive.h --- a/llvm/include/llvm/Object/Archive.h +++ b/llvm/include/llvm/Object/Archive.h @@ -410,9 +410,7 @@ BigArchive(MemoryBufferRef Source, Error &Err); uint64_t getFirstChildOffset() const override { return FirstChildOffset; } uint64_t getLastChildOffset() const { return LastChildOffset; } - bool isEmpty() const override { - return Data.getBufferSize() == sizeof(FixLenHdr); - }; + bool isEmpty() const override { return getFirstChildOffset() == 0; } }; } // end namespace object diff --git a/llvm/lib/Object/Archive.cpp b/llvm/lib/Object/Archive.cpp --- a/llvm/lib/Object/Archive.cpp +++ b/llvm/lib/Object/Archive.cpp @@ -135,6 +135,13 @@ return; ErrorAsOutParameter ErrAsOutParam(Err); + if (RawHeaderPtr + getSizeOf() >= Parent->getData().end()) { + if (Err) + *Err = malformedError("malformed AIX big archive: remaining buffer is " + "unable to contain next archive member"); + return; + } + if (Size < getSizeOf()) { Error SubErr = createMemberHeaderParseError(this, RawHeaderPtr, Size); if (Err) @@ -1172,6 +1179,14 @@ ErrorAsOutParameter ErrAsOutParam(&Err); StringRef Buffer = Data.getBuffer(); ArFixLenHdr = reinterpret_cast(Buffer.data()); + uint64_t BufferSize = Data.getBufferSize(); + + if (BufferSize < sizeof(FixLenHdr)) { + Err = malformedError("malformed AIX big archive: incomplete fixed length " + "header, the archive is only" + + Twine(BufferSize) + " byte(s)"); + return; + } StringRef RawOffset = getFieldRawString(ArFixLenHdr->FirstChildOffset); if (RawOffset.getAsInteger(10, FirstChildOffset)) @@ -1198,7 +1213,6 @@ return; if (GlobSymOffset > 0) { - uint64_t BufferSize = Data.getBufferSize(); uint64_t GlobalSymTblContentOffset = GlobSymOffset + sizeof(BigArMemHdrType); if (GlobalSymTblContentOffset > BufferSize) { diff --git a/llvm/test/Object/archive-big-malformed-first-member.test b/llvm/test/Object/archive-big-malformed-first-member.test new file mode 100644 --- /dev/null +++ b/llvm/test/Object/archive-big-malformed-first-member.test @@ -0,0 +1,15 @@ +## Test reading an archive with malformed header. +# RUN: echo "" > %t.a +# RUN: not llvm-ar tv %t.a 2>&1 | FileCheck --check-prefix=CHECK-HEADER %s +# CHECK-HEADER: truncated or malformed archive{{.*}}malformed AIX big archive: incomplete fixed length header + +## Test reading an empty archive with first member's offset is not zero. +# RUN: echo "" > %t.a +# RUN: echo -n "0 0 0 128 0 0 " >> %t.a +# RUN: not llvm-ar tv %t.a 2>&1 | FileCheck %s + +# RUN: echo "" > %t.a +# RUN: echo -n "0 0 0 28 0 0 " >> %t.a +# RUN: not llvm-ar tv %t.a 2>&1 | FileCheck %s + +# CHECK: truncated or malformed archive{{.*}}malformed AIX big archive: remaining buffer is unable to contain next archive member diff --git a/llvm/test/Object/archive-big-read-empty-with-freelist.test b/llvm/test/Object/archive-big-read-empty-with-freelist.test --- a/llvm/test/Object/archive-big-read-empty-with-freelist.test +++ b/llvm/test/Object/archive-big-read-empty-with-freelist.test @@ -1,3 +1,3 @@ -# Test reading an empty archive with free list in it. -# RUN: not llvm-ar tv %p/Inputs/aix-empty-big-archive-with-freelist.a 2>&1 \ -# RUN: | grep 'truncated or malformed archive' +## Test reading an empty archive with free list in it, should exit normally. +# RUN: llvm-ar tv %p/Inputs/aix-empty-big-archive-with-freelist.a 2>&1 \ +# RUN: | count 0