diff --git a/compiler-rt/lib/profile/InstrProfilingPlatformLinux.c b/compiler-rt/lib/profile/InstrProfilingPlatformLinux.c --- a/compiler-rt/lib/profile/InstrProfilingPlatformLinux.c +++ b/compiler-rt/lib/profile/InstrProfilingPlatformLinux.c @@ -95,10 +95,13 @@ * have a fixed length. */ static int WriteOneBinaryId(ProfDataWriter *Writer, uint64_t BinaryIdLen, - const uint8_t *BinaryIdData) { + const uint8_t *BinaryIdData, + uint64_t BinaryIdPadding) { ProfDataIOVec BinaryIdIOVec[] = { {&BinaryIdLen, sizeof(uint64_t), 1, 0}, - {BinaryIdData, sizeof(uint8_t), BinaryIdLen, 0}}; + {BinaryIdData, sizeof(uint8_t), BinaryIdLen, 0}, + {NULL, sizeof(uint8_t), BinaryIdPadding, 1}, + }; if (Writer->Write(Writer, BinaryIdIOVec, sizeof(BinaryIdIOVec) / sizeof(*BinaryIdIOVec))) return -1; @@ -130,11 +133,12 @@ uint64_t BinaryIdLen = Note->n_descsz; const uint8_t *BinaryIdData = (const uint8_t *)(NoteName + RoundUp(Note->n_namesz, 4)); - if (Writer != NULL && - WriteOneBinaryId(Writer, BinaryIdLen, BinaryIdData) == -1) + uint8_t BinaryIdPadding = __llvm_profile_get_num_padding_bytes(BinaryIdLen); + if (Writer != NULL && WriteOneBinaryId(Writer, BinaryIdLen, BinaryIdData, + BinaryIdPadding) == -1) return -1; - BinaryIdSize = sizeof(BinaryIdLen) + BinaryIdLen; + BinaryIdSize = sizeof(BinaryIdLen) + BinaryIdLen + BinaryIdPadding; } return BinaryIdSize; diff --git a/llvm/lib/ProfileData/InstrProfReader.cpp b/llvm/lib/ProfileData/InstrProfReader.cpp --- a/llvm/lib/ProfileData/InstrProfReader.cpp +++ b/llvm/lib/ProfileData/InstrProfReader.cpp @@ -367,6 +367,9 @@ return error(instrprof_error::unsupported_version); BinaryIdsSize = swap(Header.BinaryIdsSize); + if (BinaryIdsSize % sizeof(uint64_t)) + return error(instrprof_error::bad_header); + CountersDelta = swap(Header.CountersDelta); NamesDelta = swap(Header.NamesDelta); auto DataSize = swap(Header.DataSize); @@ -402,6 +405,10 @@ NamesStart = Start + NamesOffset; ValueDataStart = reinterpret_cast(Start + ValueDataOffset); + const uint8_t *BufferEnd = (const uint8_t *)DataBuffer->getBufferEnd(); + if (BinaryIdsStart + BinaryIdsSize > BufferEnd) + return error(instrprof_error::bad_header); + std::unique_ptr NewSymtab = std::make_unique(); if (Error E = createSymtab(*NewSymtab.get())) return E; @@ -520,6 +527,10 @@ return success(); } +static size_t RoundUp(size_t size, size_t align) { + return (size + align - 1) & ~(align - 1); +} + template Error RawInstrProfReader::printBinaryIds(raw_ostream &OS) { if (BinaryIdsSize == 0) @@ -527,8 +538,21 @@ OS << "Binary IDs: \n"; const uint8_t *BI = BinaryIdsStart; - while (BI < BinaryIdsStart + BinaryIdsSize) { + const uint8_t *BIEnd = BinaryIdsStart + BinaryIdsSize; + while (BI < BIEnd) { + size_t Remaining = BIEnd - BI; + + // There should be enough left to read the binary ID size field. + if (Remaining < sizeof(uint64_t)) + return make_error(instrprof_error::malformed); + uint64_t BinaryIdLen = swap(*reinterpret_cast(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(instrprof_error::malformed); + // Increment by binary id length data type size. BI += sizeof(BinaryIdLen); if (BI > (const uint8_t *)DataBuffer->getBufferEnd()) @@ -538,8 +562,9 @@ OS << format("%02x", BI[I]); OS << "\n"; - // Increment by binary id data length. - BI += BinaryIdLen; + // Increment by binary id data length, rounded to the next 8 bytes. This + // accounts for the zero-padding after each build ID. + BI += RoundUp(BinaryIdLen, sizeof(uint64_t)); if (BI > (const uint8_t *)DataBuffer->getBufferEnd()) return make_error(instrprof_error::malformed); } diff --git a/llvm/test/tools/llvm-profdata/binary-ids-padding.test b/llvm/test/tools/llvm-profdata/binary-ids-padding.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-profdata/binary-ids-padding.test @@ -0,0 +1,72 @@ +// 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 '\7\0\0\0\0\0\0\0' >> %t.profraw +// There will be 2 20-byte binary IDs, so the total Binary IDs size will be 64 bytes. +// 2 * 8 binary ID sizes +// + 2 * 20 binary IDs (of size 20) +// + 2 * 4 binary ID paddings +// -------- +// = 64 bytes +RUN: printf '\100\0\0\0\0\0\0\0' >> %t.profraw +RUN: printf '\2\0\0\0\0\0\0\0' >> %t.profraw +RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw +RUN: printf '\3\0\0\0\0\0\0\0' >> %t.profraw +RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw +RUN: printf '\20\0\0\0\0\0\0\0' >> %t.profraw +RUN: printf '\0\0\4\0\1\0\0\0' >> %t.profraw +RUN: printf '\0\0\4\0\2\0\0\0' >> %t.profraw +RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw + +// Binary IDs - There are only two in this case that are 20 bytes. +RUN: printf '\24\0\0\0\0\0\0\0' >> %t.profraw +RUN: printf '\0\1\2\3\4\5\6\7' >> %t.profraw +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: printf '\24\0\0\0\0\0\0\0' >> %t.profraw +RUN: printf '\1\1\1\1\1\1\1\1' >> %t.profraw +RUN: printf '\2\2\2\2\2\2\2\2' >> %t.profraw +RUN: printf '\3\3\3\3\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 '\254\275\030\333\114\302\370\134' >> %t.profraw +RUN: printf '\1\0\0\0\0\0\0\0' >> %t.profraw +RUN: printf '\0\0\4\0\1\0\0\0' >> %t.profraw +RUN: printf '\0\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 '\067\265\035\031\112\165\023\344' >> %t.profraw +RUN: printf '\02\0\0\0\0\0\0\0' >> %t.profraw +RUN: printf '\xd8\xff\3\0\1\0\0\0' >> %t.profraw +RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw +RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw +RUN: printf '\02\0\0\0\0\0\0\0' >> %t.profraw + +RUN: printf '\023\0\0\0\0\0\0\0' >> %t.profraw +RUN: printf '\067\0\0\0\0\0\0\0' >> %t.profraw +RUN: printf '\101\0\0\0\0\0\0\0' >> %t.profraw +RUN: printf '\7\0foo\1bar\0\0\0\0\0\0\0' >> %t.profraw + +// RUN: llvm-profdata show --binary-ids %t.profraw | FileCheck %s +// CHECK: Instrumentation level: Front-end +// CHECK: Binary IDs: +// CHECK-NEXT: 0001020304050607000102030405060700010203 +// CHECK-NEXT: 0101010101010101020202020202020203030303 diff --git a/llvm/test/tools/llvm-profdata/insufficient-binary-ids-size.test b/llvm/test/tools/llvm-profdata/insufficient-binary-ids-size.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-profdata/insufficient-binary-ids-size.test @@ -0,0 +1,20 @@ +RUN: printf '\201rforpl\377' > %t.profraw +RUN: printf '\7\0\0\0\0\0\0\0' >> %t.profraw +// We should fail on this because the data buffer (profraw file) is not long +// enough to hold this binary IDs size. NOTE that this (combined with the 8-byte +// alignment requirement for binary IDs size) will ensure we can at least read one +// 8-byte size if the binary IDs are provided. +RUN: printf '\8\0\0\0\0\0\0\0' >> %t.profraw +RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw +RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw +RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw +RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw +RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw +RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw +RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw +RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw + +RUN: printf '\0\0\0\0\0\0\0' >> %t.profraw + +// RUN: not llvm-profdata show --binary-ids %t.profraw 2>&1 | FileCheck %s +// CHECK: invalid instrumentation profile data (file header is corrupt) diff --git a/llvm/test/tools/llvm-profdata/large-binary-id-size.test b/llvm/test/tools/llvm-profdata/large-binary-id-size.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-profdata/large-binary-id-size.test @@ -0,0 +1,20 @@ +RUN: printf '\201rforpl\377' > %t.profraw +RUN: printf '\7\0\0\0\0\0\0\0' >> %t.profraw +RUN: printf '\40\0\0\0\0\0\0\0' >> %t.profraw +RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw +RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw +RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw +RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw +RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw +RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw +RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw +RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw + +// Check for a corrupted size being too large past the end of the file. +RUN: printf '\7\7\7\7\7\7\7\7' >> %t.profraw +RUN: printf '\0\1\2\3\4\5\6\7' >> %t.profraw +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 diff --git a/llvm/test/tools/llvm-profdata/misaligned-binary-ids-size.test b/llvm/test/tools/llvm-profdata/misaligned-binary-ids-size.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-profdata/misaligned-binary-ids-size.test @@ -0,0 +1,25 @@ +RUN: printf '\201rforpl\377' > %t.profraw +RUN: printf '\7\0\0\0\0\0\0\0' >> %t.profraw +// We should fail on this because the binary IDs is not a multiple of 8 bytes. +RUN: printf '\77\0\0\0\0\0\0\0' >> %t.profraw +RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw +RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw +RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw +RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw +RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw +RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw +RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw +RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw + +// Binary IDs - There are only two in this case that are 20 bytes. +RUN: printf '\24\0\0\0\0\0\0\0' >> %t.profraw +RUN: printf '\0\1\2\3\4\5\6\7' >> %t.profraw +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: printf '\24\0\0\0\0\0\0\0' >> %t.profraw +RUN: printf '\1\1\1\1\1\1\1\1' >> %t.profraw +RUN: printf '\2\2\2\2\2\2\2\2' >> %t.profraw +RUN: printf '\3\3\3\3\0\0\0\0' >> %t.profraw + +// RUN: not llvm-profdata show --binary-ids %t.profraw 2>&1 | FileCheck %s +// CHECK: invalid instrumentation profile data (file header is corrupt)