diff options
Diffstat (limited to 'thirdparty/tourist')
| -rw-r--r-- | thirdparty/tourist/analysis/src/dispatcher.cpp | 27 | ||||
| -rw-r--r-- | thirdparty/tourist/foundation/src/stream.cpp | 7 | ||||
| -rw-r--r-- | thirdparty/tourist/trace/src/protocol.cpp | 71 |
3 files changed, 79 insertions, 26 deletions
diff --git a/thirdparty/tourist/analysis/src/dispatcher.cpp b/thirdparty/tourist/analysis/src/dispatcher.cpp index e7ea4a37e..a667b0c37 100644 --- a/thirdparty/tourist/analysis/src/dispatcher.cpp +++ b/thirdparty/tourist/analysis/src/dispatcher.cpp @@ -2,6 +2,8 @@ #include <foundation/scheduler.h> #include <trace/trace.h> +#include <constants.h> + //------------------------------------------------------------------------------ void Dispatcher::add_analyzer(Analyzer& analyzer) { @@ -20,6 +22,12 @@ void Dispatcher::on_new_type(const Type* type) if (outline->hash != type_hash) continue; + // Sum of non-array field sizes — the byte length of event.data that + // scalar field reads index into. + uint32 event_size = 0; + for (uint32 i = 0, n = type->get_field_count(); i < n; ++i) + event_size += type->get_field(i).get_size(); + for (uint32 i = 0; i < type->get_field_count(); ++i) { auto [field_name, field] = type->get_field_info(i); @@ -30,8 +38,23 @@ void Dispatcher::on_new_type(const Type* type) if (f->hash != field_hash) continue; - f->type_info = uint8(field.get_type_info()); - f->offset = int16(field.get_offset()); + uint32 type_info = field.get_type_info(); + uint32 offset = field.get_offset(); + + // Scalar fields are read via event.data + offset. Validate the + // offset and element size against event_size so a malicious + // utrace can't make Outline::Field memcpy past the event + // payload. Array/string fields are read from aux and ignore + // offset. + if ((type_info & TYPE_INFO_CAT_ARRAY) == 0) + { + uint32 element_size = 1u << (type_info & TYPE_INFO_SIZE_MASK); + if (offset > event_size || element_size > event_size - offset) + break; + } + + f->type_info = uint8(type_info); + f->offset = int16(offset); f->set = 1; f->index = i; break; diff --git a/thirdparty/tourist/foundation/src/stream.cpp b/thirdparty/tourist/foundation/src/stream.cpp index c560436d3..97d4cb395 100644 --- a/thirdparty/tourist/foundation/src/stream.cpp +++ b/thirdparty/tourist/foundation/src/stream.cpp @@ -2,6 +2,8 @@ #include "slab.h" +#include <stdexcept> + //------------------------------------------------------------------------------ BufferStream::BufferStream(Slab* slab, const uint8* ptr, uint32 size) : _ptr(ptr) @@ -37,8 +39,11 @@ uint32 BufferStream::get_remaining() const //------------------------------------------------------------------------------ const uint8* BufferStream::read(uint32 size) { + if (size > _end - _cursor) + throw std::runtime_error("BufferStream: read past end of buffer"); + const uint8* ret = (uint8*)_ptr + _cursor; _cursor += size; - return (uint8*)_ptr + _cursor - size; + return ret; } //------------------------------------------------------------------------------ diff --git a/thirdparty/tourist/trace/src/protocol.cpp b/thirdparty/tourist/trace/src/protocol.cpp index 5297048ec..38868a127 100644 --- a/thirdparty/tourist/trace/src/protocol.cpp +++ b/thirdparty/tourist/trace/src/protocol.cpp @@ -28,7 +28,27 @@ Tuple<uint32, TypeDesc> TypeDesc::parse(BufferStream& stream) uint32 info_size = stream.read<uint16>(); const uint8* type_info = stream.read(info_size); + + // Validate that the declared field count and name lengths actually fit + // within info_size before patch() walks _fields[] and writes to it. + if (info_size < sizeof(Type)) + fatal("type info smaller than Type header"); + auto* type = (Type*)type_info; + uint32 field_count = type->get_field_count(); + uint64 required = sizeof(Type); + required += uint64(field_count) * sizeof(Type::Field); + required += type->_logger_name_len; + required += type->_event_name_len; + if (required > info_size) + fatal("type info too small for declared fields"); + for (uint32 i = 0; i < field_count; ++i) + { + required += type->_fields[i].name_size; + if (required > info_size) + fatal("type info too small for declared field names"); + } + type->patch(); uint32 uid = type->get_uid(); @@ -69,6 +89,11 @@ private: //------------------------------------------------------------------------------ void Types::parse(Buffer& buffer, Vector<const Type*>& new_types) { + // Pin the buffer up-front. TypeDesc::parse stores raw pointers into + // this buffer's slab in _descs; if a later type is malformed and + // throws, we still need the already-stored entries to remain valid. + _buffer_refs.push_back(buffer.create_ref()); + BufferStream stream = buffer.create_stream(); do { @@ -83,9 +108,6 @@ void Types::parse(Buffer& buffer, Vector<const Type*>& new_types) new_types.push_back(desc.type); } while (stream.has_data()); - - BufferRef buffer_ref = buffer.create_ref(); - _buffer_refs.push_back(std::move(buffer_ref)); } //------------------------------------------------------------------------------ @@ -466,22 +488,25 @@ void EventParser::parse_aux(const State& state) //------------------------------------------------------------------------------ Serial EventParser::parse_important_aux(const State& state) { - uint32 uid = state.stream.read<uint8>(); - - if (uid == 1) // AuxData + while (true) { - parse_aux(state); - return parse_important_aux(state); - } + uint32 uid = state.stream.read<uint8>(); - if (uid == 3) // AuxDataTerminal - { - _stage = -1; - return Serial(Serial::NO_SYNC); - } + if (uid == 1) // AuxData + { + parse_aux(state); + continue; + } - fatal("unsupported important sub-uid"); - return Serial(); + if (uid == 3) // AuxDataTerminal + { + _stage = -1; + return Serial(Serial::NO_SYNC); + } + + fatal("unsupported important sub-uid"); + return Serial(); + } } @@ -544,12 +569,12 @@ class ParserPool { public: EventParser& get_parser(uint32 index); - uint16 alloc_parser(); + uint32 alloc_parser(); void free_parser(uint32 index); private: Vector<EventParser> _parsers; - Vector<uint16> _frees; + Vector<uint32> _frees; }; //------------------------------------------------------------------------------ @@ -559,15 +584,15 @@ EventParser& ParserPool::get_parser(uint32 index) } //------------------------------------------------------------------------------ -uint16 ParserPool::alloc_parser() +uint32 ParserPool::alloc_parser() { if (_frees.empty()) { _parsers.emplace_back(); - return uint16(_parsers.size() - 1); + return uint32(_parsers.size() - 1); } - uint16 index = _frees.back(); + uint32 index = _frees.back(); _frees.pop_back(); return index; } @@ -576,7 +601,7 @@ uint16 ParserPool::alloc_parser() void ParserPool::free_parser(uint32 index) { _parsers[index] = EventParser(); - _frees.push_back(uint16(index)); + _frees.push_back(index); } @@ -598,7 +623,7 @@ private: { Serial serial; uint16 id; - uint16 parser_index; + uint32 parser_index; PacketNode* head = nullptr; PacketNode* tail = nullptr; }; |