| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| #include "circle_schema_generated.h" |
|
|
| #include <cassert> |
| #include <cstdint> |
| #include <cstdio> |
| #include <cstring> |
| #include <fstream> |
| #include <stdexcept> |
| #include <string> |
| #include <vector> |
|
|
| |
| namespace luci |
| { |
| template <typename T> class VectorWrapper |
| { |
| public: |
| explicit VectorWrapper(const flatbuffers::Vector<T> *ptr) : _vector(ptr) {} |
| const T *data() const { return null() ? nullptr : _vector->data(); } |
| uint32_t size() const { return null() ? 0 : _vector->size(); } |
| bool null() const { return _vector == nullptr; } |
| bool empty() const { return size() == 0; } |
| private: |
| const flatbuffers::Vector<T> *_vector; |
| }; |
| template <typename T> VectorWrapper<T> wrap(const flatbuffers::Vector<T> *vec) |
| { |
| return VectorWrapper<T>(vec); |
| } |
| } |
|
|
| |
| |
| |
| struct CircleConst |
| { |
| std::vector<std::string> _strings; |
| void *sparsityparam() const { return nullptr; } |
| template <int DT> void size(uint32_t n) { _strings.resize(n); } |
| template <int DT> std::string &at(uint32_t i) { return _strings.at(i); } |
| }; |
|
|
| |
| |
| |
| static void copy_data_STRING(const luci::VectorWrapper<uint8_t> &raw_data, uint32_t num_elements, |
| CircleConst *const_node) |
| { |
| assert(const_node->sparsityparam() == nullptr); |
|
|
| const auto *data = reinterpret_cast<const char *>(raw_data.data()); |
| const auto *i32d = reinterpret_cast<const int32_t *>(raw_data.data()); |
|
|
| |
| assert(static_cast<uint32_t>(*i32d) == num_elements); |
| i32d++; |
|
|
| std::vector<int32_t> offsets; |
| offsets.push_back(*i32d++); |
| for (uint32_t i = 0; i < num_elements; ++i) |
| { |
| offsets.push_back(*i32d++); |
| } |
| assert(offsets.size() == num_elements + 1); |
|
|
| (void)data; |
| fprintf(stderr, "[harness] survived read loop (no ASAN?) offsets=%zu\n", offsets.size()); |
| } |
|
|
| int main(int argc, char **argv) |
| { |
| const char *path = (argc > 1) ? argv[1] : "evil.circle"; |
|
|
| |
| |
| std::ifstream ifs(path, std::ios::binary | std::ios::ate); |
| if (!ifs) |
| { |
| fprintf(stderr, "cannot open %s\n", path); |
| return 2; |
| } |
| std::streamsize sz = ifs.tellg(); |
| ifs.seekg(0, std::ios::beg); |
| std::vector<char> model_data(static_cast<size_t>(sz)); |
| if (!ifs.read(model_data.data(), sz)) |
| { |
| fprintf(stderr, "read failed\n"); |
| return 2; |
| } |
| auto data_data = reinterpret_cast<const uint8_t *>(model_data.data()); |
| size_t data_size = model_data.size(); |
| fprintf(stderr, "[harness] loaded %s (%zu bytes) into heap\n", path, data_size); |
|
|
| |
| { |
| flatbuffers::Verifier verifier(data_data, data_size); |
| if (!circle::VerifyModelBuffer(verifier)) |
| { |
| fprintf(stderr, "[harness] VerifyModelBuffer FAILED -- file would be rejected\n"); |
| return 3; |
| } |
| fprintf(stderr, "[harness] VerifyModelBuffer PASSED (file accepted by importer gate)\n"); |
| } |
|
|
| |
| const circle::Model *model = circle::GetModel(data_data); |
| const auto *subgraphs = model->subgraphs(); |
| if (!subgraphs || subgraphs->size() == 0) |
| { |
| fprintf(stderr, "no subgraphs\n"); |
| return 4; |
| } |
| const circle::SubGraph *sg = subgraphs->Get(0); |
| const auto *tensors = sg->tensors(); |
| const auto *buffers = model->buffers(); |
|
|
| |
| for (uint32_t ti = 0; ti < tensors->size(); ++ti) |
| { |
| const circle::Tensor *const_tensor = tensors->Get(ti); |
|
|
| |
| if (const_tensor->is_variable()) |
| continue; |
| uint32_t c_buffer = const_tensor->buffer(); |
| const circle::Buffer *r_buffer = buffers->Get(c_buffer); |
| if (r_buffer->offset() == 1 || r_buffer->size() == 1) |
| throw std::runtime_error("invalid extended buffer"); |
|
|
| |
| |
| luci::VectorWrapper<uint8_t> buffer(nullptr); |
| if (r_buffer->offset() > 1) |
| { |
| fprintf(stderr, "[harness] tensor %u uses extended buffer (not our path)\n", ti); |
| continue; |
| } |
| else |
| { |
| buffer = luci::wrap(r_buffer->data()); |
| } |
|
|
| const auto *shp = const_tensor->shape(); |
| uint32_t dims = shp ? shp->size() : 0; |
| if (dims == 0 && buffer.empty()) |
| continue; |
|
|
| |
|
|
| uint32_t num_elements = 1; |
| for (uint32_t r = 0; r < dims; ++r) |
| num_elements = num_elements * shp->Get(r); |
|
|
| if (buffer.empty() && num_elements > 0) |
| continue; |
|
|
| if (num_elements == 0) |
| continue; |
|
|
| if (const_tensor->type() == circle::TensorType_STRING) |
| { |
| fprintf(stderr, |
| "[harness] tensor %u STRING: num_elements=%u, Buffer.data size=%u bytes; " |
| "read loop will touch %u int32 = %u bytes\n", |
| ti, num_elements, buffer.size(), num_elements + 1, 4u * (num_elements + 1)); |
| CircleConst node; |
| copy_data_STRING(buffer, num_elements, &node); |
| } |
| } |
|
|
| fprintf(stderr, "[harness] done (no crash observed)\n"); |
| return 0; |
| } |
|
|