Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prevent 'illegal reflective access' problems when instantiating XStream #216

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
}
}
Loading