From b1f29cec251188a594148f7a53d063281d4ef155 Mon Sep 17 00:00:00 2001 From: Pavel Labath Date: Tue, 27 Aug 2019 11:24:08 +0000 Subject: Add error handling to the DataExtractor class Summary: This is motivated by D63591, where we realized that there isn't a really good way of telling whether a DataExtractor is reading actual data, or is it just returning default values because it reached the end of the buffer. This patch resolves that by providing a new "Cursor" class. A Cursor object encapsulates two things: - the current position/offset in the DataExtractor - an error object Storing the error object inside the Cursor enables one to use the same pattern as the std::{io}stream API, where one can blindly perform a sequence of reads and only check for errors once at the end of the operation. Similarly to the stream API, as soon as we encounter one error, all of the subsequent operations are skipped (return default values) too, even if the would suceed with clear error state. Unlike the std::stream API (but in line with other llvm APIs), we force the error state to be checked through usage of llvm::Error. Reviewers: probinson, dblaikie, JDevlieghere, aprantl, echristo Subscribers: kristina, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D63713 llvm-svn: 370042 --- llvm/lib/Support/DataExtractor.cpp | 127 +++++++++++++++++++++++++------------ 1 file changed, 86 insertions(+), 41 deletions(-) (limited to 'llvm/lib/Support/DataExtractor.cpp') diff --git a/llvm/lib/Support/DataExtractor.cpp b/llvm/lib/Support/DataExtractor.cpp index 1d88ec06151..a98297cdb35 100644 --- a/llvm/lib/Support/DataExtractor.cpp +++ b/llvm/lib/Support/DataExtractor.cpp @@ -7,104 +7,131 @@ //===----------------------------------------------------------------------===// #include "llvm/Support/DataExtractor.h" +#include "llvm/Support/Errc.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Host.h" -#include "llvm/Support/SwapByteOrder.h" #include "llvm/Support/LEB128.h" +#include "llvm/Support/SwapByteOrder.h" + using namespace llvm; +static void unexpectedEndReached(Error *E) { + if (E) + *E = createStringError(errc::illegal_byte_sequence, + "unexpected end of data"); +} + +static bool isError(Error *E) { return E && *E; } + template static T getU(uint64_t *offset_ptr, const DataExtractor *de, - bool isLittleEndian, const char *Data) { + bool isLittleEndian, const char *Data, llvm::Error *Err) { + ErrorAsOutParameter ErrAsOut(Err); T val = 0; - uint64_t offset = *offset_ptr; - if (de->isValidOffsetForDataOfSize(offset, sizeof(val))) { - std::memcpy(&val, &Data[offset], sizeof(val)); - if (sys::IsLittleEndianHost != isLittleEndian) - sys::swapByteOrder(val); + if (isError(Err)) + return val; - // Advance the offset - *offset_ptr += sizeof(val); + uint64_t offset = *offset_ptr; + if (!de->isValidOffsetForDataOfSize(offset, sizeof(T))) { + unexpectedEndReached(Err); + return val; } + std::memcpy(&val, &Data[offset], sizeof(val)); + if (sys::IsLittleEndianHost != isLittleEndian) + sys::swapByteOrder(val); + + // Advance the offset + *offset_ptr += sizeof(val); return val; } template static T *getUs(uint64_t *offset_ptr, T *dst, uint32_t count, - const DataExtractor *de, bool isLittleEndian, const char *Data){ + const DataExtractor *de, bool isLittleEndian, const char *Data, + llvm::Error *Err) { + ErrorAsOutParameter ErrAsOut(Err); + if (isError(Err)) + return nullptr; + uint64_t offset = *offset_ptr; - if (count > 0 && de->isValidOffsetForDataOfSize(offset, sizeof(*dst)*count)) { - for (T *value_ptr = dst, *end = dst + count; value_ptr != end; - ++value_ptr, offset += sizeof(*dst)) - *value_ptr = getU(offset_ptr, de, isLittleEndian, Data); - // Advance the offset - *offset_ptr = offset; - // Return a non-NULL pointer to the converted data as an indicator of - // success - return dst; + if (!de->isValidOffsetForDataOfSize(offset, sizeof(*dst) * count)) { + unexpectedEndReached(Err); + return nullptr; } - return nullptr; + for (T *value_ptr = dst, *end = dst + count; value_ptr != end; + ++value_ptr, offset += sizeof(*dst)) + *value_ptr = getU(offset_ptr, de, isLittleEndian, Data, Err); + // Advance the offset + *offset_ptr = offset; + // Return a non-NULL pointer to the converted data as an indicator of + // success + return dst; } -uint8_t DataExtractor::getU8(uint64_t *offset_ptr) const { - return getU(offset_ptr, this, IsLittleEndian, Data.data()); +uint8_t DataExtractor::getU8(uint64_t *offset_ptr, llvm::Error *Err) const { + return getU(offset_ptr, this, IsLittleEndian, Data.data(), Err); } uint8_t * DataExtractor::getU8(uint64_t *offset_ptr, uint8_t *dst, uint32_t count) const { return getUs(offset_ptr, dst, count, this, IsLittleEndian, - Data.data()); + Data.data(), nullptr); +} + +uint8_t *DataExtractor::getU8(Cursor &C, uint8_t *Dst, uint32_t Count) const { + return getUs(&C.Offset, Dst, Count, this, IsLittleEndian, + Data.data(), &C.Err); } -uint16_t DataExtractor::getU16(uint64_t *offset_ptr) const { - return getU(offset_ptr, this, IsLittleEndian, Data.data()); +uint16_t DataExtractor::getU16(uint64_t *offset_ptr, llvm::Error *Err) const { + return getU(offset_ptr, this, IsLittleEndian, Data.data(), Err); } uint16_t *DataExtractor::getU16(uint64_t *offset_ptr, uint16_t *dst, uint32_t count) const { return getUs(offset_ptr, dst, count, this, IsLittleEndian, - Data.data()); + Data.data(), nullptr); } uint32_t DataExtractor::getU24(uint64_t *offset_ptr) const { uint24_t ExtractedVal = - getU(offset_ptr, this, IsLittleEndian, Data.data()); + getU(offset_ptr, this, IsLittleEndian, Data.data(), nullptr); // The 3 bytes are in the correct byte order for the host. return ExtractedVal.getAsUint32(sys::IsLittleEndianHost); } -uint32_t DataExtractor::getU32(uint64_t *offset_ptr) const { - return getU(offset_ptr, this, IsLittleEndian, Data.data()); +uint32_t DataExtractor::getU32(uint64_t *offset_ptr, llvm::Error *Err) const { + return getU(offset_ptr, this, IsLittleEndian, Data.data(), Err); } uint32_t *DataExtractor::getU32(uint64_t *offset_ptr, uint32_t *dst, uint32_t count) const { return getUs(offset_ptr, dst, count, this, IsLittleEndian, - Data.data()); + Data.data(), nullptr); } -uint64_t DataExtractor::getU64(uint64_t *offset_ptr) const { - return getU(offset_ptr, this, IsLittleEndian, Data.data()); +uint64_t DataExtractor::getU64(uint64_t *offset_ptr, llvm::Error *Err) const { + return getU(offset_ptr, this, IsLittleEndian, Data.data(), Err); } uint64_t *DataExtractor::getU64(uint64_t *offset_ptr, uint64_t *dst, uint32_t count) const { return getUs(offset_ptr, dst, count, this, IsLittleEndian, - Data.data()); + Data.data(), nullptr); } -uint64_t -DataExtractor::getUnsigned(uint64_t *offset_ptr, uint32_t byte_size) const { +uint64_t DataExtractor::getUnsigned(uint64_t *offset_ptr, uint32_t byte_size, + llvm::Error *Err) const { switch (byte_size) { case 1: - return getU8(offset_ptr); + return getU8(offset_ptr, Err); case 2: - return getU16(offset_ptr); + return getU16(offset_ptr, Err); case 4: - return getU32(offset_ptr); + return getU32(offset_ptr, Err); case 8: - return getU64(offset_ptr); + return getU64(offset_ptr, Err); } llvm_unreachable("getUnsigned unhandled case!"); } @@ -144,16 +171,23 @@ StringRef DataExtractor::getCStrRef(uint64_t *offset_ptr) const { return StringRef(); } -uint64_t DataExtractor::getULEB128(uint64_t *offset_ptr) const { +uint64_t DataExtractor::getULEB128(uint64_t *offset_ptr, + llvm::Error *Err) const { assert(*offset_ptr <= Data.size()); + ErrorAsOutParameter ErrAsOut(Err); + if (isError(Err)) + return 0; const char *error; unsigned bytes_read; uint64_t result = decodeULEB128( reinterpret_cast(Data.data() + *offset_ptr), &bytes_read, reinterpret_cast(Data.data() + Data.size()), &error); - if (error) + if (error) { + if (Err) + *Err = createStringError(errc::illegal_byte_sequence, error); return 0; + } *offset_ptr += bytes_read; return result; } @@ -171,3 +205,14 @@ int64_t DataExtractor::getSLEB128(uint64_t *offset_ptr) const { *offset_ptr += bytes_read; return result; } + +void DataExtractor::skip(Cursor &C, uint64_t Length) const { + ErrorAsOutParameter ErrAsOut(&C.Err); + if (isError(&C.Err)) + return; + + if (isValidOffsetForDataOfSize(C.Offset, Length)) + C.Offset += Length; + else + unexpectedEndReached(&C.Err); +} -- cgit v1.2.3