Skip to content

Commit

Permalink
Prevent 'illegal reflective access' problems when instantiating XStream
Browse files Browse the repository at this point in the history
  • Loading branch information
famod committed Aug 2, 2020
1 parent 4438248 commit eb2dcfd
Show file tree
Hide file tree
Showing 10 changed files with 226 additions and 137 deletions.
7 changes: 7 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -673,6 +673,12 @@
<version>${version.org.ops4j.pax.exam}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.github.classgraph</groupId>
<artifactId>classgraph</artifactId>
<version>${version.io.github.classgraph}</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>

Expand Down Expand Up @@ -1095,6 +1101,7 @@
<version.commons.codec>1.11</version.commons.codec>
<version.commons.lang3>3.8.1</version.commons.lang3>
<version.hsqldb>2.2.8</version.hsqldb>
<version.io.github.classgraph>4.8.87</version.io.github.classgraph>
<version.jakarta.activation.api>1.2.1</version.jakarta.activation.api>
<version.jakarta.annotation.api>1.3.4</version.jakarta.annotation.api>
<version.jakarta.xml.bind.api>2.3.2</version.jakarta.xml.bind.api>
Expand Down
5 changes: 5 additions & 0 deletions xstream/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,11 @@
<artifactId>commons-lang3</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.github.classgraph</groupId>
<artifactId>classgraph</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* The software in this package is published under the terms of the BSD
* style license a copy of which has been included with this distribution in
* the LICENSE.txt file.
*
*
* Created on 23. February 2004 by Joe Walnes
*/
package com.thoughtworks.xstream.converters.collections;
Expand Down Expand Up @@ -35,13 +35,12 @@
* permissions for SecurityManager.checkPackageAccess, SecurityManager.checkMemberAccess(this, EnumSet.MEMBER) and
* ReflectPermission("suppressAccessChecks").
* </p>
*
*
* @author Joe Walnes
* @author Kevin Ring
*/
public class PropertiesConverter implements Converter {

private final static Field defaultsField = Fields.locate(Properties.class, Properties.class, false);
private final boolean sort;

public PropertiesConverter() {
Expand All @@ -67,8 +66,8 @@ public void marshal(final Object source, final HierarchicalStreamWriter writer,
writer.addAttribute("value", entry.getValue().toString());
writer.endNode();
}
if (defaultsField != null) {
final Properties defaults = (Properties)Fields.read(defaultsField, properties);
if (Reflections.defaultsField != null) {
final Properties defaults = (Properties)Fields.read(Reflections.defaultsField, properties);
if (defaults != null) {
writer.startNode("defaults");
marshal(defaults, writer, context);
Expand Down Expand Up @@ -101,4 +100,7 @@ public Object unmarshal(final HierarchicalStreamReader reader, final Unmarshalli
}
}

private static class Reflections {
private final static Field defaultsField = Fields.locate(Properties.class, Properties.class, false);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ public int compare(final Comparable o1, final Comparable o2) {

@SuppressWarnings("rawtypes")
private final static Comparator NULL_MARKER = new NullComparator();
private final static Field comparatorField = Fields.locate(TreeMap.class, Comparator.class, false);

public TreeMapConverter(final Mapper mapper) {
super(mapper, TreeMap.class);
Expand All @@ -75,7 +74,7 @@ protected void marshalComparator(final Comparator<?> comparator, final Hierarchi

@Override
public Object unmarshal(final HierarchicalStreamReader reader, final UnmarshallingContext context) {
TreeMap<Object, Object> result = comparatorField != null ? new TreeMap<>() : null;
TreeMap<Object, Object> result = Reflections.comparatorField != null ? new TreeMap<>() : null;
@SuppressWarnings("unchecked")
final Comparator<Object> comparator = (Comparator<Object>)unmarshalComparator(reader, context, result);
if (result == null) {
Expand Down Expand Up @@ -125,19 +124,23 @@ protected void populateTreeMap(final HierarchicalStreamReader reader, final Unma
final TreeMap<Object, Object> typedResult = (TreeMap<Object, Object>)result;
try {
if (JVM.hasOptimizedTreeMapPutAll()) {
if (comparator != null && comparatorField != null) {
comparatorField.set(result, comparator);
if (comparator != null && Reflections.comparatorField != null) {
Reflections.comparatorField.set(result, comparator);
}
typedResult.putAll(sortedMap); // internal optimization will not call comparator
} else if (comparatorField != null) {
comparatorField.set(result, sortedMap.comparator());
} else if (Reflections.comparatorField != null) {
Reflections.comparatorField.set(result, sortedMap.comparator());
typedResult.putAll(sortedMap); // "sort" by index
comparatorField.set(result, comparator);
Reflections.comparatorField.set(result, comparator);
} else {
typedResult.putAll(sortedMap); // will use comparator for already sorted map
}
} catch (final IllegalAccessException e) {
throw new ObjectAccessException("Cannot set comparator of TreeMap", e);
}
}

private static class Reflections {
private final static Field comparatorField = Fields.locate(TreeMap.class, Comparator.class, false);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,43 +42,6 @@
*/
public class TreeSetConverter extends CollectionConverter {
private transient TreeMapConverter treeMapConverter;
private final static Field sortedMapField;
private final static Object constantValue;

static {
Object value = null;
sortedMapField = JVM.hasOptimizedTreeSetAddAll() ? Fields.locate(TreeSet.class, SortedMap.class, false) : null;
if (sortedMapField != null) {
final TreeSet<String> set = new TreeSet<>();
set.add("1");
set.add("2");

Map<String, Object> backingMap = null;
try {
@SuppressWarnings("unchecked")
final Map<String, Object> map = (Map<String, Object>)sortedMapField.get(set);
backingMap = map;
} catch (final IllegalAccessException e) {
// give up;
}
if (backingMap != null) {
final Object[] values = backingMap.values().toArray();
if (values[0] == values[1]) {
value = values[0];
}
}
} else {
final Field valueField = Fields.locate(TreeSet.class, Object.class, true);
if (valueField != null) {
try {
value = valueField.get(null);
} catch (final IllegalAccessException e) {
// give up;
}
}
}
constantValue = value;
}

public TreeSetConverter(final Mapper mapper) {
super(mapper, TreeSet.class);
Expand All @@ -100,11 +63,11 @@ public Object unmarshal(final HierarchicalStreamReader reader, final Unmarshalli
final boolean inFirstElement = unmarshalledComparator instanceof Mapper.Null;
@SuppressWarnings("unchecked")
final Comparator<Object> comparator = inFirstElement ? null : (Comparator<Object>)unmarshalledComparator;
if (sortedMapField != null) {
if (Reflections.sortedMapField != null) {
final TreeSet<Object> possibleResult = comparator == null ? new TreeSet<>() : new TreeSet<>(comparator);
Object backingMap = null;
try {
backingMap = sortedMapField.get(possibleResult);
backingMap = Reflections.sortedMapField.get(possibleResult);
} catch (final IllegalAccessException e) {
throw new ObjectAccessException("Cannot get backing map of TreeSet", e);
}
Expand Down Expand Up @@ -146,7 +109,8 @@ protected void populateMap(final HierarchicalStreamReader reader, final Unmarsha
public boolean add(final Object object) {
@SuppressWarnings("unchecked")
final Map<Object, Object> collectionTarget = (Map<Object, Object>)target;
return collectionTarget.put(object, constantValue != null ? constantValue : object) != null;
return collectionTarget.put(
object, Reflections.constantValue != null ? Reflections.constantValue : object) != null;
}

@Override
Expand All @@ -173,4 +137,46 @@ protected void putCurrentEntryIntoMap(final HierarchicalStreamReader reader,
};
return this;
}

private static class Reflections {

private final static Field sortedMapField;
private final static Object constantValue;

static {
Object value = null;
sortedMapField = JVM.hasOptimizedTreeSetAddAll()
? Fields.locate(TreeSet.class, SortedMap.class, false) : null;
if (sortedMapField != null) {
final TreeSet<String> set = new TreeSet<>();
set.add("1");
set.add("2");

Map<String, Object> backingMap = null;
try {
@SuppressWarnings("unchecked")
final Map<String, Object> map = (Map<String, Object>)sortedMapField.get(set);
backingMap = map;
} catch (final IllegalAccessException e) {
// give up;
}
if (backingMap != null) {
final Object[] values = backingMap.values().toArray();
if (values[0] == values[1]) {
value = values[0];
}
}
} else {
final Field valueField = Fields.locate(TreeSet.class, Object.class, true);
if (valueField != null) {
try {
value = valueField.get(null);
} catch (final IllegalAccessException e) {
// give up;
}
}
}
constantValue = value;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* The software in this package is published under the terms of the BSD
* style license a copy of which has been included with this distribution in
* the LICENSE.txt file.
*
*
* Created on 06. April 2005 by Joe Walnes
*/

Expand All @@ -31,25 +31,24 @@
* If a {@link SecurityManager} is set, the converter will only work with permissions for SecurityManager.checkPackageAccess,
* SecurityManager.checkMemberAccess(this, EnumSet.MEMBER) and ReflectPermission("suppressAccessChecks").
* </p>
*
*
* @author Joe Walnes
*/
public class EnumMapConverter extends MapConverter {

private final static Field typeField = Fields.locate(EnumMap.class, Class.class, false);

public EnumMapConverter(final Mapper mapper) {
super(mapper);
}

@Override
public boolean canConvert(final Class<?> type) {
return typeField != null && type == EnumMap.class;
return type == EnumMap.class && Reflections.typeField != null;
}

@Override
public void marshal(final Object source, final HierarchicalStreamWriter writer, final MarshallingContext context) {
final Class<?> type = (Class<?>)Fields.read(typeField, source);
final Class<?> type = (Class<?>)Fields.read(Reflections.typeField, source);
final String attributeName = mapper().aliasForSystemAttribute("enum-type");
if (attributeName != null) {
writer.addAttribute(attributeName, mapper().serializedClass(type));
Expand All @@ -69,4 +68,8 @@ public Object unmarshal(final HierarchicalStreamReader reader, final Unmarshalli
populateMap(reader, context, map);
return map;
}

private static class Reflections {
private final static Field typeField = Fields.locate(EnumMap.class, Class.class, false);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* The software in this package is published under the terms of the BSD
* style license a copy of which has been included with this distribution in
* the LICENSE.txt file.
*
*
* Created on 06. April 2005 by Joe Walnes
*/

Expand All @@ -31,13 +31,12 @@
* If a SecurityManager is set, the converter will only work with permissions for SecurityManager.checkPackageAccess,
* SecurityManager.checkMemberAccess(this, EnumSet.MEMBER) and ReflectPermission("suppressAccessChecks").
* </p>
*
*
* @author Joe Walnes
* @author J&ouml;rg Schaible
*/
public class EnumSetConverter implements Converter {

private final static Field typeField = Fields.locate(EnumSet.class, Class.class, false);
private final Mapper mapper;

public EnumSetConverter(final Mapper mapper) {
Expand All @@ -46,13 +45,13 @@ public EnumSetConverter(final Mapper mapper) {

@Override
public boolean canConvert(final Class<?> type) {
return typeField != null && type != null && EnumSet.class.isAssignableFrom(type);
return type != null && EnumSet.class.isAssignableFrom(type) && Reflections.typeField != null;
}

@Override
public void marshal(final Object source, final HierarchicalStreamWriter writer, final MarshallingContext context) {
final EnumSet<?> set = (EnumSet<?>)source;
final Class<?> enumTypeForSet = (Class<?>)Fields.read(typeField, set);
final Class<?> enumTypeForSet = (Class<?>)Fields.read(Reflections.typeField, set);
final String attributeName = mapper.aliasForSystemAttribute("enum-type");
if (attributeName != null) {
writer.addAttribute(attributeName, mapper.serializedClass(enumTypeForSet));
Expand Down Expand Up @@ -99,4 +98,7 @@ private <T extends Enum<T>> EnumSet<T> create(final Class<T> type, final String
return set;
}

private static class Reflections {
private final static Field typeField = Fields.locate(EnumSet.class, Class.class, false);
}
}

0 comments on commit eb2dcfd

Please sign in to comment.