/* * Copyright (c) 2019, The Android Open Source Project * * 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 * * http://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 #define LOG_TAG "CppBor" #include namespace cppbor { namespace { std::string insufficientLengthString(size_t bytesNeeded, size_t bytesAvail, const std::string& type) { std::stringstream errStream; errStream << "Need " << bytesNeeded << " byte(s) for " << type << ", have " << bytesAvail << "."; return errStream.str(); } 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, 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) { if (end - valueBegin < static_cast(length)) { 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: 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: 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 { mEntries.reserve(mSize * 2); mEntries.push_back(std::move(item)); } private: size_t mSize; }; class IncompleteSemantic : public Semantic, public IncompleteItem { public: IncompleteSemantic(uint64_t value) : Semantic(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 { mEntries.reserve(1); mEntries.push_back(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, 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, 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, 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, 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, ParseClient* parseClient) { 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 || tagInt > EIGHT_BYTE_LENGTH) { addlData = tagInt; } 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: return handleString(addlData, begin, pos, end, "byte string", parseClient); case TSTR: return handleString(addlData, begin, pos, end, "text string", parseClient); case ARRAY: return handleCompound(std::make_unique(addlData), addlData, begin, pos, end, "array", parseClient); case MAP: return handleCompound(std::make_unique(addlData), addlData * 2, begin, pos, end, "map", parseClient); case SEMANTIC: return handleCompound(std::make_unique(addlData), 1, begin, pos, end, "semantic", parseClient); case SIMPLE: switch (addlData) { case TRUE: case FALSE: return handleBool(addlData, begin, pos, parseClient); case NULL_V: return handleNull(begin, pos, parseClient); } } 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. assert(dynamic_cast(item.get())); mParentStack.push(static_cast(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(); assert(dynamic_cast(parent)); if (parent->type() == ARRAY) { static_cast(parent)->add(std::move(item)); } else if (parent->type() == MAP) { static_cast(parent)->add(std::move(item)); } else if (parent->type() == SEMANTIC) { static_cast(parent)->add(std::move(item)); } else { CHECK(false); // Impossible to get here. } } 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, 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(); } } // namespace cppbor