Skip to content

Commit ea8e10e

Browse files
committed
[GOFF] Fix buffer overflow for ED with offset
Element definitions may contain an offset, though only HLASM generates such objects. The offset of a text record is relative to the section rather than the element so this offset needs to be accounted for when unwinding text records.
1 parent bb17651 commit ea8e10e

File tree

2 files changed

+137
-23
lines changed

2 files changed

+137
-23
lines changed

llvm/lib/Object/GOFFObjectFile.cpp

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,7 @@ GOFFObjectFile::getSectionContents(DataRefImpl Sec) const {
464464
return ArrayRef<uint8_t>(Buf);
465465
}
466466
uint64_t SectionSize = getSectionSize(Sec);
467+
uint64_t SectionOffset = getSectionAddress(Sec);
467468
uint32_t DefEsdId = getSectionDefEsdId(Sec);
468469

469470
const uint8_t *EdEsdRecord = getSectionEdEsdRecord(Sec);
@@ -492,15 +493,28 @@ GOFFObjectFile::getSectionContents(DataRefImpl Sec) const {
492493
uint16_t TxtDataSize;
493494
TXTRecord::getDataLength(TxtRecordPtr, TxtDataSize);
494495

495-
LLVM_DEBUG(dbgs() << "Record offset " << TxtDataOffset << ", data size "
496+
LLVM_DEBUG(dbgs() << "Section offset " << SectionOffset
497+
<< ", Record offset " << TxtDataOffset << ", data size "
496498
<< TxtDataSize << "\n");
497499

498500
SmallString<256> CompleteData;
499501
CompleteData.reserve(TxtDataSize);
500502
if (Error Err = TXTRecord::getData(TxtRecordPtr, CompleteData))
501503
return std::move(Err);
502504
assert(CompleteData.size() == TxtDataSize && "Wrong length of data");
503-
std::copy(CompleteData.data(), CompleteData.data() + TxtDataSize,
505+
506+
if (SectionOffset > TxtDataOffset)
507+
return createStringError(
508+
object_error::parse_failed,
509+
"TXT record offset too low for element with ESDID " +
510+
Twine(TxtEsdId));
511+
TxtDataOffset -= SectionOffset;
512+
if (Data.size() < TxtDataOffset + TxtDataSize)
513+
return createStringError(object_error::parse_failed,
514+
"TXT record overflows element with ESDID " +
515+
Twine(TxtEsdId));
516+
517+
std::copy(CompleteData.begin(), CompleteData.end(),
504518
Data.begin() + TxtDataOffset);
505519
}
506520
auto &Cache = SectionDataCache[Sec.d.a];

llvm/unittests/Object/GOFFObjectFileTest.cpp

Lines changed: 121 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "llvm/Object/GOFFObjectFile.h"
10-
#include "llvm/Support/MemoryBuffer.h"
10+
#include "llvm/ADT/StringExtras.h"
1111
#include "llvm/Testing/Support/Error.h"
1212
#include "gtest/gtest.h"
1313

@@ -16,45 +16,58 @@ using namespace llvm::object;
1616
using namespace llvm::GOFF;
1717

1818
namespace {
19-
char GOFFData[GOFF::RecordLength * 3] = {0x00};
19+
struct TestData {
20+
std::vector<uint8_t> Data;
2021

21-
void constructValidGOFF(size_t Size) {
22-
StringRef ValidSize(GOFFData, Size);
22+
TestData() = default;
23+
TestData(size_t Records) : Data(Records * GOFF::RecordLength, 0) {}
24+
MemoryBufferRef getMemoryBuffer() const {
25+
return MemoryBufferRef(toStringRef(Data), "dummyGOFF");
26+
}
27+
MutableArrayRef<uint8_t> operator[](size_t Index) {
28+
return MutableArrayRef(Data.data() + (GOFF::RecordLength * Index),
29+
GOFF::RecordLength);
30+
}
31+
};
32+
33+
void assertValidGOFF(TestData Data) {
2334
Expected<std::unique_ptr<ObjectFile>> GOFFObjOrErr =
24-
object::ObjectFile::createGOFFObjectFile(
25-
MemoryBufferRef(ValidSize, "dummyGOFF"));
35+
object::ObjectFile::createGOFFObjectFile(Data.getMemoryBuffer());
2636

2737
ASSERT_THAT_EXPECTED(GOFFObjOrErr, Succeeded());
2838
}
2939

30-
void constructInvalidGOFF(size_t Size) {
31-
// Construct GOFFObject with record of length != multiple of 80.
32-
StringRef InvalidData(GOFFData, Size);
40+
void assertInvalidGOFF(TestData Data) {
3341
Expected<std::unique_ptr<ObjectFile>> GOFFObjOrErr =
34-
object::ObjectFile::createGOFFObjectFile(
35-
MemoryBufferRef(InvalidData, "dummyGOFF"));
42+
object::ObjectFile::createGOFFObjectFile(Data.getMemoryBuffer());
3643

3744
ASSERT_THAT_EXPECTED(
3845
GOFFObjOrErr,
3946
FailedWithMessage("object file is not the right size. Must be a multiple "
4047
"of 80 bytes, but is " +
41-
std::to_string(Size) + " bytes"));
48+
std::to_string(Data.Data.size()) + " bytes"));
4249
}
4350
} // namespace
4451

4552
TEST(GOFFObjectFileTest, ConstructGOFFObjectValidSize) {
46-
GOFFData[0] = (char)0x03;
47-
GOFFData[1] = (char)0xF0;
48-
GOFFData[80] = (char)0x03;
49-
GOFFData[81] = (char)0x40;
50-
constructValidGOFF(160);
51-
constructValidGOFF(0);
53+
TestData Records(2);
54+
Records[0][0] = 0x03;
55+
Records[0][1] = 0xF0;
56+
Records[1][0] = 0x03;
57+
Records[1][1] = 0x40;
58+
assertValidGOFF(Records);
59+
Records.Data.resize(0);
60+
assertValidGOFF(Records);
5261
}
5362

5463
TEST(GOFFObjectFileTest, ConstructGOFFObjectInvalidSize) {
55-
constructInvalidGOFF(70);
56-
constructInvalidGOFF(79);
57-
constructInvalidGOFF(81);
64+
TestData Records;
65+
Records.Data.resize(70);
66+
assertInvalidGOFF(Records);
67+
Records.Data.resize(79);
68+
assertInvalidGOFF(Records);
69+
Records.Data.resize(81);
70+
assertInvalidGOFF(Records);
5871
}
5972

6073
TEST(GOFFObjectFileTest, MissingHDR) {
@@ -599,3 +612,90 @@ TEST(GOFFObjectFileTest, TXTConstruct) {
599612
StringRef Contents = SectionContent.get();
600613
EXPECT_EQ(Contents, "\x12\x34\x56\x78\x9a\xbc\xde\xf0");
601614
}
615+
616+
// Test elements with an offset as produced by HLASM.
617+
TEST(GOFFObjectFileTest, EdWithOffset) {
618+
TestData Records(5);
619+
620+
// HDR record.
621+
auto Hdr = Records[0];
622+
Hdr[0] = 0x03;
623+
Hdr[1] = 0xF0;
624+
Hdr[50] = 0x01;
625+
626+
// SD ESD record.
627+
auto Sd = Records[1];
628+
Sd[0] = 0x03;
629+
Sd[3] = 0x00; // SD symbol type.
630+
Sd[7] = 0x01; // ESDID.
631+
Sd[71] = 0x04; // Symbol name length.
632+
Sd[72] = 0xa5; // Symbol name is T.
633+
Sd[73] = 0x81; // Symbol name is E.
634+
Sd[74] = 0x99; // Symbol name is S.
635+
Sd[75] = 0x7b; // Symbol name is T.
636+
637+
// ED ESD record.
638+
auto Ed = Records[2];
639+
Ed[0] = 0x03;
640+
Ed[3] = 0x01; // ED symbol type.
641+
Ed[7] = 0x02; // ESDID.
642+
Ed[11] = 0x01; // Parent ESDID.
643+
Ed[18] = 0x01; // Offset is 4096.
644+
Ed[19] = 0x00; // Offset is 4096.
645+
Ed[27] = 0x08; // Element length is 8.
646+
Ed[40] = 0x01; // Name Space ID.
647+
Ed[41] = 0x80;
648+
Ed[60] = 0x04; // Addressing mode is AMODE(64).
649+
Ed[61] = 0x03; // Residence mode is AMODE(31).
650+
Ed[63] |= 0x60; // Tasking behaviour is RENT.
651+
Ed[63] |= 0x08; // Element is read-only.
652+
Ed[63] |= 0x02; // Element is executable.
653+
Ed[66] = 0x03; // Element has doubleword alignment.
654+
Ed[71] = 0x06; // Size of symbol name.
655+
Ed[72] = 0xc3; // Symbol name is B.
656+
Ed[73] = 0x6d; // Symbol name is _.
657+
Ed[74] = 0xc3; // Symbol name is T.
658+
Ed[75] = 0xd6; // Symbol name is E.
659+
Ed[76] = 0xc4; // Symbol name is X.
660+
Ed[77] = 0xc5; // Symbol name is T.
661+
662+
// TXT record.
663+
auto Txt = Records[3];
664+
Txt[0] = 0x03;
665+
Txt[1] = 0x10;
666+
Txt[7] = 0x02; // Owning ESDID is the ED.
667+
Txt[14] = 0x01; // Offset is 4096.
668+
Txt[15] = 0x00; // Offset is 4096.
669+
Txt[23] = 0x08; // Data Length.
670+
Txt[24] = 0x12;
671+
Txt[25] = 0x34;
672+
Txt[26] = 0x56;
673+
Txt[27] = 0x78;
674+
Txt[28] = 0x9a;
675+
Txt[29] = 0xbc;
676+
Txt[30] = 0xde;
677+
Txt[31] = 0xf0;
678+
679+
// END record.
680+
auto End = Records[4];
681+
End[0] = 0x03;
682+
End[1] = 0x40;
683+
End[11] = 0x05;
684+
685+
Expected<std::unique_ptr<ObjectFile>> GOFFObjOrErr =
686+
object::ObjectFile::createGOFFObjectFile(Records.getMemoryBuffer());
687+
688+
ASSERT_THAT_EXPECTED(GOFFObjOrErr, Succeeded());
689+
690+
GOFFObjectFile *GOFFObj = dyn_cast<GOFFObjectFile>((*GOFFObjOrErr).get());
691+
auto Symbols = GOFFObj->symbols();
692+
ASSERT_EQ(std::distance(Symbols.begin(), Symbols.end()), 0);
693+
694+
auto Sections = GOFFObj->sections();
695+
ASSERT_EQ(std::distance(Sections.begin(), Sections.end()), 1);
696+
SectionRef Section = *Sections.begin();
697+
Expected<StringRef> SectionContent = Section.getContents();
698+
ASSERT_THAT_EXPECTED(SectionContent, Succeeded());
699+
StringRef Contents = SectionContent.get();
700+
EXPECT_EQ(Contents, "\x12\x34\x56\x78\x9a\xbc\xde\xf0");
701+
}

0 commit comments

Comments
 (0)