Skip to content

Commit 0d3c70b

Browse files
rshestkelset
authored andcommitted
Fix race condition in ReadableNativeMap (#36201)
Summary: Pull Request resolved: #36201 [Changelog][Internal] Guard call to the C++ ReadableNAtiveMap.importValues with a lock. Note that all the occurrences in this class (together with importTypes) already were protected by a lock, except of this one, which with the very high chance caused crashes in T145271136. My corresponding comment from the task, for justification: > If callstack to be trusted, the crash happens on the C++ side, in ReadableNativeMap::importValues(). It throws ArrayIndexOutOfBoundsException, which, looking at the code, seems to be only possible due to a corrupted data or race conditions. > Now, looking at the Java side of ReadableNativeMap, and the particular call site... it's very dodgy, since all other occurrences of calling to native importTypes/importValues are guarded by locks, but the one crashing isn't. NOTE: A couple of `importKeys()` instances appears to suffer from the same problem as well. Reviewed By: javache Differential Revision: D43398416 fbshipit-source-id: 0402de5dc723a2fba7d0247c8ad4aeff150d8340
1 parent a413e3e commit 0d3c70b

File tree

1 file changed

+45
-43
lines changed

1 file changed

+45
-43
lines changed

ReactAndroid/src/main/java/com/facebook/react/bridge/ReadableNativeMap.java

Lines changed: 45 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,21 @@ public static int getJNIPassCounter() {
3939
return mJniCallCounter;
4040
}
4141

42-
private HashMap<String, Object> getLocalMap() {
43-
if (mLocalMap != null) {
44-
return mLocalMap;
45-
}
42+
private void ensureKeysAreImported() {
4643
synchronized (this) {
4744
if (mKeys == null) {
4845
mKeys = Assertions.assertNotNull(importKeys());
4946
mJniCallCounter++;
5047
}
48+
}
49+
}
50+
51+
private HashMap<String, Object> getLocalMap() {
52+
if (mLocalMap != null) {
53+
return mLocalMap;
54+
}
55+
ensureKeysAreImported();
56+
synchronized (this) {
5157
if (mLocalMap == null) {
5258
Object[] values = Assertions.assertNotNull(importValues());
5359
mJniCallCounter++;
@@ -69,11 +75,8 @@ private HashMap<String, Object> getLocalMap() {
6975
if (mLocalTypeMap != null) {
7076
return mLocalTypeMap;
7177
}
78+
ensureKeysAreImported();
7279
synchronized (this) {
73-
if (mKeys == null) {
74-
mKeys = Assertions.assertNotNull(importKeys());
75-
mJniCallCounter++;
76-
}
7780
// check that no other thread has already updated
7881
if (mLocalTypeMap == null) {
7982
Object[] types = Assertions.assertNotNull(importTypes());
@@ -187,48 +190,47 @@ public int getInt(@NonNull String name) {
187190

188191
@Override
189192
public @NonNull Iterator<Map.Entry<String, Object>> getEntryIterator() {
190-
if (mKeys == null) {
191-
mKeys = Assertions.assertNotNull(importKeys());
192-
}
193+
ensureKeysAreImported();
193194
final String[] iteratorKeys = mKeys;
194-
final Object[] iteratorValues = Assertions.assertNotNull(importValues());
195-
return new Iterator<Map.Entry<String, Object>>() {
196-
int currentIndex = 0;
195+
synchronized (this) {
196+
final Object[] iteratorValues = Assertions.assertNotNull(importValues());
197197

198-
@Override
199-
public boolean hasNext() {
200-
return currentIndex < iteratorKeys.length;
201-
}
198+
return new Iterator<Map.Entry<String, Object>>() {
199+
int currentIndex = 0;
202200

203-
@Override
204-
public Map.Entry<String, Object> next() {
205-
final int index = currentIndex++;
206-
return new Map.Entry<String, Object>() {
207-
@Override
208-
public String getKey() {
209-
return iteratorKeys[index];
210-
}
211-
212-
@Override
213-
public Object getValue() {
214-
return iteratorValues[index];
215-
}
216-
217-
@Override
218-
public Object setValue(Object value) {
219-
throw new UnsupportedOperationException(
220-
"Can't set a value while iterating over a ReadableNativeMap");
221-
}
222-
};
223-
}
224-
};
201+
@Override
202+
public boolean hasNext() {
203+
return currentIndex < iteratorKeys.length;
204+
}
205+
206+
@Override
207+
public Map.Entry<String, Object> next() {
208+
final int index = currentIndex++;
209+
return new Map.Entry<String, Object>() {
210+
@Override
211+
public String getKey() {
212+
return iteratorKeys[index];
213+
}
214+
215+
@Override
216+
public Object getValue() {
217+
return iteratorValues[index];
218+
}
219+
220+
@Override
221+
public Object setValue(Object value) {
222+
throw new UnsupportedOperationException(
223+
"Can't set a value while iterating over a ReadableNativeMap");
224+
}
225+
};
226+
}
227+
};
228+
}
225229
}
226230

227231
@Override
228232
public @NonNull ReadableMapKeySetIterator keySetIterator() {
229-
if (mKeys == null) {
230-
mKeys = Assertions.assertNotNull(importKeys());
231-
}
233+
ensureKeysAreImported();
232234
final String[] iteratorKeys = mKeys;
233235
return new ReadableMapKeySetIterator() {
234236
int currentIndex = 0;

0 commit comments

Comments
 (0)