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,17 +53,19 @@ 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)) {
2467 auto header = reinterpret_cast <const Header*>(bytes_.data ());
2568 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 ();
31- }
3269}
3370
3471int32_t MapBuffer::getKeyBucket (Key key) const {
@@ -37,8 +74,7 @@ int32_t MapBuffer::getKeyBucket(Key key) const {
3774 while (lo <= hi) {
3875 int32_t mid = (lo + hi) >> 1 ;
3976
40- Key midVal =
41- *reinterpret_cast <const Key*>(bytes_.data () + bucketOffset (mid));
77+ Key midVal = readUnaligned<Key>(bytes_.data (), bucketOffset (mid));
4278
4379 if (midVal < key) {
4480 lo = mid + 1 ;
@@ -53,8 +89,7 @@ int32_t MapBuffer::getKeyBucket(Key key) const {
5389}
5490
5591inline int32_t MapBuffer::getIntAtBucket (int32_t bucketIndex) const {
56- return *reinterpret_cast <const int32_t *>(
57- bytes_.data () + valueOffset (bucketIndex));
92+ return readUnaligned<int32_t >(bytes_.data (), valueOffset (bucketIndex));
5893}
5994
6095int32_t MapBuffer::getInt (Key key) const {
@@ -74,8 +109,7 @@ int64_t MapBuffer::getLong(Key key) const {
74109 return 0 ;
75110 }
76111
77- return *reinterpret_cast <const int64_t *>(
78- bytes_.data () + valueOffset (bucketIndex));
112+ return readUnaligned<int64_t >(bytes_.data (), valueOffset (bucketIndex));
79113}
80114
81115bool MapBuffer::getBool (Key key) const {
@@ -89,8 +123,7 @@ double MapBuffer::getDouble(Key key) const {
89123 return 0 ;
90124 }
91125
92- return *reinterpret_cast <const double *>(
93- bytes_.data () + valueOffset (bucketIndex));
126+ return readUnaligned<double >(bytes_.data (), valueOffset (bucketIndex));
94127}
95128
96129int32_t MapBuffer::getDynamicDataOffset () const {
@@ -107,9 +140,10 @@ std::string MapBuffer::getString(Key key) const {
107140 }
108141
109142 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 );
143+ auto stringLength =
144+ readUnaligned<int32_t >(bytes_.data (), lengthOffset (bucketIndex));
145+ stringLength = clampToBufferBounds (offset, stringLength, bytes_.size ());
146+ const uint8_t * stringPtr = bytes_.data () + offset;
113147
114148 return {stringPtr, stringPtr + stringLength};
115149}
@@ -122,17 +156,13 @@ MapBuffer MapBuffer::getMapBuffer(Key key) const {
122156 }
123157
124158 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- }
159+ auto mapBufferLength =
160+ readUnaligned<int32_t >(bytes_.data (), lengthOffset (bucketIndex));
161+ mapBufferLength = clampToBufferBounds (offset, mapBufferLength, bytes_.size ());
131162
132163 std::vector<uint8_t > value (mapBufferLength);
133164
134- memcpy (
135- value.data (), bytes_.data () + offset + sizeof (int32_t ), mapBufferLength);
165+ memcpy (value.data (), bytes_.data () + offset, mapBufferLength);
136166
137167 return MapBuffer (std::move (value));
138168}
@@ -146,15 +176,18 @@ std::vector<MapBuffer> MapBuffer::getMapBufferList(MapBuffer::Key key) const {
146176
147177 std::vector<MapBuffer> mapBufferList;
148178 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 );
179+ auto mapBufferListLength =
180+ readUnaligned<int32_t >(bytes_.data (), lengthOffset (bucketIndex));
181+ mapBufferListLength =
182+ clampToBufferBounds (offset, mapBufferListLength, bytes_.size ());
152183
153184 int32_t curLen = 0 ;
154185 while (curLen < mapBufferListLength) {
155- int32_t mapBufferLength =
156- * reinterpret_cast < const int32_t * >(bytes_.data () + offset + curLen);
186+ auto mapBufferLength =
187+ readUnaligned< int32_t >(bytes_.data (), offset + curLen);
157188 curLen = curLen + sizeof (uint32_t );
189+ mapBufferLength =
190+ clampToBufferBounds (offset + curLen, mapBufferLength, bytes_.size ());
158191 std::vector<uint8_t > value (mapBufferLength);
159192 memcpy (value.data (), bytes_.data () + offset + curLen, mapBufferLength);
160193 mapBufferList.emplace_back (std::move (value));
@@ -171,14 +204,15 @@ std::vector<int32_t> MapBuffer::getIntBuffer(MapBuffer::Key key) const {
171204 }
172205
173206 int32_t offset = getDynamicDataOffset () + getIntAtBucket (bucketIndex);
174- int32_t count = *reinterpret_cast <const int32_t *>(bytes_.data () + offset);
207+ auto byteLength =
208+ readUnaligned<int32_t >(bytes_.data (), lengthOffset (bucketIndex));
209+ byteLength = clampToBufferBounds (offset, byteLength, bytes_.size ());
210+ int32_t count = byteLength / static_cast <int32_t >(sizeof (int32_t ));
175211
176212 std::vector<int32_t > result (count);
177- if (count > 0 ) {
213+ if (byteLength > 0 ) {
178214 memcpy (
179- result.data (),
180- bytes_.data () + offset + sizeof (int32_t ),
181- static_cast <size_t >(count) * sizeof (int32_t ));
215+ result.data (), bytes_.data () + offset, static_cast <size_t >(byteLength));
182216 }
183217 return result;
184218}
@@ -191,14 +225,15 @@ std::vector<double> MapBuffer::getDoubleBuffer(MapBuffer::Key key) const {
191225 }
192226
193227 int32_t offset = getDynamicDataOffset () + getIntAtBucket (bucketIndex);
194- int32_t count = *reinterpret_cast <const int32_t *>(bytes_.data () + offset);
228+ auto byteLength =
229+ readUnaligned<int32_t >(bytes_.data (), lengthOffset (bucketIndex));
230+ byteLength = clampToBufferBounds (offset, byteLength, bytes_.size ());
231+ int32_t count = byteLength / static_cast <int32_t >(sizeof (double ));
195232
196233 std::vector<double > result (count);
197- if (count > 0 ) {
234+ if (byteLength > 0 ) {
198235 memcpy (
199- result.data (),
200- bytes_.data () + offset + sizeof (int32_t ),
201- static_cast <size_t >(count) * sizeof (double ));
236+ result.data (), bytes_.data () + offset, static_cast <size_t >(byteLength));
202237 }
203238 return result;
204239}
0 commit comments