summaryrefslogtreecommitdiffstats
path: root/document
diff options
context:
space:
mode:
authorTor Brede Vekterli <vekterli@yahooinc.com>2023-01-25 14:56:28 +0000
committerTor Brede Vekterli <vekterli@yahooinc.com>2023-01-25 14:56:28 +0000
commit3e3c404a0f22d05f328f46f3e92d3b3718907e33 (patch)
treefb8df03b576eec549dfa7f5e68b202a1b88a38c1 /document
parent0d886192fdbb1c07776019da6c8a97e7f5aa0c61 (diff)
Field offsets/sizes may point into resulting uncompressed range
This may be larger than the input buffer. It is explicitly verified that the actual uncompressed buffer size matches the expected size, meaning that these offsets will be valid prior to use. Additionally: * Ensure that we constrain non-compressed offsets/sizes to point within the buffer range of the field itself, and not beyond. * Do not attempt to set a field in the legacy deserialization path if the field extraction failed and returned an empty `unique_ptr`.
Diffstat (limited to 'document')
-rw-r--r--document/src/vespa/document/serialization/vespadocumentdeserializer.cpp17
1 files changed, 9 insertions, 8 deletions
diff --git a/document/src/vespa/document/serialization/vespadocumentdeserializer.cpp b/document/src/vespa/document/serialization/vespadocumentdeserializer.cpp
index b09896c2460..829cf9d306c 100644
--- a/document/src/vespa/document/serialization/vespadocumentdeserializer.cpp
+++ b/document/src/vespa/document/serialization/vespadocumentdeserializer.cpp
@@ -294,19 +294,19 @@ namespace {
using FieldInfo = SerializableArray::EntryMap;
-void readFieldInfo(nbostream& input, SerializableArray::EntryMap & field_info) __attribute__((noinline));
+void readFieldInfo(nbostream& input, SerializableArray::EntryMap & field_info, size_t max_buffer_extent) __attribute__((noinline));
void
-readFieldInfo(nbostream& input, SerializableArray::EntryMap & field_info) {
+readFieldInfo(nbostream& input, SerializableArray::EntryMap & field_info, size_t max_buffer_extent) {
size_t field_count = getInt1_4Bytes(input);
field_info.reserve(field_count);
uint32_t offset = 0;
for (size_t i = 0; i < field_count; ++i) {
const uint32_t id = getInt1_4Bytes(input);
const uint32_t size = getInt2_4_8Bytes(input);
- if (size_t(offset) + size > input.size()) [[unlikely]] {
- throw DeserializeException(fmt("Field (id=%u, offset=%u, size=%u) extends beyond remaining buffer size (%zu)",
- id, offset, size, input.size()), VESPA_STRLOC);
+ if (size_t(offset) + size > max_buffer_extent) [[unlikely]] {
+ throw DeserializeException(fmt("Field (id=%u, offset=%u, size=%u) extends beyond max buffer extent (%zu)",
+ id, offset, size, max_buffer_extent), VESPA_STRLOC);
}
field_info.emplace_back(id, size, offset);
offset += size;
@@ -356,8 +356,6 @@ VespaDocumentDeserializer::readStructNoReset(StructFieldValue &value) {
if (is_compressed) {
uncompressed_size = getInt2_4_8Bytes(_stream);
}
- readFieldInfo(_stream, field_info);
-
if (is_compressed && (compression_type != CompressionConfig::LZ4)) [[unlikely]] {
throw DeserializeException(fmt("Unsupported compression type: %u", static_cast<uint8_t>(compression_type)), VESPA_STRLOC);
}
@@ -365,6 +363,7 @@ VespaDocumentDeserializer::readStructNoReset(StructFieldValue &value) {
throw DeserializeException(fmt("Struct size (%zu) is greater than remaining buffer size (%zu)",
data_size, _stream.size()), VESPA_STRLOC);
}
+ readFieldInfo(_stream, field_info, is_compressed ? uncompressed_size : data_size);
if (data_size > 0) {
ByteBuffer buffer = is_compressed
? deCompress(compression_type, uncompressed_size, ConstBufferRef(_stream.peek(), data_size))
@@ -381,7 +380,9 @@ VespaDocumentDeserializer::readStructNoReset(StructFieldValue &value) {
for (const auto & entry : tmp) {
try {
FieldValue::UP decoded = tmp.getValue(entry);
- value.setValue(entry, std::move(decoded));
+ if (decoded) {
+ value.setValue(entry, std::move(decoded));
+ }
} catch (const vespalib::Exception & e) {
LOG(warning, "Failed decoding field '%s' in legacy bodyfield -> Skipping it: %s",
entry.getName().data(), e.what());