Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Fleece/Core/Array.hh
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ namespace fleece { namespace impl {

inline iterator begin() const noexcept;

constexpr Array() :Value(internal::kArrayTag, 0, 0) { }
constexpr Array() :Value(internal::kArrayTag, 0, 0, 0, 0) { }

protected:
internal::HeapArray* heapArray() const;
Expand Down
2 changes: 1 addition & 1 deletion Fleece/Core/Dict.hh
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ namespace fleece { namespace impl {

const Value* get(const key_t&) const noexcept;

constexpr Dict() :Value(internal::kDictTag, 0, 0) { }
constexpr Dict() :Value(internal::kDictTag, 0, 0, 0, 0) { }

protected:
internal::HeapDict* heapDict() const noexcept FLPURE;
Expand Down
4 changes: 2 additions & 2 deletions Fleece/Core/Value.cc
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ namespace fleece { namespace impl {

class ConstValue : public Value {
public:
constexpr ConstValue(internal::tags tag, int tiny, int byte1 = 0)
:Value(tag, tiny, byte1) { }
constexpr ConstValue(internal::tags tag, uint8_t tiny, uint8_t byte1 = 0)
:Value(tag, tiny, byte1, 0, 0) { }
};

EVEN_ALIGNED static constexpr const ConstValue
Expand Down
11 changes: 9 additions & 2 deletions Fleece/Core/Value.hh
Original file line number Diff line number Diff line change
Expand Up @@ -203,9 +203,16 @@ namespace fleece { namespace impl {
void _release() const;

protected:
constexpr Value(internal::tags tag, int tiny, int byte1 = 0)
// This constructor only writes to the first 2 bytes because this Value might be narrow.
Value(internal::tags tag, int tiny, int byte1 = 0) {
_byte[0] = uint8_t((tag<<4) | tiny);
_byte[1] = uint8_t(byte1);
}

// This constructor writes to all 4 bytes, because `constexpr` requires full initialization.
constexpr Value(internal::tags tag, uint8_t tiny, uint8_t byte1, uint8_t byte2, uint8_t byte3)
:_byte {(uint8_t)((tag<<4) | tiny),
(uint8_t)byte1}
byte1, byte2, byte3}
{ }

static const Value* findRoot(slice) noexcept FLPURE;
Expand Down
24 changes: 24 additions & 0 deletions Tests/EncoderTests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1212,4 +1212,28 @@ class EncoderTests {
}
}

TEST_CASE("Encoder snip overflow") {
// Test for CBSE-22711, wherein Encoder::snip() can write past the end of a buffer.
// The inline buffer is 256 bytes. We don't know exactly how many bytes
// beginArray/items/endArray consume, so sweep a window covering every
// possible alignment of reserveSpace(2) within the last 8 bytes of _initialBuf.
for (int extraInts = 0; extraInts < 280; ++extraInts) {
fleece::impl::Encoder enc;
enc.beginArray();
for (int i = 0; i < extraInts; ++i)
enc.writeInt(i & 0x7f); // 1 or 2 bytes each, varies tag boundary
enc.endArray();

// Capture _items before snip; snip's placement-new must not modify it.
auto itemsBefore = *(void**)((char*)&enc + 0x178); // _items offset
REQUIRE_NOTHROW(enc.snip());
auto itemsAfter = *(void**)((char*)&enc + 0x178);

// Pre-fix: at some specific extraInts this will fail (low 2 bytes zeroed).
// Post-fix: itemsAfter == itemsBefore (or both null after finish/reset).
CAPTURE(extraInts);
REQUIRE(itemsBefore == itemsAfter);
}
}

} }
Loading