/* * Copyright 2019 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "cppbor_parse.h" #include #include #ifndef __TRUSTY__ #include #define LOG_TAG "CppBor" #else #define CHECK(x) (void)(x) #endif namespace cppbor { namespace { std::string insufficientLengthString(size_t bytesNeeded, size_t bytesAvail, const std::string& type) { char buf[1024]; snprintf(buf, sizeof(buf), "Need %zu byte(s) for %s, have %zu.", bytesNeeded, type.c_str(), bytesAvail); return std::string(buf); } template >> std::tuple parseLength(const uint8_t* pos, const uint8_t* end, ParseClient* parseClient) { if (pos + sizeof(T) > end) { parseClient->error(pos - 1, insufficientLengthString(sizeof(T), end - pos, "length field")); return {false, 0, pos}; } const uint8_t* intEnd = pos + sizeof(T); T result = 0; do { result = static_cast((result << 8) | *pos++); } while (pos < intEnd); return {true, result, pos}; } std::tuple parseRecursively(const uint8_t* begin, const uint8_t* end, bool emitViews, ParseClient* parseClient); std::tuple handleUint(uint64_t value, const uint8_t* hdrBegin, const uint8_t* hdrEnd, ParseClient* parseClient) { std::unique_ptr item = std::make_unique(value); return {hdrEnd, parseClient->item(item, hdrBegin, hdrEnd /* valueBegin */, hdrEnd /* itemEnd */)}; } std::tuple handleNint(uint64_t value, const uint8_t* hdrBegin, const uint8_t* hdrEnd, ParseClient* parseClient) { if (value > std::numeric_limits::max()) { parseClient->error(hdrBegin, "NINT values that don't fit in int64_t are not supported."); return {hdrBegin, nullptr /* end parsing */}; } std::unique_ptr item = std::make_unique(-1 - static_cast(value)); return {hdrEnd, parseClient->item(item, hdrBegin, hdrEnd /* valueBegin */, hdrEnd /* itemEnd */)}; } std::tuple handleBool(uint64_t value, const uint8_t* hdrBegin, const uint8_t* hdrEnd, ParseClient* parseClient) { std::unique_ptr item = std::make_unique(value == TRUE); return {hdrEnd, parseClient->item(item, hdrBegin, hdrEnd /* valueBegin */, hdrEnd /* itemEnd */)}; } std::tuple handleNull(const uint8_t* hdrBegin, const uint8_t* hdrEnd, ParseClient* parseClient) { std::unique_ptr item = std::make_unique(); return {hdrEnd, parseClient->item(item, hdrBegin, hdrEnd /* valueBegin */, hdrEnd /* itemEnd */)}; } template std::tuple handleString(uint64_t length, const uint8_t* hdrBegin, const uint8_t* valueBegin, const uint8_t* end, const std::string& errLabel, ParseClient* parseClient) { ssize_t signed_length = static_cast(length); if (end - valueBegin < signed_length || signed_length < 0) { parseClient->error(hdrBegin, insufficientLengthString(length, end - valueBegin, errLabel)); return {hdrBegin, nullptr /* end parsing */}; } std::unique_ptr item = std::make_unique(valueBegin, valueBegin + length); return {valueBegin + length, parseClient->item(item, hdrBegin, valueBegin, valueBegin + length)}; } class IncompleteItem { public: virtual ~IncompleteItem() {} virtual void add(std::unique_ptr item) = 0; }; class IncompleteArray : public Array, public IncompleteItem { public: explicit IncompleteArray(size_t size) : mSize(size) {} // We return the "complete" size, rather than the actual size. size_t size() const override { return mSize; } void add(std::unique_ptr item) override { mEntries.reserve(mSize); mEntries.push_back(std::move(item)); } private: size_t mSize; }; class IncompleteMap : public Map, public IncompleteItem { public: explicit IncompleteMap(size_t size) : mSize(size) {} // We return the "complete" size, rather than the actual size. size_t size() const override { return mSize; } void add(std::unique_ptr item) override { if (mKeyHeldForAdding) { mEntries.reserve(mSize); mEntries.push_back({std::move(mKeyHeldForAdding), std::move(item)}); } else { mKeyHeldForAdding = std::move(item); } } private: std::unique_ptr mKeyHeldForAdding; size_t mSize; }; class IncompleteSemanticTag : public SemanticTag, public IncompleteItem { public: explicit IncompleteSemanticTag(uint64_t value) : SemanticTag(value) {} // We return the "complete" size, rather than the actual size. size_t size() const override { return 1; } void add(std::unique_ptr item) override { mTaggedItem = std::move(item); } }; std::tuple handleEntries(size_t entryCount, const uint8_t* hdrBegin, const uint8_t* pos, const uint8_t* end, const std::string& typeName, bool emitViews, ParseClient* parseClient) { while (entryCount > 0) { --entryCount; if (pos == end) { parseClient->error(hdrBegin, "Not enough entries for " + typeName + "."); return {hdrBegin, nullptr /* end parsing */}; } std::tie(pos, parseClient) = parseRecursively(pos, end, emitViews, parseClient); if (!parseClient) return {hdrBegin, nullptr}; } return {pos, parseClient}; } std::tuple handleCompound( std::unique_ptr item, uint64_t entryCount, const uint8_t* hdrBegin, const uint8_t* valueBegin, const uint8_t* end, const std::string& typeName, bool emitViews, ParseClient* parseClient) { parseClient = parseClient->item(item, hdrBegin, valueBegin, valueBegin /* don't know the end yet */); if (!parseClient) return {hdrBegin, nullptr}; const uint8_t* pos; std::tie(pos, parseClient) = handleEntries(entryCount, hdrBegin, valueBegin, end, typeName, emitViews, parseClient); if (!parseClient) return {hdrBegin, nullptr}; return {pos, parseClient->itemEnd(item, hdrBegin, valueBegin, pos)}; } std::tuple parseRecursively(const uint8_t* begin, const uint8_t* end, bool emitViews, ParseClient* parseClient) { if (begin == end) { parseClient->error( begin, "Input buffer is empty. Begin and end cannot point to the same location."); return {begin, nullptr}; } const uint8_t* pos = begin; MajorType type = static_cast(*pos & 0xE0); uint8_t tagInt = *pos & 0x1F; ++pos; bool success = true; uint64_t addlData; if (tagInt < ONE_BYTE_LENGTH) { addlData = tagInt; } else if (tagInt > EIGHT_BYTE_LENGTH) { parseClient->error( begin, "Reserved additional information value or unsupported indefinite length item."); return {begin, nullptr}; } else { switch (tagInt) { case ONE_BYTE_LENGTH: std::tie(success, addlData, pos) = parseLength(pos, end, parseClient); break; case TWO_BYTE_LENGTH: std::tie(success, addlData, pos) = parseLength(pos, end, parseClient); break; case FOUR_BYTE_LENGTH: std::tie(success, addlData, pos) = parseLength(pos, end, parseClient); break; case EIGHT_BYTE_LENGTH: std::tie(success, addlData, pos) = parseLength(pos, end, parseClient); break; default: CHECK(false); // It's impossible to get here break; } } if (!success) return {begin, nullptr}; switch (type) { case UINT: return handleUint(addlData, begin, pos, parseClient); case NINT: return handleNint(addlData, begin, pos, parseClient); case BSTR: if (emitViews) { return handleString(addlData, begin, pos, end, "byte string", parseClient); } else { return handleString(addlData, begin, pos, end, "byte string", parseClient); } case TSTR: if (emitViews) { return handleString(addlData, begin, pos, end, "text string", parseClient); } else { return handleString(addlData, begin, pos, end, "text string", parseClient); } case ARRAY: return handleCompound(std::make_unique(addlData), addlData, begin, pos, end, "array", emitViews, parseClient); case MAP: return handleCompound(std::make_unique(addlData), addlData * 2, begin, pos, end, "map", emitViews, parseClient); case SEMANTIC: return handleCompound(std::make_unique(addlData), 1, begin, pos, end, "semantic", emitViews, parseClient); case SIMPLE: switch (addlData) { case TRUE: case FALSE: return handleBool(addlData, begin, pos, parseClient); case NULL_V: return handleNull(begin, pos, parseClient); default: parseClient->error(begin, "Unsupported floating-point or simple value."); return {begin, nullptr}; } } CHECK(false); // Impossible to get here. return {}; } class FullParseClient : public ParseClient { public: virtual ParseClient* item(std::unique_ptr& item, const uint8_t*, const uint8_t*, const uint8_t* end) override { if (mParentStack.empty() && !item->isCompound()) { // This is the first and only item. mTheItem = std::move(item); mPosition = end; return nullptr; // We're done. } if (item->isCompound()) { // Starting a new compound data item, i.e. a new parent. Save it on the parent stack. // It's safe to save a raw pointer because the unique_ptr is guaranteed to stay in // existence until the corresponding itemEnd() call. mParentStack.push(item.get()); return this; } else { appendToLastParent(std::move(item)); return this; } } virtual ParseClient* itemEnd(std::unique_ptr& item, const uint8_t*, const uint8_t*, const uint8_t* end) override { CHECK(item->isCompound() && item.get() == mParentStack.top()); mParentStack.pop(); if (mParentStack.empty()) { mTheItem = std::move(item); mPosition = end; return nullptr; // We're done } else { appendToLastParent(std::move(item)); return this; } } virtual void error(const uint8_t* position, const std::string& errorMessage) override { mPosition = position; mErrorMessage = errorMessage; } std::tuple /* result */, const uint8_t* /* newPos */, std::string /* errMsg */> parseResult() { std::unique_ptr p = std::move(mTheItem); return {std::move(p), mPosition, std::move(mErrorMessage)}; } private: void appendToLastParent(std::unique_ptr item) { auto parent = mParentStack.top(); #if __has_feature(cxx_rtti) assert(dynamic_cast(parent)); #endif IncompleteItem* parentItem{}; if (parent->type() == ARRAY) { parentItem = static_cast(parent); } else if (parent->type() == MAP) { parentItem = static_cast(parent); } else if (parent->asSemanticTag()) { parentItem = static_cast(parent); } else { CHECK(false); // Impossible to get here. } parentItem->add(std::move(item)); } std::unique_ptr mTheItem; std::stack mParentStack; const uint8_t* mPosition = nullptr; std::string mErrorMessage; }; } // anonymous namespace void parse(const uint8_t* begin, const uint8_t* end, ParseClient* parseClient) { parseRecursively(begin, end, false, parseClient); } std::tuple /* result */, const uint8_t* /* newPos */, std::string /* errMsg */> parse(const uint8_t* begin, const uint8_t* end) { FullParseClient parseClient; parse(begin, end, &parseClient); return parseClient.parseResult(); } void parseWithViews(const uint8_t* begin, const uint8_t* end, ParseClient* parseClient) { parseRecursively(begin, end, true, parseClient); } std::tuple /* result */, const uint8_t* /* newPos */, std::string /* errMsg */> parseWithViews(const uint8_t* begin, const uint8_t* end) { FullParseClient parseClient; parseWithViews(begin, end, &parseClient); return parseClient.parseResult(); } } // namespace cppbor