88#include " MapBuffer.h"
99#include < react/renderer/mapbuffer/MapBufferBuilder.h>
1010
11+ #include < algorithm>
12+ #include < cstring>
13+
1114namespace facebook ::react {
1215
16+ namespace {
17+ // Reads a value of type T from a (possibly unaligned) offset in the buffer.
18+ // MapBuffer's packed layout places multi-byte values at offsets that are not
19+ // naturally aligned for their type (e.g. an 8-byte value at a 2-byte boundary),
20+ // so dereferencing a reinterpret_cast pointer there is undefined behavior and
21+ // can fault on 32-bit ARM. memcpy compiles to a single unaligned load on
22+ // arm64/x86 and to alignment-safe loads on armv7.
23+ template <typename T>
24+ inline T readUnaligned (const uint8_t * data, int32_t offset) {
25+ T value;
26+ std::memcpy (&value, data + offset, sizeof (T));
27+ return value;
28+ }
29+
30+ // Debug-asserts on OOB (catches corrupt buffers early in dev) AND clamps in
31+ // release so a corrupt bucket length can never drive an OOB memcpy read.
32+ // react_native_assert is compiled out in release, so the runtime cost outside
33+ // dev is the single min() call.
34+ inline int32_t
35+ clampToBufferBounds (int32_t offset, int32_t byteLength, size_t bufferSize) {
36+ react_native_assert (offset >= 0 && byteLength >= 0 );
37+ react_native_assert (
38+ static_cast <size_t >(offset) + static_cast <size_t >(byteLength) <=
39+ bufferSize);
40+ size_t maxLength = bufferSize > static_cast <size_t >(offset)
41+ ? bufferSize - static_cast <size_t >(offset)
42+ : 0 ;
43+ return static_cast <int32_t >(
44+ std::min (static_cast <size_t >(std::max (byteLength, 0 )), maxLength));
45+ }
46+ } // namespace
47+
1348static inline int32_t bucketOffset (int32_t index) {
1449 return sizeof (MapBuffer::Header) + sizeof (MapBuffer::Bucket) * index;
1550}
@@ -18,16 +53,20 @@ static inline int32_t valueOffset(int32_t bucketIndex) {
1853 return bucketOffset (bucketIndex) + offsetof (MapBuffer::Bucket, data);
1954}
2055
56+ // Dynamic-data entries pack [offset (low 32 bits)][byteLength (high 32 bits)]
57+ // into the bucket's 8-byte value, so the payload in the dynamic data section
58+ // carries no in-band length prefix. This returns the position of the high
59+ // 32 bits (the length).
60+ static inline int32_t lengthOffset (int32_t bucketIndex) {
61+ return valueOffset (bucketIndex) + static_cast <int32_t >(sizeof (int32_t ));
62+ }
63+
2164// TODO T83483191: Extend MapBuffer C++ implementation to support basic random
2265// access
2366MapBuffer::MapBuffer (std::vector<uint8_t > data) : bytes_(std::move(data)) {
24- auto header = reinterpret_cast <const Header*>(bytes_.data ());
25- count_ = header->count ;
26-
27- if (header->bufferSize != bytes_.size ()) {
28- LOG (ERROR ) << " Error: Data size does not match, expected "
29- << header->bufferSize << " found: " << bytes_.size ();
30- abort ();
67+ if (bytes_.size () >= sizeof (Header)) {
68+ auto header = reinterpret_cast <const Header*>(bytes_.data ());
69+ count_ = header->count ;
3170 }
3271}
3372
@@ -37,8 +76,7 @@ int32_t MapBuffer::getKeyBucket(Key key) const {
3776 while (lo <= hi) {
3877 int32_t mid = (lo + hi) >> 1 ;
3978
40- Key midVal =
41- *reinterpret_cast <const Key*>(bytes_.data () + bucketOffset (mid));
79+ Key midVal = readUnaligned<Key>(bytes_.data (), bucketOffset (mid));
4280
4381 if (midVal < key) {
4482 lo = mid + 1 ;
@@ -53,8 +91,7 @@ int32_t MapBuffer::getKeyBucket(Key key) const {
5391}
5492
5593inline int32_t MapBuffer::getIntAtBucket (int32_t bucketIndex) const {
56- return *reinterpret_cast <const int32_t *>(
57- bytes_.data () + valueOffset (bucketIndex));
94+ return readUnaligned<int32_t >(bytes_.data (), valueOffset (bucketIndex));
5895}
5996
6097int32_t MapBuffer::getInt (Key key) const {
@@ -74,8 +111,7 @@ int64_t MapBuffer::getLong(Key key) const {
74111 return 0 ;
75112 }
76113
77- return *reinterpret_cast <const int64_t *>(
78- bytes_.data () + valueOffset (bucketIndex));
114+ return readUnaligned<int64_t >(bytes_.data (), valueOffset (bucketIndex));
79115}
80116
81117bool MapBuffer::getBool (Key key) const {
@@ -89,8 +125,7 @@ double MapBuffer::getDouble(Key key) const {
89125 return 0 ;
90126 }
91127
92- return *reinterpret_cast <const double *>(
93- bytes_.data () + valueOffset (bucketIndex));
128+ return readUnaligned<double >(bytes_.data (), valueOffset (bucketIndex));
94129}
95130
96131int32_t MapBuffer::getDynamicDataOffset () const {
@@ -107,9 +142,10 @@ std::string MapBuffer::getString(Key key) const {
107142 }
108143
109144 int32_t offset = getDynamicDataOffset () + getIntAtBucket (bucketIndex);
110- int32_t stringLength =
111- *reinterpret_cast <const int32_t *>(bytes_.data () + offset);
112- const uint8_t * stringPtr = bytes_.data () + offset + sizeof (int );
145+ auto stringLength =
146+ readUnaligned<int32_t >(bytes_.data (), lengthOffset (bucketIndex));
147+ stringLength = clampToBufferBounds (offset, stringLength, bytes_.size ());
148+ const uint8_t * stringPtr = bytes_.data () + offset;
113149
114150 return {stringPtr, stringPtr + stringLength};
115151}
@@ -122,17 +158,13 @@ MapBuffer MapBuffer::getMapBuffer(Key key) const {
122158 }
123159
124160 int32_t offset = getDynamicDataOffset () + getIntAtBucket (bucketIndex);
125- int32_t mapBufferLength =
126- *reinterpret_cast <const int32_t *>(bytes_.data () + offset);
127- size_t maxLength = bytes_.size () - offset - sizeof (int32_t );
128- if (mapBufferLength > maxLength) {
129- mapBufferLength = maxLength;
130- }
161+ auto mapBufferLength =
162+ readUnaligned<int32_t >(bytes_.data (), lengthOffset (bucketIndex));
163+ mapBufferLength = clampToBufferBounds (offset, mapBufferLength, bytes_.size ());
131164
132165 std::vector<uint8_t > value (mapBufferLength);
133166
134- memcpy (
135- value.data (), bytes_.data () + offset + sizeof (int32_t ), mapBufferLength);
167+ memcpy (value.data (), bytes_.data () + offset, mapBufferLength);
136168
137169 return MapBuffer (std::move (value));
138170}
@@ -146,19 +178,27 @@ std::vector<MapBuffer> MapBuffer::getMapBufferList(MapBuffer::Key key) const {
146178
147179 std::vector<MapBuffer> mapBufferList;
148180 int32_t offset = getDynamicDataOffset () + getIntAtBucket (bucketIndex);
149- int32_t mapBufferListLength =
150- *reinterpret_cast <const int32_t *>(bytes_.data () + offset);
151- offset = offset + sizeof (uint32_t );
181+ auto mapBufferListLength =
182+ readUnaligned<int32_t >(bytes_.data (), lengthOffset (bucketIndex));
183+ mapBufferListLength =
184+ clampToBufferBounds (offset, mapBufferListLength, bytes_.size ());
152185
153186 int32_t curLen = 0 ;
154187 while (curLen < mapBufferListLength) {
155- int32_t mapBufferLength =
156- *reinterpret_cast <const int32_t *>(bytes_.data () + offset + curLen);
157- curLen = curLen + sizeof (uint32_t );
188+ if (curLen + sizeof (int32_t ) > mapBufferListLength) {
189+ break ;
190+ }
191+
192+ auto mapBufferLength =
193+ readUnaligned<int32_t >(bytes_.data (), offset + curLen);
194+ curLen += sizeof (int32_t );
195+
196+ mapBufferLength =
197+ clampToBufferBounds (offset + curLen, mapBufferLength, bytes_.size ());
158198 std::vector<uint8_t > value (mapBufferLength);
159199 memcpy (value.data (), bytes_.data () + offset + curLen, mapBufferLength);
160200 mapBufferList.emplace_back (std::move (value));
161- curLen = curLen + mapBufferLength;
201+ curLen += mapBufferLength;
162202 }
163203 return mapBufferList;
164204}
@@ -171,13 +211,18 @@ std::vector<int32_t> MapBuffer::getIntBuffer(MapBuffer::Key key) const {
171211 }
172212
173213 int32_t offset = getDynamicDataOffset () + getIntAtBucket (bucketIndex);
174- int32_t count = *reinterpret_cast <const int32_t *>(bytes_.data () + offset);
214+ auto byteLength =
215+ readUnaligned<int32_t >(bytes_.data (), lengthOffset (bucketIndex));
216+ byteLength = clampToBufferBounds (offset, byteLength, bytes_.size ());
217+ int32_t count = byteLength / static_cast <int32_t >(sizeof (int32_t ));
175218
176219 std::vector<int32_t > result (count);
177220 if (count > 0 ) {
221+ // Copy only whole elements: a clamped byteLength may not be a multiple of
222+ // sizeof(int32_t), and result holds exactly count elements.
178223 memcpy (
179224 result.data (),
180- bytes_.data () + offset + sizeof ( int32_t ) ,
225+ bytes_.data () + offset,
181226 static_cast <size_t >(count) * sizeof (int32_t ));
182227 }
183228 return result;
@@ -191,13 +236,18 @@ std::vector<double> MapBuffer::getDoubleBuffer(MapBuffer::Key key) const {
191236 }
192237
193238 int32_t offset = getDynamicDataOffset () + getIntAtBucket (bucketIndex);
194- int32_t count = *reinterpret_cast <const int32_t *>(bytes_.data () + offset);
239+ auto byteLength =
240+ readUnaligned<int32_t >(bytes_.data (), lengthOffset (bucketIndex));
241+ byteLength = clampToBufferBounds (offset, byteLength, bytes_.size ());
242+ int32_t count = byteLength / static_cast <int32_t >(sizeof (double ));
195243
196244 std::vector<double > result (count);
197245 if (count > 0 ) {
246+ // Copy only whole elements: a clamped byteLength may not be a multiple of
247+ // sizeof(double), and result holds exactly count elements.
198248 memcpy (
199249 result.data (),
200- bytes_.data () + offset + sizeof ( int32_t ) ,
250+ bytes_.data () + offset,
201251 static_cast <size_t >(count) * sizeof (double ));
202252 }
203253 return result;
0 commit comments