[PDB] Fix use after free.

Previously MappedBlockStream owned its own BumpPtrAllocator that
it would allocate from when a read crossed a block boundary.  This
way it could still return the user a contiguous buffer of the
requested size.  However, It's not uncommon to open a stream, read
some stuff, close it, and then save the information for later.
After all, since the entire file is mapped into memory, the data
should always be available as long as the file is open.

Of course, the exception to this is when the data isn't *in* the
file, but rather in some buffer that we temporarily allocated to
present this contiguous view.  And this buffer would get destroyed
as soon as the strema was closed.

The fix here is to force the user to specify the allocator, this
way it can provide an allocator that has whatever lifetime it
chooses.

Differential Revision: https://reviews.llvm.org/D33858

llvm-svn: 304623
This commit is contained in:
Zachary Turner 2017-06-03 00:33:35 +00:00
parent 4e8624d138
commit 5b74ff33e7
15 changed files with 161 additions and 107 deletions

View File

@ -44,17 +44,19 @@ class MappedBlockStream : public BinaryStream {
public:
static std::unique_ptr<MappedBlockStream>
createStream(uint32_t BlockSize, const MSFStreamLayout &Layout,
BinaryStreamRef MsfData);
BinaryStreamRef MsfData, BumpPtrAllocator &Allocator);
static std::unique_ptr<MappedBlockStream>
createIndexedStream(const MSFLayout &Layout, BinaryStreamRef MsfData,
uint32_t StreamIndex);
uint32_t StreamIndex, BumpPtrAllocator &Allocator);
static std::unique_ptr<MappedBlockStream>
createFpmStream(const MSFLayout &Layout, BinaryStreamRef MsfData);
createFpmStream(const MSFLayout &Layout, BinaryStreamRef MsfData,
BumpPtrAllocator &Allocator);
static std::unique_ptr<MappedBlockStream>
createDirectoryStream(const MSFLayout &Layout, BinaryStreamRef MsfData);
createDirectoryStream(const MSFLayout &Layout, BinaryStreamRef MsfData,
BumpPtrAllocator &Allocator);
llvm::support::endianness getEndian() const override {
return llvm::support::little;
@ -67,9 +69,7 @@ public:
uint32_t getLength() override;
uint32_t getNumBytesCopied() const;
llvm::BumpPtrAllocator &getAllocator() { return Pool; }
llvm::BumpPtrAllocator &getAllocator() { return Allocator; }
void invalidateCache();
@ -79,7 +79,7 @@ public:
protected:
MappedBlockStream(uint32_t BlockSize, const MSFStreamLayout &StreamLayout,
BinaryStreamRef MsfData);
BinaryStreamRef MsfData, BumpPtrAllocator &Allocator);
private:
const MSFStreamLayout &getStreamLayout() const { return StreamLayout; }
@ -94,7 +94,15 @@ private:
BinaryStreamRef MsfData;
typedef MutableArrayRef<uint8_t> CacheEntry;
llvm::BumpPtrAllocator Pool;
// We just store the allocator by reference. We use this to allocate
// contiguous memory for things like arrays or strings that cross a block
// boundary, and this memory is expected to outlive the stream. For example,
// someone could create a stream, read some stuff, then close the stream, and
// we would like outstanding references to fields to remain valid since the
// entire file is mapped anyway. Because of that, the user must supply the
// allocator to allocate broken records from.
BumpPtrAllocator &Allocator;
DenseMap<uint32_t, std::vector<CacheEntry>> CacheMap;
};
@ -102,18 +110,20 @@ class WritableMappedBlockStream : public WritableBinaryStream {
public:
static std::unique_ptr<WritableMappedBlockStream>
createStream(uint32_t BlockSize, const MSFStreamLayout &Layout,
WritableBinaryStreamRef MsfData);
WritableBinaryStreamRef MsfData, BumpPtrAllocator &Allocator);
static std::unique_ptr<WritableMappedBlockStream>
createIndexedStream(const MSFLayout &Layout, WritableBinaryStreamRef MsfData,
uint32_t StreamIndex);
uint32_t StreamIndex, BumpPtrAllocator &Allocator);
static std::unique_ptr<WritableMappedBlockStream>
createDirectoryStream(const MSFLayout &Layout,
WritableBinaryStreamRef MsfData);
WritableBinaryStreamRef MsfData,
BumpPtrAllocator &Allocator);
static std::unique_ptr<WritableMappedBlockStream>
createFpmStream(const MSFLayout &Layout, WritableBinaryStreamRef MsfData);
createFpmStream(const MSFLayout &Layout, WritableBinaryStreamRef MsfData,
BumpPtrAllocator &Allocator);
llvm::support::endianness getEndian() const override {
return llvm::support::little;
@ -139,7 +149,8 @@ public:
protected:
WritableMappedBlockStream(uint32_t BlockSize,
const MSFStreamLayout &StreamLayout,
WritableBinaryStreamRef MsfData);
WritableBinaryStreamRef MsfData,
BumpPtrAllocator &Allocator);
private:
MappedBlockStream ReadInterface;

View File

@ -34,8 +34,7 @@ class TpiStream {
friend class TpiStreamBuilder;
public:
TpiStream(const PDBFile &File,
std::unique_ptr<msf::MappedBlockStream> Stream);
TpiStream(PDBFile &File, std::unique_ptr<msf::MappedBlockStream> Stream);
~TpiStream();
Error reload();
@ -61,7 +60,7 @@ public:
Error commit();
private:
const PDBFile &Pdb;
PDBFile &Pdb;
std::unique_ptr<msf::MappedBlockStream> Stream;
std::unique_ptr<codeview::LazyRandomTypeCollection> Types;

View File

@ -47,42 +47,46 @@ static Interval intersect(const Interval &I1, const Interval &I2) {
MappedBlockStream::MappedBlockStream(uint32_t BlockSize,
const MSFStreamLayout &Layout,
BinaryStreamRef MsfData)
: BlockSize(BlockSize), StreamLayout(Layout), MsfData(MsfData) {}
BinaryStreamRef MsfData,
BumpPtrAllocator &Allocator)
: BlockSize(BlockSize), StreamLayout(Layout), MsfData(MsfData),
Allocator(Allocator) {}
std::unique_ptr<MappedBlockStream>
MappedBlockStream::createStream(uint32_t BlockSize,
const MSFStreamLayout &Layout,
BinaryStreamRef MsfData) {
std::unique_ptr<MappedBlockStream> MappedBlockStream::createStream(
uint32_t BlockSize, const MSFStreamLayout &Layout, BinaryStreamRef MsfData,
BumpPtrAllocator &Allocator) {
return llvm::make_unique<MappedBlockStreamImpl<MappedBlockStream>>(
BlockSize, Layout, MsfData);
BlockSize, Layout, MsfData, Allocator);
}
std::unique_ptr<MappedBlockStream> MappedBlockStream::createIndexedStream(
const MSFLayout &Layout, BinaryStreamRef MsfData, uint32_t StreamIndex) {
const MSFLayout &Layout, BinaryStreamRef MsfData, uint32_t StreamIndex,
BumpPtrAllocator &Allocator) {
assert(StreamIndex < Layout.StreamMap.size() && "Invalid stream index");
MSFStreamLayout SL;
SL.Blocks = Layout.StreamMap[StreamIndex];
SL.Length = Layout.StreamSizes[StreamIndex];
return llvm::make_unique<MappedBlockStreamImpl<MappedBlockStream>>(
Layout.SB->BlockSize, SL, MsfData);
Layout.SB->BlockSize, SL, MsfData, Allocator);
}
std::unique_ptr<MappedBlockStream>
MappedBlockStream::createDirectoryStream(const MSFLayout &Layout,
BinaryStreamRef MsfData) {
BinaryStreamRef MsfData,
BumpPtrAllocator &Allocator) {
MSFStreamLayout SL;
SL.Blocks = Layout.DirectoryBlocks;
SL.Length = Layout.SB->NumDirectoryBytes;
return createStream(Layout.SB->BlockSize, SL, MsfData);
return createStream(Layout.SB->BlockSize, SL, MsfData, Allocator);
}
std::unique_ptr<MappedBlockStream>
MappedBlockStream::createFpmStream(const MSFLayout &Layout,
BinaryStreamRef MsfData) {
BinaryStreamRef MsfData,
BumpPtrAllocator &Allocator) {
MSFStreamLayout SL;
initializeFpmStreamLayout(Layout, SL);
return createStream(Layout.SB->BlockSize, SL, MsfData);
return createStream(Layout.SB->BlockSize, SL, MsfData, Allocator);
}
Error MappedBlockStream::readBytes(uint32_t Offset, uint32_t Size,
@ -148,7 +152,7 @@ Error MappedBlockStream::readBytes(uint32_t Offset, uint32_t Size,
// into it, and return an ArrayRef to that. Do not touch existing pool
// allocations, as existing clients may be holding a pointer which must
// not be invalidated.
uint8_t *WriteBuffer = static_cast<uint8_t *>(Pool.Allocate(Size, 8));
uint8_t *WriteBuffer = static_cast<uint8_t *>(Allocator.Allocate(Size, 8));
if (auto EC = readBytes(Offset, MutableArrayRef<uint8_t>(WriteBuffer, Size)))
return EC;
@ -269,10 +273,6 @@ Error MappedBlockStream::readBytes(uint32_t Offset,
return Error::success();
}
uint32_t MappedBlockStream::getNumBytesCopied() const {
return static_cast<uint32_t>(Pool.getBytesAllocated());
}
void MappedBlockStream::invalidateCache() { CacheMap.shrink_and_clear(); }
void MappedBlockStream::fixCacheAfterWrite(uint32_t Offset,
@ -313,43 +313,48 @@ void MappedBlockStream::fixCacheAfterWrite(uint32_t Offset,
WritableMappedBlockStream::WritableMappedBlockStream(
uint32_t BlockSize, const MSFStreamLayout &Layout,
WritableBinaryStreamRef MsfData)
: ReadInterface(BlockSize, Layout, MsfData), WriteInterface(MsfData) {}
WritableBinaryStreamRef MsfData, BumpPtrAllocator &Allocator)
: ReadInterface(BlockSize, Layout, MsfData, Allocator),
WriteInterface(MsfData) {}
std::unique_ptr<WritableMappedBlockStream>
WritableMappedBlockStream::createStream(uint32_t BlockSize,
const MSFStreamLayout &Layout,
WritableBinaryStreamRef MsfData) {
WritableBinaryStreamRef MsfData,
BumpPtrAllocator &Allocator) {
return llvm::make_unique<MappedBlockStreamImpl<WritableMappedBlockStream>>(
BlockSize, Layout, MsfData);
BlockSize, Layout, MsfData, Allocator);
}
std::unique_ptr<WritableMappedBlockStream>
WritableMappedBlockStream::createIndexedStream(const MSFLayout &Layout,
WritableBinaryStreamRef MsfData,
uint32_t StreamIndex) {
uint32_t StreamIndex,
BumpPtrAllocator &Allocator) {
assert(StreamIndex < Layout.StreamMap.size() && "Invalid stream index");
MSFStreamLayout SL;
SL.Blocks = Layout.StreamMap[StreamIndex];
SL.Length = Layout.StreamSizes[StreamIndex];
return createStream(Layout.SB->BlockSize, SL, MsfData);
return createStream(Layout.SB->BlockSize, SL, MsfData, Allocator);
}
std::unique_ptr<WritableMappedBlockStream>
WritableMappedBlockStream::createDirectoryStream(
const MSFLayout &Layout, WritableBinaryStreamRef MsfData) {
const MSFLayout &Layout, WritableBinaryStreamRef MsfData,
BumpPtrAllocator &Allocator) {
MSFStreamLayout SL;
SL.Blocks = Layout.DirectoryBlocks;
SL.Length = Layout.SB->NumDirectoryBytes;
return createStream(Layout.SB->BlockSize, SL, MsfData);
return createStream(Layout.SB->BlockSize, SL, MsfData, Allocator);
}
std::unique_ptr<WritableMappedBlockStream>
WritableMappedBlockStream::createFpmStream(const MSFLayout &Layout,
WritableBinaryStreamRef MsfData) {
WritableBinaryStreamRef MsfData,
BumpPtrAllocator &Allocator) {
MSFStreamLayout SL;
initializeFpmStreamLayout(Layout, SL);
return createStream(Layout.SB->BlockSize, SL, MsfData);
return createStream(Layout.SB->BlockSize, SL, MsfData, Allocator);
}
Error WritableMappedBlockStream::readBytes(uint32_t Offset, uint32_t Size,

View File

@ -144,7 +144,7 @@ Error DbiModuleDescriptorBuilder::commit(BinaryStreamWriter &ModiWriter,
if (Layout.ModDiStream != kInvalidStreamIndex) {
auto NS = WritableMappedBlockStream::createIndexedStream(
MsfLayout, MsfBuffer, Layout.ModDiStream);
MsfLayout, MsfBuffer, Layout.ModDiStream, MSF.getAllocator());
WritableBinaryStreamRef Ref(*NS);
BinaryStreamWriter SymbolWriter(Ref);
// Write the symbols.

View File

@ -252,7 +252,7 @@ Error DbiStream::initializeSectionHeadersData() {
return make_error<RawError>(raw_error_code::no_stream);
auto SHS = MappedBlockStream::createIndexedStream(
Pdb.getMsfLayout(), Pdb.getMsfBuffer(), StreamNum);
Pdb.getMsfLayout(), Pdb.getMsfBuffer(), StreamNum, Pdb.getAllocator());
size_t StreamLen = SHS->getLength();
if (StreamLen % sizeof(object::coff_section))
@ -284,7 +284,7 @@ Error DbiStream::initializeFpoRecords() {
return make_error<RawError>(raw_error_code::no_stream);
auto FS = MappedBlockStream::createIndexedStream(
Pdb.getMsfLayout(), Pdb.getMsfBuffer(), StreamNum);
Pdb.getMsfLayout(), Pdb.getMsfBuffer(), StreamNum, Pdb.getAllocator());
size_t StreamLen = FS->getLength();
if (StreamLen % sizeof(object::FpoData))

View File

@ -357,8 +357,8 @@ Error DbiStreamBuilder::commit(const msf::MSFLayout &Layout,
if (auto EC = finalize())
return EC;
auto DbiS = WritableMappedBlockStream::createIndexedStream(Layout, MsfBuffer,
StreamDBI);
auto DbiS = WritableMappedBlockStream::createIndexedStream(
Layout, MsfBuffer, StreamDBI, Allocator);
BinaryStreamWriter Writer(*DbiS);
if (auto EC = Writer.writeObject(*Header))
@ -396,7 +396,7 @@ Error DbiStreamBuilder::commit(const msf::MSFLayout &Layout,
if (Stream.StreamNumber == kInvalidStreamIndex)
continue;
auto WritableStream = WritableMappedBlockStream::createIndexedStream(
Layout, MsfBuffer, Stream.StreamNumber);
Layout, MsfBuffer, Stream.StreamNumber, Allocator);
BinaryStreamWriter DbgStreamWriter(*WritableStream);
if (auto EC = DbgStreamWriter.writeArray(Stream.Data))
return EC;

View File

@ -50,8 +50,8 @@ Error InfoStreamBuilder::finalizeMsfLayout() {
Error InfoStreamBuilder::commit(const msf::MSFLayout &Layout,
WritableBinaryStreamRef Buffer) const {
auto InfoS =
WritableMappedBlockStream::createIndexedStream(Layout, Buffer, StreamPDB);
auto InfoS = WritableMappedBlockStream::createIndexedStream(
Layout, Buffer, StreamPDB, Msf.getAllocator());
BinaryStreamWriter Writer(*InfoS);
InfoStreamHeader H;

View File

@ -146,7 +146,8 @@ Error PDBFile::parseFileHeaders() {
// at getBlockSize() intervals, so we have to be compatible.
// See the function fpmPn() for more information:
// https://github.com/Microsoft/microsoft-pdb/blob/master/PDB/msf/msf.cpp#L489
auto FpmStream = MappedBlockStream::createFpmStream(ContainerLayout, *Buffer);
auto FpmStream =
MappedBlockStream::createFpmStream(ContainerLayout, *Buffer, Allocator);
BinaryStreamReader FpmReader(*FpmStream);
ArrayRef<uint8_t> FpmBytes;
if (auto EC = FpmReader.readBytes(FpmBytes,
@ -184,7 +185,8 @@ Error PDBFile::parseStreamData() {
// is exactly what we are attempting to parse. By specifying a custom
// subclass of IPDBStreamData which only accesses the fields that have already
// been parsed, we can avoid this and reuse MappedBlockStream.
auto DS = MappedBlockStream::createDirectoryStream(ContainerLayout, *Buffer);
auto DS = MappedBlockStream::createDirectoryStream(ContainerLayout, *Buffer,
Allocator);
BinaryStreamReader Reader(*DS);
if (auto EC = Reader.readInteger(NumStreams))
return EC;
@ -407,5 +409,6 @@ PDBFile::safelyCreateIndexedStream(const MSFLayout &Layout,
uint32_t StreamIndex) const {
if (StreamIndex >= getNumStreams())
return make_error<RawError>(raw_error_code::no_stream);
return MappedBlockStream::createIndexedStream(Layout, MsfData, StreamIndex);
return MappedBlockStream::createIndexedStream(Layout, MsfData, StreamIndex,
Allocator);
}

View File

@ -140,8 +140,8 @@ Error PDBFileBuilder::commit(StringRef Filename) {
if (auto EC = Writer.writeArray(Layout.DirectoryBlocks))
return EC;
auto DirStream =
WritableMappedBlockStream::createDirectoryStream(Layout, Buffer);
auto DirStream = WritableMappedBlockStream::createDirectoryStream(
Layout, Buffer, Allocator);
BinaryStreamWriter DW(*DirStream);
if (auto EC = DW.writeInteger<uint32_t>(Layout.StreamSizes.size()))
return EC;
@ -158,8 +158,8 @@ Error PDBFileBuilder::commit(StringRef Filename) {
if (!ExpectedSN)
return ExpectedSN.takeError();
auto NS = WritableMappedBlockStream::createIndexedStream(Layout, Buffer,
*ExpectedSN);
auto NS = WritableMappedBlockStream::createIndexedStream(
Layout, Buffer, *ExpectedSN, Allocator);
BinaryStreamWriter NSWriter(*NS);
if (auto EC = Strings.commit(NSWriter))
return EC;

View File

@ -32,8 +32,7 @@ using namespace llvm::support;
using namespace llvm::msf;
using namespace llvm::pdb;
TpiStream::TpiStream(const PDBFile &File,
std::unique_ptr<MappedBlockStream> Stream)
TpiStream::TpiStream(PDBFile &File, std::unique_ptr<MappedBlockStream> Stream)
: Pdb(File), Stream(std::move(Stream)) {}
TpiStream::~TpiStream() = default;
@ -77,7 +76,8 @@ Error TpiStream::reload() {
"Invalid TPI hash stream index.");
auto HS = MappedBlockStream::createIndexedStream(
Pdb.getMsfLayout(), Pdb.getMsfBuffer(), Header->HashStreamIndex);
Pdb.getMsfLayout(), Pdb.getMsfBuffer(), Header->HashStreamIndex,
Pdb.getAllocator());
BinaryStreamReader HSR(*HS);
// There should be a hash value for every type record, or no hashes at all.

View File

@ -147,8 +147,8 @@ Error TpiStreamBuilder::commit(const msf::MSFLayout &Layout,
if (auto EC = finalize())
return EC;
auto InfoS =
WritableMappedBlockStream::createIndexedStream(Layout, Buffer, Idx);
auto InfoS = WritableMappedBlockStream::createIndexedStream(Layout, Buffer,
Idx, Allocator);
BinaryStreamWriter Writer(*InfoS);
if (auto EC = Writer.writeObject(*Header))
@ -159,8 +159,8 @@ Error TpiStreamBuilder::commit(const msf::MSFLayout &Layout,
return EC;
if (HashStreamIndex != kInvalidStreamIndex) {
auto HVS = WritableMappedBlockStream::createIndexedStream(Layout, Buffer,
HashStreamIndex);
auto HVS = WritableMappedBlockStream::createIndexedStream(
Layout, Buffer, HashStreamIndex, Allocator);
BinaryStreamWriter HW(*HVS);
if (HashValueStream) {
if (auto EC = HW.writeStreamRef(*HashValueStream))

View File

@ -483,8 +483,8 @@ Error LLVMOutputStyle::dumpStreamBytes() {
if (SI >= File.getNumStreams())
return make_error<RawError>(raw_error_code::no_stream);
auto S = MappedBlockStream::createIndexedStream(File.getMsfLayout(),
File.getMsfBuffer(), SI);
auto S = MappedBlockStream::createIndexedStream(
File.getMsfLayout(), File.getMsfBuffer(), SI, File.getAllocator());
if (!S)
continue;
DictScope DD(P, "Stream");
@ -791,7 +791,7 @@ Error LLVMOutputStyle::dumpDbiStream() {
if (HasModuleDI && (ShouldDumpSymbols || opts::raw::DumpLineInfo)) {
auto ModStreamData = MappedBlockStream::createIndexedStream(
File.getMsfLayout(), File.getMsfBuffer(),
Modi.getModuleStreamIndex());
Modi.getModuleStreamIndex(), File.getAllocator());
ModuleDebugStreamRef ModS(Modi, std::move(ModStreamData));
if (auto EC = ModS.reload())

View File

@ -229,7 +229,8 @@ Error YAMLOutputStyle::dumpDbiStream() {
continue;
auto ModStreamData = msf::MappedBlockStream::createIndexedStream(
File.getMsfLayout(), File.getMsfBuffer(), ModiStream);
File.getMsfLayout(), File.getMsfBuffer(), ModiStream,
File.getAllocator());
pdb::ModuleDebugStreamRef ModS(MI, std::move(ModStreamData));
if (auto EC = ModS.reload())

View File

@ -85,7 +85,7 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t *data, size_t size) {
for (auto &Modi : DS.modules()) {
auto ModStreamData = pdb::MappedBlockStream::createIndexedStream(
Modi.Info.getModuleStreamIndex(), *File);
Modi.Info.getModuleStreamIndex(), *File, File->getAllocator());
if (!ModStreamData) {
consumeError(ModStreamData.takeError());
return 0;

View File

@ -70,6 +70,8 @@ public:
return MSFStreamLayout{static_cast<uint32_t>(Data.size()), Blocks};
}
BumpPtrAllocator Allocator;
private:
std::vector<support::ulittle32_t> Blocks;
MutableArrayRef<uint8_t> Data;
@ -77,7 +79,8 @@ private:
TEST(MappedBlockStreamTest, NumBlocks) {
DiscontiguousStream F(BlocksAry, DataAry);
auto S = MappedBlockStream::createStream(F.block_size(), F.layout(), F);
auto S = MappedBlockStream::createStream(F.block_size(), F.layout(), F,
F.Allocator);
EXPECT_EQ(F.block_size(), S->getBlockSize());
EXPECT_EQ(F.layout().Blocks.size(), S->getNumBlocks());
@ -87,7 +90,8 @@ TEST(MappedBlockStreamTest, NumBlocks) {
// and does not allocate.
TEST(MappedBlockStreamTest, ReadBeyondEndOfStreamRef) {
DiscontiguousStream F(BlocksAry, DataAry);
auto S = MappedBlockStream::createStream(F.block_size(), F.layout(), F);
auto S = MappedBlockStream::createStream(F.block_size(), F.layout(), F,
F.Allocator);
BinaryStreamReader R(*S);
BinaryStreamRef SR;
@ -102,13 +106,14 @@ TEST(MappedBlockStreamTest, ReadBeyondEndOfStreamRef) {
// does not fail due to the length of the output buffer.
TEST(MappedBlockStreamTest, ReadOntoNonEmptyBuffer) {
DiscontiguousStream F(BlocksAry, DataAry);
auto S = MappedBlockStream::createStream(F.block_size(), F.layout(), F);
auto S = MappedBlockStream::createStream(F.block_size(), F.layout(), F,
F.Allocator);
BinaryStreamReader R(*S);
StringRef Str = "ZYXWVUTSRQPONMLKJIHGFEDCBA";
EXPECT_NO_ERROR(R.readFixedString(Str, 1));
EXPECT_EQ(Str, StringRef("A"));
EXPECT_EQ(0U, S->getNumBytesCopied());
EXPECT_EQ(0U, F.Allocator.getBytesAllocated());
}
// Tests that a read which crosses a block boundary, but where the subsequent
@ -116,18 +121,18 @@ TEST(MappedBlockStreamTest, ReadOntoNonEmptyBuffer) {
// not allocate memory.
TEST(MappedBlockStreamTest, ZeroCopyReadContiguousBreak) {
DiscontiguousStream F(BlocksAry, DataAry);
auto S = MappedBlockStream::createStream(F.block_size(),
F.layout(), F);
auto S = MappedBlockStream::createStream(F.block_size(), F.layout(), F,
F.Allocator);
BinaryStreamReader R(*S);
StringRef Str;
EXPECT_NO_ERROR(R.readFixedString(Str, 2));
EXPECT_EQ(Str, StringRef("AB"));
EXPECT_EQ(0U, S->getNumBytesCopied());
EXPECT_EQ(0U, F.Allocator.getBytesAllocated());
R.setOffset(6);
EXPECT_NO_ERROR(R.readFixedString(Str, 4));
EXPECT_EQ(Str, StringRef("GHIJ"));
EXPECT_EQ(0U, S->getNumBytesCopied());
EXPECT_EQ(0U, F.Allocator.getBytesAllocated());
}
// Tests that a read which crosses a block boundary and cannot be referenced
@ -135,62 +140,67 @@ TEST(MappedBlockStreamTest, ZeroCopyReadContiguousBreak) {
// requested.
TEST(MappedBlockStreamTest, CopyReadNonContiguousBreak) {
DiscontiguousStream F(BlocksAry, DataAry);
auto S = MappedBlockStream::createStream(F.block_size(), F.layout(), F);
auto S = MappedBlockStream::createStream(F.block_size(), F.layout(), F,
F.Allocator);
BinaryStreamReader R(*S);
StringRef Str;
EXPECT_NO_ERROR(R.readFixedString(Str, 10));
EXPECT_EQ(Str, StringRef("ABCDEFGHIJ"));
EXPECT_EQ(10U, S->getNumBytesCopied());
EXPECT_EQ(10U, F.Allocator.getBytesAllocated());
}
// Test that an out of bounds read which doesn't cross a block boundary
// fails and allocates no memory.
TEST(MappedBlockStreamTest, InvalidReadSizeNoBreak) {
DiscontiguousStream F(BlocksAry, DataAry);
auto S = MappedBlockStream::createStream(F.block_size(), F.layout(), F);
auto S = MappedBlockStream::createStream(F.block_size(), F.layout(), F,
F.Allocator);
BinaryStreamReader R(*S);
StringRef Str;
R.setOffset(10);
EXPECT_ERROR(R.readFixedString(Str, 1));
EXPECT_EQ(0U, S->getNumBytesCopied());
EXPECT_EQ(0U, F.Allocator.getBytesAllocated());
}
// Test that an out of bounds read which crosses a contiguous block boundary
// fails and allocates no memory.
TEST(MappedBlockStreamTest, InvalidReadSizeContiguousBreak) {
DiscontiguousStream F(BlocksAry, DataAry);
auto S = MappedBlockStream::createStream(F.block_size(), F.layout(), F);
auto S = MappedBlockStream::createStream(F.block_size(), F.layout(), F,
F.Allocator);
BinaryStreamReader R(*S);
StringRef Str;
R.setOffset(6);
EXPECT_ERROR(R.readFixedString(Str, 5));
EXPECT_EQ(0U, S->getNumBytesCopied());
EXPECT_EQ(0U, F.Allocator.getBytesAllocated());
}
// Test that an out of bounds read which crosses a discontiguous block
// boundary fails and allocates no memory.
TEST(MappedBlockStreamTest, InvalidReadSizeNonContiguousBreak) {
DiscontiguousStream F(BlocksAry, DataAry);
auto S = MappedBlockStream::createStream(F.block_size(), F.layout(), F);
auto S = MappedBlockStream::createStream(F.block_size(), F.layout(), F,
F.Allocator);
BinaryStreamReader R(*S);
StringRef Str;
EXPECT_ERROR(R.readFixedString(Str, 11));
EXPECT_EQ(0U, S->getNumBytesCopied());
EXPECT_EQ(0U, F.Allocator.getBytesAllocated());
}
// Tests that a read which is entirely contained within a single block but
// beyond the end of a StreamRef fails.
TEST(MappedBlockStreamTest, ZeroCopyReadNoBreak) {
DiscontiguousStream F(BlocksAry, DataAry);
auto S = MappedBlockStream::createStream(F.block_size(), F.layout(), F);
auto S = MappedBlockStream::createStream(F.block_size(), F.layout(), F,
F.Allocator);
BinaryStreamReader R(*S);
StringRef Str;
EXPECT_NO_ERROR(R.readFixedString(Str, 1));
EXPECT_EQ(Str, StringRef("A"));
EXPECT_EQ(0U, S->getNumBytesCopied());
EXPECT_EQ(0U, F.Allocator.getBytesAllocated());
}
// Tests that a read which is not aligned on the same boundary as a previous
@ -198,19 +208,20 @@ TEST(MappedBlockStreamTest, ZeroCopyReadNoBreak) {
// previous allocation.
TEST(MappedBlockStreamTest, UnalignedOverlappingRead) {
DiscontiguousStream F(BlocksAry, DataAry);
auto S = MappedBlockStream::createStream(F.block_size(), F.layout(), F);
auto S = MappedBlockStream::createStream(F.block_size(), F.layout(), F,
F.Allocator);
BinaryStreamReader R(*S);
StringRef Str1;
StringRef Str2;
EXPECT_NO_ERROR(R.readFixedString(Str1, 7));
EXPECT_EQ(Str1, StringRef("ABCDEFG"));
EXPECT_EQ(7U, S->getNumBytesCopied());
EXPECT_EQ(7U, F.Allocator.getBytesAllocated());
R.setOffset(2);
EXPECT_NO_ERROR(R.readFixedString(Str2, 3));
EXPECT_EQ(Str2, StringRef("CDE"));
EXPECT_EQ(Str1.data() + 2, Str2.data());
EXPECT_EQ(7U, S->getNumBytesCopied());
EXPECT_EQ(7U, F.Allocator.getBytesAllocated());
}
// Tests that a read which is not aligned on the same boundary as a previous
@ -218,18 +229,19 @@ TEST(MappedBlockStreamTest, UnalignedOverlappingRead) {
// still works correctly and allocates again from the shared pool.
TEST(MappedBlockStreamTest, UnalignedOverlappingReadFail) {
DiscontiguousStream F(BlocksAry, DataAry);
auto S = MappedBlockStream::createStream(F.block_size(), F.layout(), F);
auto S = MappedBlockStream::createStream(F.block_size(), F.layout(), F,
F.Allocator);
BinaryStreamReader R(*S);
StringRef Str1;
StringRef Str2;
EXPECT_NO_ERROR(R.readFixedString(Str1, 6));
EXPECT_EQ(Str1, StringRef("ABCDEF"));
EXPECT_EQ(6U, S->getNumBytesCopied());
EXPECT_EQ(6U, F.Allocator.getBytesAllocated());
R.setOffset(4);
EXPECT_NO_ERROR(R.readFixedString(Str2, 4));
EXPECT_EQ(Str2, StringRef("EFGH"));
EXPECT_EQ(10U, S->getNumBytesCopied());
EXPECT_EQ(10U, F.Allocator.getBytesAllocated());
}
TEST(MappedBlockStreamTest, WriteBeyondEndOfStream) {
@ -241,8 +253,8 @@ TEST(MappedBlockStreamTest, WriteBeyondEndOfStream) {
"LargeBuffer is not big enough");
DiscontiguousStream F(BlocksAry, Data);
auto S = WritableMappedBlockStream::createStream(
F.block_size(), F.layout(), F);
auto S = WritableMappedBlockStream::createStream(F.block_size(), F.layout(),
F, F.Allocator);
ArrayRef<uint8_t> Buffer;
EXPECT_ERROR(S->writeBytes(0, ArrayRef<uint8_t>(LargeBuffer)));
@ -254,8 +266,8 @@ TEST(MappedBlockStreamTest, WriteBeyondEndOfStream) {
TEST(MappedBlockStreamTest, TestWriteBytesNoBreakBoundary) {
static uint8_t Data[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'};
DiscontiguousStream F(BlocksAry, Data);
auto S = WritableMappedBlockStream::createStream(
F.block_size(), F.layout(), F);
auto S = WritableMappedBlockStream::createStream(F.block_size(), F.layout(),
F, F.Allocator);
ArrayRef<uint8_t> Buffer;
EXPECT_NO_ERROR(S->readBytes(0, 1, Buffer));
@ -287,8 +299,8 @@ TEST(MappedBlockStreamTest, TestWriteBytesBreakBoundary) {
'T', 'G', '.', '0', '0'};
DiscontiguousStream F(BlocksAry, Data);
auto S = WritableMappedBlockStream::createStream(
F.block_size(), F.layout(), F);
auto S = WritableMappedBlockStream::createStream(F.block_size(), F.layout(),
F, F.Allocator);
ArrayRef<uint8_t> Buffer;
EXPECT_NO_ERROR(S->writeBytes(0, TestData));
@ -306,8 +318,8 @@ TEST(MappedBlockStreamTest, TestWriteThenRead) {
const uint32_t Blocks[] = {2, 1, 0, 6, 3, 4, 5, 7, 9, 8};
DiscontiguousStream F(Blocks, Data);
auto S = WritableMappedBlockStream::createStream(
F.block_size(), F.layout(), F);
auto S = WritableMappedBlockStream::createStream(F.block_size(), F.layout(),
F, F.Allocator);
enum class MyEnum : uint32_t { Val1 = 2908234, Val2 = 120891234 };
using support::ulittle32_t;
@ -399,7 +411,7 @@ TEST(MappedBlockStreamTest, TestWriteContiguousStreamRef) {
DiscontiguousStream F(DestBlocks, DestData);
auto DestStream = WritableMappedBlockStream::createStream(
F.block_size(), F.layout(), F);
F.block_size(), F.layout(), F, F.Allocator);
// First write "Test Str" into the source stream.
MutableBinaryByteStream SourceStream(SrcData, little);
@ -434,9 +446,9 @@ TEST(MappedBlockStreamTest, TestWriteDiscontiguousStreamRef) {
DiscontiguousStream SrcF(SrcBlocks, SrcData);
auto Dest = WritableMappedBlockStream::createStream(
DestF.block_size(), DestF.layout(), DestF);
DestF.block_size(), DestF.layout(), DestF, DestF.Allocator);
auto Src = WritableMappedBlockStream::createStream(
SrcF.block_size(), SrcF.layout(), SrcF);
SrcF.block_size(), SrcF.layout(), SrcF, SrcF.Allocator);
// First write "Test Str" into the source stream.
BinaryStreamWriter SourceWriter(*Src);
@ -457,4 +469,27 @@ TEST(MappedBlockStreamTest, TestWriteDiscontiguousStreamRef) {
EXPECT_EQ(Result, "Test Str");
}
TEST(MappedBlockStreamTest, DataLivesAfterStreamDestruction) {
std::vector<uint8_t> DataBytes(10);
MutableArrayRef<uint8_t> Data(DataBytes);
const uint32_t Blocks[] = {2, 1, 0, 6, 3, 4, 5, 7, 9, 8};
StringRef Str[] = {"Zero Str", ""};
DiscontiguousStream F(Blocks, Data);
{
auto S = WritableMappedBlockStream::createStream(F.block_size(), F.layout(),
F, F.Allocator);
BinaryStreamReader Reader(*S);
BinaryStreamWriter Writer(*S);
::memset(DataBytes.data(), 0, 10);
EXPECT_NO_ERROR(Writer.writeCString(Str[0]));
EXPECT_NO_ERROR(Reader.readCString(Str[1]));
EXPECT_EQ(Str[0], Str[1]);
}
EXPECT_EQ(Str[0], Str[1]);
}
} // end anonymous namespace