71 changes: 51 additions & 20 deletions llvm/lib/ProfileData/InstrProfReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -204,13 +204,15 @@ TextInstrProfReader::readValueProfileData(InstrProfRecord &Record) {
return success();
}
if (NumValueKinds == 0 || NumValueKinds > IPVK_Last + 1)
return error(instrprof_error::malformed);
return error(instrprof_error::malformed,
"number of value kinds is invalid");
Line++;

for (uint32_t VK = 0; VK < NumValueKinds; VK++) {
VP_READ_ADVANCE(ValueKind);
if (ValueKind > IPVK_Last)
return error(instrprof_error::malformed);
return error(instrprof_error::malformed, "value kind is invalid");
;
VP_READ_ADVANCE(NumValueSites);
if (!NumValueSites)
continue;
Expand Down Expand Up @@ -268,16 +270,18 @@ Error TextInstrProfReader::readNextRecord(NamedInstrProfRecord &Record) {
if (Line.is_at_end())
return error(instrprof_error::truncated);
if ((Line++)->getAsInteger(0, Record.Hash))
return error(instrprof_error::malformed);
return error(instrprof_error::malformed,
"function hash is not a valid integer");

// Read the number of counters.
uint64_t NumCounters;
if (Line.is_at_end())
return error(instrprof_error::truncated);
if ((Line++)->getAsInteger(10, NumCounters))
return error(instrprof_error::malformed);
return error(instrprof_error::malformed,
"number of counters is not a valid integer");
if (NumCounters == 0)
return error(instrprof_error::malformed);
return error(instrprof_error::malformed, "number of counters is zero");

// Read each counter and fill our internal storage with the values.
Record.Clear();
Expand All @@ -287,7 +291,7 @@ Error TextInstrProfReader::readNextRecord(NamedInstrProfRecord &Record) {
return error(instrprof_error::truncated);
uint64_t Count;
if ((Line++)->getAsInteger(10, Count))
return error(instrprof_error::malformed);
return error(instrprof_error::malformed, "count is invalid");
Record.Counts.push_back(Count);
}

Expand Down Expand Up @@ -332,10 +336,12 @@ Error RawInstrProfReader<IntPtrT>::readNextHeader(const char *CurrentPos) {
// If there isn't enough space for another header, this is probably just
// garbage at the end of the file.
if (CurrentPos + sizeof(RawInstrProf::Header) > End)
return make_error<InstrProfError>(instrprof_error::malformed);
return make_error<InstrProfError>(instrprof_error::malformed,
"not enough space for another header");
// The writer ensures each profile is padded to start at an aligned address.
if (reinterpret_cast<size_t>(CurrentPos) % alignof(uint64_t))
return make_error<InstrProfError>(instrprof_error::malformed);
return make_error<InstrProfError>(instrprof_error::malformed,
"insufficient padding");
// The magic should have the same byte order as in the previous header.
uint64_t Magic = *reinterpret_cast<const uint64_t *>(CurrentPos);
if (Magic != swap(RawInstrProf::getMagic<IntPtrT>()))
Expand Down Expand Up @@ -433,17 +439,18 @@ template <class IntPtrT>
Error RawInstrProfReader<IntPtrT>::readRawCounts(
InstrProfRecord &Record) {
uint32_t NumCounters = swap(Data->NumCounters);
IntPtrT CounterPtr = Data->CounterPtr;
if (NumCounters == 0)
return error(instrprof_error::malformed);
return error(instrprof_error::malformed, "number of counters is zero");

IntPtrT CounterPtr = Data->CounterPtr;
auto *NamesStartAsCounter = reinterpret_cast<const uint64_t *>(NamesStart);
ptrdiff_t MaxNumCounters = NamesStartAsCounter - CountersStart;

// Check bounds. Note that the counter pointer embedded in the data record
// may itself be corrupt.
if (MaxNumCounters < 0 || NumCounters > (uint32_t)MaxNumCounters)
return error(instrprof_error::malformed);
return error(instrprof_error::malformed,
"counter pointer is out of bounds");

// We need to compute the in-buffer counter offset from the in-memory address
// distance. The initial CountersDelta is the in-memory address difference
Expand All @@ -453,9 +460,26 @@ Error RawInstrProfReader<IntPtrT>::readRawCounts(
// CountersDelta decreases as we advance to the next data record.
ptrdiff_t CounterOffset = getCounterOffset(CounterPtr);
CountersDelta -= sizeof(*Data);
if (CounterOffset < 0 || CounterOffset > MaxNumCounters ||
((uint32_t)CounterOffset + NumCounters) > (uint32_t)MaxNumCounters)
return error(instrprof_error::malformed);
if (CounterOffset < 0)
return error(
instrprof_error::malformed,
("counter offset(" + Twine(CounterOffset) + ")" + " is < 0").str());

if (CounterOffset > MaxNumCounters)
return error(instrprof_error::malformed,
("counter offset(" + Twine(CounterOffset) + ")" + " > " +
"max number of counters(" + Twine((uint32_t)MaxNumCounters) +
")")
.str());

if (((uint32_t)CounterOffset + NumCounters) > (uint32_t)MaxNumCounters)
return error(instrprof_error::malformed,
("number of counters is out of bounds(counter offset(" +
Twine((uint32_t)CounterOffset) + ") + " +
"number of counters(" + Twine(NumCounters) + ") > " +
"max number of counters(" + Twine((uint32_t)MaxNumCounters) +
"))")
.str());

auto RawCounts = makeArrayRef(getCounter(CounterOffset), NumCounters);

Expand Down Expand Up @@ -544,19 +568,24 @@ Error RawInstrProfReader<IntPtrT>::printBinaryIds(raw_ostream &OS) {

// There should be enough left to read the binary ID size field.
if (Remaining < sizeof(uint64_t))
return make_error<InstrProfError>(instrprof_error::malformed);
return make_error<InstrProfError>(
instrprof_error::malformed,
"not enough data to read binary id length");

uint64_t BinaryIdLen = swap(*reinterpret_cast<const uint64_t *>(BI));

// There should be enough left to read the binary ID size field, and the
// binary ID.
if (Remaining < sizeof(BinaryIdLen) + BinaryIdLen)
return make_error<InstrProfError>(instrprof_error::malformed);
return make_error<InstrProfError>(
instrprof_error::malformed, "not enough data to read binary id data");

// Increment by binary id length data type size.
BI += sizeof(BinaryIdLen);
if (BI > (const uint8_t *)DataBuffer->getBufferEnd())
return make_error<InstrProfError>(instrprof_error::malformed);
return make_error<InstrProfError>(
instrprof_error::malformed,
"binary id that is read is bigger than buffer size");

for (uint64_t I = 0; I < BinaryIdLen; I++)
OS << format("%02x", BI[I]);
Expand Down Expand Up @@ -657,7 +686,8 @@ Error InstrProfReaderIndex<HashTableImpl>::getRecords(

Data = (*Iter);
if (Data.empty())
return make_error<InstrProfError>(instrprof_error::malformed);
return make_error<InstrProfError>(instrprof_error::malformed,
"profile data is empty");

return Error::success();
}
Expand All @@ -671,7 +701,8 @@ Error InstrProfReaderIndex<HashTableImpl>::getRecords(
Data = *RecordIterator;

if (Data.empty())
return make_error<InstrProfError>(instrprof_error::malformed);
return make_error<InstrProfError>(instrprof_error::malformed,
"profile data is empty");

return Error::success();
}
Expand Down Expand Up @@ -702,7 +733,7 @@ class InstrProfReaderNullRemapper : public InstrProfReaderRemapper {
return Underlying.getRecords(FuncName, Data);
}
};
}
} // namespace

/// A remapper that applies remappings based on a symbol remapping file.
template <typename HashTableImpl>
Expand Down
2 changes: 1 addition & 1 deletion llvm/test/tools/llvm-profdata/large-binary-id-size.test
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ RUN: printf '\0\1\2\3\4\5\6\7' >> %t.profraw
RUN: printf '\0\1\2\3\0\0\0\0' >> %t.profraw

// RUN: not llvm-profdata show --binary-ids %t.profraw 2>&1 | FileCheck %s
// CHECK: malformed instrumentation profile data
// CHECK: malformed instrumentation profile data: 'not enough data to read binary id data'
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Header
//
// INSTR_PROF_RAW_HEADER(uint64_t, Magic, __llvm_profile_get_magic())
// INSTR_PROF_RAW_HEADER(uint64_t, Version, __llvm_profile_get_version())
// INSTR_PROF_RAW_HEADER(uint64_t, BinaryIdsSize, __llvm_write_binary_ids(NULL))
// INSTR_PROF_RAW_HEADER(uint64_t, DataSize, DataSize)
// INSTR_PROF_RAW_HEADER(uint64_t, CountersSize, CountersSize)
// INSTR_PROF_RAW_HEADER(uint64_t, NamesSize, NamesSize)
// INSTR_PROF_RAW_HEADER(uint64_t, CountersDelta, (uintptr_t)CountersBegin)
// INSTR_PROF_RAW_HEADER(uint64_t, NamesDelta, (uintptr_t)NamesBegin)
// INSTR_PROF_RAW_HEADER(uint64_t, ValueKindLast, IPVK_Last)

RUN: printf '\201rforpl\377' > %t.profraw
RUN: printf '\x8\0\0\0\0\0\0\0' >> %t.profraw
RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw
RUN: printf '\1\0\0\0\0\0\0\0' >> %t.profraw
RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw
RUN: printf '\1\0\0\0\0\0\0\0' >> %t.profraw
RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw
RUN: printf '\xe\0\0\0\0\0\0\0' >> %t.profraw
RUN: printf '\xf8\xff\xff\xff\xff\xff\xff\xff' >> %t.profraw
RUN: printf '\x89\x7a\x40\x00\x00\x00\x00\x00' >> %t.profraw
RUN: printf '\1\0\0\0\0\0\0\0' >> %t.profraw

// Data Section
//
// struct ProfData {
// #define INSTR_PROF_DATA(Type, LLVMType, Name, Initializer) \
// Type Name;
// #include "llvm/ProfileData/InstrProfData.inc"
// };

RUN: printf '\xfa\xd5\x8d\xe7\x36\x64\x95\xdb' >> %t.profraw // NameRef
RUN: printf '\x18\0\0\0\0\0\0\0' >> %t.profraw // FuncHash
RUN: printf '\xf8\xff\xff\xff\xff\xff\xff\xff' >> %t.profraw // RelativeCounterPtr
RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw // FunctionPointer
RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw // Values
RUN: printf '\1\0\0\0\0\0\0\0' >> %t.profraw // NumCounters and NumValueSites

// Counter section
RUN: printf '\1\0\0\0\0\0\0\0' >> %t.profraw

// Name section (Name section is 14 bytes and 2 bytes padding is added)
RUN: printf '\x04\x0c\x78\xda\xcb\x4d\xcc\xcc' >> %t.profraw
RUN: printf '\x03\x00\x04\x1b\x01\xa6\x00\x00' >> %t.profraw

// Write some garbage data at the end so we get "not enough space for another header" message
RUN: printf '\x03\x00\' >> %t.profraw

RUN: not llvm-profdata show %t.profraw 2>&1 | FileCheck %s
CHECK: malformed instrumentation profile data: 'not enough space for another header'
49 changes: 49 additions & 0 deletions llvm/test/tools/llvm-profdata/malformed-num-counters-zero.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Header
//
// INSTR_PROF_RAW_HEADER(uint64_t, Magic, __llvm_profile_get_magic())
// INSTR_PROF_RAW_HEADER(uint64_t, Version, __llvm_profile_get_version())
// INSTR_PROF_RAW_HEADER(uint64_t, BinaryIdsSize, __llvm_write_binary_ids(NULL))
// INSTR_PROF_RAW_HEADER(uint64_t, DataSize, DataSize)
// INSTR_PROF_RAW_HEADER(uint64_t, CountersSize, CountersSize)
// INSTR_PROF_RAW_HEADER(uint64_t, NamesSize, NamesSize)
// INSTR_PROF_RAW_HEADER(uint64_t, CountersDelta, (uintptr_t)CountersBegin)
// INSTR_PROF_RAW_HEADER(uint64_t, NamesDelta, (uintptr_t)NamesBegin)
// INSTR_PROF_RAW_HEADER(uint64_t, ValueKindLast, IPVK_Last)

RUN: printf '\201rforpl\377' > %t.profraw
RUN: printf '\x8\0\0\0\0\0\0\0' >> %t.profraw
RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw
RUN: printf '\1\0\0\0\0\0\0\0' >> %t.profraw
RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw
RUN: printf '\1\0\0\0\0\0\0\0' >> %t.profraw
RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw
RUN: printf '\xe\0\0\0\0\0\0\0' >> %t.profraw
RUN: printf '\xf8\xff\xff\xff\xff\xff\xff\xff' >> %t.profraw
RUN: printf '\x89\x7a\x40\x00\x00\x00\x00\x00' >> %t.profraw
RUN: printf '\1\0\0\0\0\0\0\0' >> %t.profraw

// Data Section
//
// struct ProfData {
// #define INSTR_PROF_DATA(Type, LLVMType, Name, Initializer) \
// Type Name;
// #include "llvm/ProfileData/InstrProfData.inc"
// };

RUN: printf '\xfa\xd5\x8d\xe7\x36\x64\x95\xdb' >> %t.profraw // NameRef
RUN: printf '\x18\0\0\0\0\0\0\0' >> %t.profraw // FuncHash
RUN: printf '\xf8\xff\xff\xff\xff\xff\xff\xff' >> %t.profraw // RelativeCounterPtr
RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw // FunctionPointer
RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw // Values
// Make NumCounters = 0 so that we get "number of counters is zero" error message
RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw // NumCounters and NumValueSites

// Counter section
RUN: printf '\1\0\0\0\0\0\0\0' >> %t.profraw

// Name section (Name section is 14 bytes and 2 bytes padding is added)
RUN: printf '\x04\x0c\x78\xda\xcb\x4d\xcc\xcc' >> %t.profraw
RUN: printf '\x03\x00\x04\x1b\x01\xa6\x00\x00' >> %t.profraw

RUN: not llvm-profdata show %t.profraw 2>&1 | FileCheck %s
CHECK: malformed instrumentation profile data: 'number of counters is zero'
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,5 @@ RUN: printf '\101\0\0\0\0\0\0\0' >> %t.profraw
RUN: printf '\3\0bar\0\0\0' >> %t.profraw

RUN: not llvm-profdata merge -o /dev/null %t.profraw 2>&1 | FileCheck %s
CHECK: warning: {{.+}}: malformed instrumentation profile data
CHECK: warning: {{.+}}: malformed instrumentation profile data: 'number of counters is out of bounds(counter offset(1) + number of counters(2) > max number of counters(2))'
CHECK: error: no profile can be merged