Skip to content

Commit ef32950

Browse files
authored
1.x: Plugin lookup workaround for System.properties access restrictions (#5820)
* 1.x: Plugin lookup workaround for System.properties access restrictions * System.getProperties() can also throw SecurityException
1 parent 265fb48 commit ef32950

File tree

2 files changed

+109
-17
lines changed

2 files changed

+109
-17
lines changed

src/main/java/rx/plugins/RxJavaPlugins.java

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ public void reset() {
105105
public RxJavaErrorHandler getErrorHandler() {
106106
if (errorHandler.get() == null) {
107107
// check for an implementation from System.getProperty first
108-
Object impl = getPluginImplementationViaProperty(RxJavaErrorHandler.class, System.getProperties());
108+
Object impl = getPluginImplementationViaProperty(RxJavaErrorHandler.class, getSystemPropertiesSafe());
109109
if (impl == null) {
110110
// nothing set via properties so initialize with default
111111
errorHandler.compareAndSet(null, DEFAULT_ERROR_HANDLER);
@@ -147,7 +147,7 @@ public void registerErrorHandler(RxJavaErrorHandler impl) {
147147
public RxJavaObservableExecutionHook getObservableExecutionHook() {
148148
if (observableExecutionHook.get() == null) {
149149
// check for an implementation from System.getProperty first
150-
Object impl = getPluginImplementationViaProperty(RxJavaObservableExecutionHook.class, System.getProperties());
150+
Object impl = getPluginImplementationViaProperty(RxJavaObservableExecutionHook.class, getSystemPropertiesSafe());
151151
if (impl == null) {
152152
// nothing set via properties so initialize with default
153153
observableExecutionHook.compareAndSet(null, RxJavaObservableExecutionHookDefault.getInstance());
@@ -189,7 +189,7 @@ public void registerObservableExecutionHook(RxJavaObservableExecutionHook impl)
189189
public RxJavaSingleExecutionHook getSingleExecutionHook() {
190190
if (singleExecutionHook.get() == null) {
191191
// check for an implementation from System.getProperty first
192-
Object impl = getPluginImplementationViaProperty(RxJavaSingleExecutionHook.class, System.getProperties());
192+
Object impl = getPluginImplementationViaProperty(RxJavaSingleExecutionHook.class, getSystemPropertiesSafe());
193193
if (impl == null) {
194194
// nothing set via properties so initialize with default
195195
singleExecutionHook.compareAndSet(null, RxJavaSingleExecutionHookDefault.getInstance());
@@ -232,7 +232,7 @@ public void registerSingleExecutionHook(RxJavaSingleExecutionHook impl) {
232232
public RxJavaCompletableExecutionHook getCompletableExecutionHook() {
233233
if (completableExecutionHook.get() == null) {
234234
// check for an implementation from System.getProperty first
235-
Object impl = getPluginImplementationViaProperty(RxJavaCompletableExecutionHook.class, System.getProperties());
235+
Object impl = getPluginImplementationViaProperty(RxJavaCompletableExecutionHook.class, getSystemPropertiesSafe());
236236
if (impl == null) {
237237
// nothing set via properties so initialize with default
238238
completableExecutionHook.compareAndSet(null, new RxJavaCompletableExecutionHook() { });
@@ -262,6 +262,19 @@ public void registerCompletableExecutionHook(RxJavaCompletableExecutionHook impl
262262
}
263263
}
264264

265+
/**
266+
* A security manager may prevent accessing the System properties entirely,
267+
* therefore, the SecurityException is turned into an empty properties.
268+
* @return the Properties to use for looking up settings
269+
*/
270+
/* test */ static Properties getSystemPropertiesSafe() {
271+
try {
272+
return System.getProperties();
273+
} catch (SecurityException ex) {
274+
return new Properties();
275+
}
276+
}
277+
265278
/* test */ static Object getPluginImplementationViaProperty(Class<?> pluginClass, Properties propsIn) {
266279
// Make a defensive clone because traversal may fail with ConcurrentModificationException
267280
// if the properties get changed by something outside RxJava.
@@ -284,25 +297,32 @@ public void registerCompletableExecutionHook(RxJavaCompletableExecutionHook impl
284297
String classSuffix = ".class";
285298
String implSuffix = ".impl";
286299

287-
for (Map.Entry<Object, Object> e : props.entrySet()) {
288-
String key = e.getKey().toString();
289-
if (key.startsWith(pluginPrefix) && key.endsWith(classSuffix)) {
290-
String value = e.getValue().toString();
300+
try {
301+
for (Map.Entry<Object, Object> e : props.entrySet()) {
302+
String key = e.getKey().toString();
303+
if (key.startsWith(pluginPrefix) && key.endsWith(classSuffix)) {
304+
String value = e.getValue().toString();
305+
306+
if (classSimpleName.equals(value)) {
307+
String index = key.substring(0, key.length() - classSuffix.length()).substring(pluginPrefix.length());
291308

292-
if (classSimpleName.equals(value)) {
293-
String index = key.substring(0, key.length() - classSuffix.length()).substring(pluginPrefix.length());
309+
String implKey = pluginPrefix + index + implSuffix;
294310

295-
String implKey = pluginPrefix + index + implSuffix;
311+
implementingClass = props.getProperty(implKey);
296312

297-
implementingClass = props.getProperty(implKey);
313+
if (implementingClass == null) {
314+
throw new IllegalStateException("Implementing class declaration for " + classSimpleName + " missing: " + implKey);
315+
}
298316

299-
if (implementingClass == null) {
300-
throw new IllegalStateException("Implementing class declaration for " + classSimpleName + " missing: " + implKey);
317+
break;
301318
}
302-
303-
break;
304319
}
305320
}
321+
} catch (SecurityException ex) {
322+
// https://github.com/ReactiveX/RxJava/issues/5819
323+
// We don't seem to have access to all properties.
324+
// At least print the exception to the console.
325+
ex.printStackTrace();
306326
}
307327
}
308328

@@ -339,7 +359,7 @@ public void registerCompletableExecutionHook(RxJavaCompletableExecutionHook impl
339359
public RxJavaSchedulersHook getSchedulersHook() {
340360
if (schedulersHook.get() == null) {
341361
// check for an implementation from System.getProperty first
342-
Object impl = getPluginImplementationViaProperty(RxJavaSchedulersHook.class, System.getProperties());
362+
Object impl = getPluginImplementationViaProperty(RxJavaSchedulersHook.class, getSystemPropertiesSafe());
343363
if (impl == null) {
344364
// nothing set via properties so initialize with default
345365
schedulersHook.compareAndSet(null, RxJavaSchedulersHook.getDefaultInstance());

src/test/java/rx/plugins/RxJavaPluginsTest.java

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import static org.junit.Assert.*;
1919
import static org.mockito.Mockito.mock;
2020

21+
import java.security.Permission;
2122
import java.util.*;
2223
import java.util.concurrent.TimeUnit;
2324

@@ -344,4 +345,75 @@ public void onNext(Object o) {
344345
assertEquals(re, errorHandler.e);
345346
assertEquals(1, errorHandler.count);
346347
}
348+
349+
@Test
350+
public void systemPropertiesSecurityException() {
351+
assertNull(RxJavaPlugins.getPluginImplementationViaProperty(Object.class, new Properties() {
352+
353+
private static final long serialVersionUID = -4291534158508255616L;
354+
355+
@Override
356+
public Set<java.util.Map.Entry<Object, Object>> entrySet() {
357+
return new HashSet<java.util.Map.Entry<Object, Object>>() {
358+
359+
private static final long serialVersionUID = -7714005655772619143L;
360+
361+
@Override
362+
public Iterator<java.util.Map.Entry<Object, Object>> iterator() {
363+
return new Iterator<java.util.Map.Entry<Object, Object>>() {
364+
@Override
365+
public boolean hasNext() {
366+
return true;
367+
}
368+
369+
@Override
370+
public Map.Entry<Object,Object> next() {
371+
throw new SecurityException();
372+
};
373+
374+
@Override
375+
public void remove() {
376+
throw new UnsupportedOperationException();
377+
}
378+
};
379+
}
380+
};
381+
}
382+
383+
@Override
384+
public synchronized Object clone() {
385+
return this;
386+
}
387+
}));
388+
}
389+
390+
@Test
391+
public void securityManagerDenySystemProperties() {
392+
SecurityManager old = System.getSecurityManager();
393+
try {
394+
SecurityManager sm = new SecurityManager() {
395+
@Override
396+
public void checkPropertiesAccess() {
397+
throw new SecurityException();
398+
}
399+
400+
@Override
401+
public void checkPermission(Permission perm) {
402+
// allow restoring the security manager
403+
}
404+
405+
@Override
406+
public void checkPermission(Permission perm, Object context) {
407+
// allow restoring the security manager
408+
}
409+
};
410+
411+
System.setSecurityManager(sm);
412+
assertTrue(RxJavaPlugins.getSystemPropertiesSafe().isEmpty());
413+
} finally {
414+
System.setSecurityManager(old);
415+
}
416+
417+
assertFalse(RxJavaPlugins.getSystemPropertiesSafe().isEmpty());
418+
}
347419
}

0 commit comments

Comments
 (0)