Skip to content

Commit a64ce58

Browse files
authored
Add checks for generic types and discard any unexpected types (#430)
* Add corrupted signature test * Add mismatched generics catches * Fix all the cases where the tests indicated I messed up Also upload the intended test result for TestCorruptedSignatures
1 parent 2e50dff commit a64ce58

File tree

12 files changed

+346
-32
lines changed

12 files changed

+346
-32
lines changed

plugins/kotlin/src/main/java/org/vineflower/kotlin/KotlinWriter.java

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.jetbrains.java.decompiler.struct.gen.generics.GenericClassDescriptor;
3333
import org.jetbrains.java.decompiler.struct.gen.generics.GenericFieldDescriptor;
3434
import org.jetbrains.java.decompiler.struct.gen.generics.GenericMethodDescriptor;
35+
import org.jetbrains.java.decompiler.struct.gen.generics.GenericsChecker;
3536
import org.jetbrains.java.decompiler.util.*;
3637
import org.jetbrains.java.decompiler.util.collections.VBStyleCollection;
3738
import org.vineflower.kotlin.expr.KAnnotationExprent;
@@ -769,6 +770,15 @@ private void writeClassDefinition(ClassNode node, TextBuffer buffer, int indent,
769770
}
770771

771772
GenericClassDescriptor descriptor = cl.getSignature();
773+
if (descriptor != null) {
774+
VarType superclass = new VarType(cl.superClass.getString(), true);
775+
List<VarType> interfaces = Arrays.stream(cl.getInterfaceNames())
776+
.map(s -> new VarType(s, true))
777+
.toList();
778+
779+
descriptor.verifyTypes(superclass, interfaces);
780+
}
781+
772782
if (descriptor != null && !descriptor.fparameters.isEmpty()) {
773783
appendTypeParameters(buffer, descriptor.fparameters, descriptor.fbounds);
774784
}
@@ -861,6 +871,9 @@ public void writeField(ClassWrapper wrapper, StructClass cl, StructField fd, Tex
861871
Map.Entry<VarType, GenericFieldDescriptor> fieldTypeData = getFieldTypeData(fd);
862872
VarType fieldType = fieldTypeData.getKey();
863873
GenericFieldDescriptor descriptor = fieldTypeData.getValue();
874+
if (descriptor != null) {
875+
descriptor.verifyType(cl.getSignature(), fieldType);
876+
}
864877

865878
if (!isEnum) {
866879
buffer.append(ExprProcessor.getCastTypeName(descriptor == null ? fieldType : descriptor.type));
@@ -1045,6 +1058,41 @@ public boolean writeMethod(ClassNode node, StructMethod mt, int methodIndex, Tex
10451058
}
10461059

10471060
GenericMethodDescriptor descriptor = mt.getSignature();
1061+
if (descriptor != null) {
1062+
List<VarType> params = new ArrayList<>(Arrays.asList(mt.methodDescriptor().params));
1063+
if ((node.access & CodeConstants.ACC_ENUM) != 0 && init) {
1064+
// Signatures skip enum parameters, the checker must as well
1065+
params.remove(0);
1066+
params.remove(0);
1067+
}
1068+
1069+
StructExceptionsAttribute attr = mt.getAttribute(StructGeneralAttribute.ATTRIBUTE_EXCEPTIONS);
1070+
List<VarType> exceptions = new ArrayList<>();
1071+
if (attr != null) {
1072+
for (int i = 0; i < attr.getThrowsExceptions().size(); i++) {
1073+
exceptions.add(new VarType(attr.getExcClassname(i, node.classStruct.getPool()), true));
1074+
}
1075+
}
1076+
1077+
GenericClassDescriptor classSig = (mt.getAccessFlags() & CodeConstants.ACC_STATIC) == 0 ? node.classStruct.getSignature() : null;
1078+
GenericsChecker checker = null;
1079+
if (classSig != null) {
1080+
checker = classSig.getChecker();
1081+
1082+
ClassNode currentParent = node.parent;
1083+
while (currentParent != null) {
1084+
GenericClassDescriptor parentSignature = currentParent.classStruct.getSignature();
1085+
if (parentSignature != null) {
1086+
checker = checker.copy(parentSignature.getChecker());
1087+
}
1088+
1089+
currentParent = currentParent.parent;
1090+
}
1091+
}
1092+
1093+
descriptor.verifyTypes(checker, params, mt.methodDescriptor().ret, exceptions);
1094+
}
1095+
10481096
boolean throwsExceptions = false;
10491097
int paramCount = 0;
10501098

src/org/jetbrains/java/decompiler/main/ClassWriter.java

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.jetbrains.java.decompiler.struct.gen.generics.GenericClassDescriptor;
3838
import org.jetbrains.java.decompiler.struct.gen.generics.GenericFieldDescriptor;
3939
import org.jetbrains.java.decompiler.struct.gen.generics.GenericMethodDescriptor;
40+
import org.jetbrains.java.decompiler.struct.gen.generics.GenericsChecker;
4041
import org.jetbrains.java.decompiler.util.InterpreterUtil;
4142
import org.jetbrains.java.decompiler.util.Key;
4243
import org.jetbrains.java.decompiler.util.TextBuffer;
@@ -704,6 +705,15 @@ else if (components != null) {
704705
buffer.appendClass(node.simpleName, true, cl.qualifiedName);
705706

706707
GenericClassDescriptor descriptor = cl.getSignature();
708+
if (descriptor != null) {
709+
VarType superclass = new VarType(cl.superClass.getString(), true);
710+
List<VarType> interfaces = Arrays.stream(cl.getInterfaceNames())
711+
.map(s -> new VarType(s, true))
712+
.toList();
713+
714+
descriptor.verifyTypes(superclass, interfaces);
715+
}
716+
707717
if (descriptor != null && !descriptor.fparameters.isEmpty()) {
708718
appendTypeParameters(buffer, descriptor.fparameters, descriptor.fbounds);
709719
}
@@ -834,6 +844,9 @@ public void writeField(ClassWrapper wrapper, StructClass cl, StructField fd, Tex
834844
Map.Entry<VarType, GenericFieldDescriptor> fieldTypeData = getFieldTypeData(fd);
835845
VarType fieldType = fieldTypeData.getKey();
836846
GenericFieldDescriptor descriptor = fieldTypeData.getValue();
847+
if (descriptor != null) {
848+
descriptor.verifyType(cl.getSignature(), fieldType);
849+
}
837850

838851
if (!isEnum) {
839852
buffer.appendCastTypeName(descriptor == null ? fieldType : descriptor.type);
@@ -1120,6 +1133,48 @@ public boolean writeMethod(ClassNode node, StructMethod mt, int methodIndex, Tex
11201133
}
11211134

11221135
GenericMethodDescriptor descriptor = mt.getSignature();
1136+
if (descriptor != null) {
1137+
List<VarType> params = new ArrayList<>(Arrays.asList(mt.methodDescriptor().params));
1138+
1139+
if (init && node.classStruct.hasModifier(CodeConstants.ACC_ENUM)) {
1140+
// Enum name and ordinal parameters need to be explicitly excluded
1141+
params.remove(0);
1142+
params.remove(0);
1143+
}
1144+
1145+
// Exclude any parameters that the signature itself won't contain
1146+
List<VarVersionPair> mask = methodWrapper.synthParameters;
1147+
if (mask != null) {
1148+
for (int i = 0, j = 0; i < mask.size(); i++, j++) {
1149+
if (mask.get(i) != null) {
1150+
params.remove(j--);
1151+
}
1152+
}
1153+
}
1154+
1155+
StructExceptionsAttribute attr = mt.getAttribute(StructGeneralAttribute.ATTRIBUTE_EXCEPTIONS);
1156+
List<VarType> exceptions = new ArrayList<>();
1157+
if (attr != null) {
1158+
for (int i = 0; i < attr.getThrowsExceptions().size(); i++) {
1159+
exceptions.add(new VarType(attr.getExcClassname(i, node.classStruct.getPool()), true));
1160+
}
1161+
}
1162+
1163+
GenericsChecker checker = new GenericsChecker();
1164+
1165+
ClassNode currentNode = node;
1166+
while (currentNode != null) {
1167+
GenericClassDescriptor parentSignature = currentNode.classStruct.getSignature();
1168+
if (parentSignature != null) {
1169+
checker = checker.copy(parentSignature.getChecker());
1170+
}
1171+
1172+
currentNode = currentNode.parent;
1173+
}
1174+
1175+
descriptor.verifyTypes(checker, params, mt.methodDescriptor().ret, exceptions);
1176+
}
1177+
11231178
boolean throwsExceptions = false;
11241179
int paramCount = 0;
11251180

src/org/jetbrains/java/decompiler/main/RecordHelper.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import org.jetbrains.java.decompiler.code.CodeConstants;
44
import org.jetbrains.java.decompiler.main.rels.MethodWrapper;
55
import org.jetbrains.java.decompiler.modules.decompiler.exps.*;
6-
import org.jetbrains.java.decompiler.modules.decompiler.flow.DirectGraph;
76
import org.jetbrains.java.decompiler.modules.decompiler.stats.BasicBlockStatement;
87
import org.jetbrains.java.decompiler.modules.decompiler.stats.RootStatement;
98
import org.jetbrains.java.decompiler.modules.decompiler.stats.Statement;
@@ -201,7 +200,10 @@ private static void recordComponentToJava(TextBuffer buffer, StructClass cl, Str
201200
VarType fieldType = new VarType(cd.getDescriptor(), false);
202201
GenericFieldDescriptor descriptor = cd.getSignature();
203202

204-
if (descriptor != null) fieldType = descriptor.type;
203+
if (descriptor != null) {
204+
descriptor.verifyType(cl.getSignature(), fieldType);
205+
fieldType = descriptor.type;
206+
}
205207

206208
buffer.appendCastTypeName(varArgComponent ? fieldType.decreaseArrayDim() : fieldType);
207209
if (varArgComponent) {

src/org/jetbrains/java/decompiler/modules/decompiler/exps/FieldExprent.java

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import org.jetbrains.java.decompiler.struct.gen.CodeType;
2020
import org.jetbrains.java.decompiler.struct.gen.FieldDescriptor;
2121
import org.jetbrains.java.decompiler.struct.gen.VarType;
22+
import org.jetbrains.java.decompiler.struct.gen.generics.GenericFieldDescriptor;
2223
import org.jetbrains.java.decompiler.struct.gen.generics.GenericType;
2324
import org.jetbrains.java.decompiler.struct.match.MatchEngine;
2425
import org.jetbrains.java.decompiler.struct.match.MatchNode;
@@ -78,31 +79,36 @@ public VarType getInferredExprType(VarType upperBound) {
7879
cl = cl.superClass == null ? null : DecompilerContext.getStructContext().getClass((String)cl.superClass.value);
7980
}
8081

81-
if (ft != null && ft.getSignature() != null) {
82-
VarType ret = ft.getSignature().type.remap(types.getOrDefault(cl.qualifiedName, Collections.emptyMap()));
82+
if (ft != null) {
83+
GenericFieldDescriptor signature = ft.getSignature();
84+
if (signature != null) {
85+
signature.verifyType(cl.getSignature(), descriptor.type);
8386

84-
if (instance != null && cl.getSignature() != null) {
85-
VarType instType = instance.getInferredExprType(null);
87+
VarType ret = signature.type.remap(types.getOrDefault(cl.qualifiedName, Collections.emptyMap()));
8688

87-
if (instType.isGeneric() && instType.type != CodeType.GENVAR) {
88-
GenericType ginstance = (GenericType)instType;
89+
if (instance != null && cl.getSignature() != null) {
90+
VarType instType = instance.getInferredExprType(null);
8991

90-
cl = DecompilerContext.getStructContext().getClass(instType.value);
91-
if (cl != null && cl.getSignature() != null) {
92-
Map<VarType, VarType> tempMap = new HashMap<>();
93-
cl.getSignature().genericType.mapGenVarsTo(ginstance, tempMap);
94-
VarType _new = ret.remap(tempMap);
92+
if (instType.isGeneric() && instType.type != CodeType.GENVAR) {
93+
GenericType ginstance = (GenericType) instType;
9594

96-
if (_new != null) {
97-
ret = _new;
98-
} else {
99-
ret = getExprType();
95+
cl = DecompilerContext.getStructContext().getClass(instType.value);
96+
if (cl != null && cl.getSignature() != null) {
97+
Map<VarType, VarType> tempMap = new HashMap<>();
98+
cl.getSignature().genericType.mapGenVarsTo(ginstance, tempMap);
99+
VarType _new = ret.remap(tempMap);
100+
101+
if (_new != null) {
102+
ret = _new;
103+
} else {
104+
ret = getExprType();
105+
}
100106
}
101107
}
102108
}
103-
}
104109

105-
return ret;
110+
return ret;
111+
}
106112
}
107113

108114
return getExprType();

src/org/jetbrains/java/decompiler/modules/decompiler/exps/VarExprent.java

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@ public VarType getDefinitionVarType() {
230230
if (lvt.getSignature() != null) {
231231
GenericFieldDescriptor descriptor = GenericMain.parseFieldSignature(lvt.getSignature());
232232
if (descriptor != null) {
233+
descriptor.verifyLoosely(lvt.getVarType());
233234
return descriptor.type;
234235
}
235236
}
@@ -245,28 +246,39 @@ public VarType getDefinitionVarType() {
245246
}
246247
int visibleOffset = bytecode == null ? -1 : bytecode.length();
247248
if (originalIndex != null) {
248-
// first try from signature
249+
// Try loading the type from LVT attributes:
250+
// First try the signature if it's available and matches a known descriptor.
251+
// If signature not available, then try the descriptor.
252+
// If neither are present, fall back to the context-inferred type.
253+
254+
VarType type = null;
255+
StructLocalVariableTableAttribute lvt = method.methodStruct.getLocalVariableAttr();
256+
if (lvt != null) {
257+
String descriptor = lvt.getDescriptor(originalIndex, visibleOffset);
258+
if (descriptor != null) {
259+
type = new VarType(descriptor);
260+
}
261+
}
262+
249263
if (DecompilerContext.getOption(IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES)) {
250-
StructLocalVariableTypeTableAttribute attr =
264+
StructLocalVariableTypeTableAttribute lvtSignatures =
251265
method.methodStruct.getAttribute(StructGeneralAttribute.ATTRIBUTE_LOCAL_VARIABLE_TYPE_TABLE);
252-
if (attr != null) {
253-
String signature = attr.getSignature(originalIndex, visibleOffset);
266+
if (lvtSignatures != null) {
267+
String signature = lvtSignatures.getSignature(originalIndex, visibleOffset);
254268
if (signature != null) {
255269
GenericFieldDescriptor descriptor = GenericMain.parseFieldSignature(signature);
256270
if (descriptor != null) {
271+
if (type != null) {
272+
descriptor.verifyLoosely(type);
273+
}
257274
return descriptor.type;
258275
}
259276
}
260277
}
261278
}
262279

263-
// then try from descriptor
264-
StructLocalVariableTableAttribute attr = method.methodStruct.getLocalVariableAttr();
265-
if (attr != null) {
266-
String descriptor = attr.getDescriptor(originalIndex, visibleOffset);
267-
if (descriptor != null) {
268-
return new VarType(descriptor);
269-
}
280+
if (type != null) {
281+
return type;
270282
}
271283
}
272284
}

src/org/jetbrains/java/decompiler/struct/gen/generics/GenericClassDescriptor.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@
22
package org.jetbrains.java.decompiler.struct.gen.generics;
33

44
import java.util.ArrayList;
5+
import java.util.HashMap;
56
import java.util.List;
7+
import java.util.Map;
68

9+
import org.jetbrains.java.decompiler.main.DecompilerContext;
10+
import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;
711
import org.jetbrains.java.decompiler.struct.gen.VarType;
812

913
public class GenericClassDescriptor {
@@ -17,4 +21,40 @@ public class GenericClassDescriptor {
1721
public final List<String> fparameters = new ArrayList<>();
1822

1923
public final List<List<VarType>> fbounds = new ArrayList<>();
24+
25+
private GenericsChecker checker;
26+
27+
public GenericsChecker getChecker() {
28+
if (checker == null) {
29+
checker = new GenericsChecker(fparameters, fbounds);
30+
}
31+
32+
return checker;
33+
}
34+
35+
public void verifyTypes(VarType actualSuperclass, List<VarType> actualSuperInterfaces) {
36+
GenericsChecker checker = getChecker();
37+
38+
if (superclass != null) {
39+
if (!checker.isProperlyBounded(actualSuperclass, superclass)) {
40+
DecompilerContext.getLogger().writeMessage("Mismatched superclass signature, expected: " + actualSuperclass + ", actual: " + superclass.value, IFernflowerLogger.Severity.WARN);
41+
superclass = actualSuperclass;
42+
}
43+
}
44+
45+
for (int i = 0; i < superinterfaces.size(); i++) {
46+
VarType actualSuperInterface = i < actualSuperInterfaces.size() ? actualSuperInterfaces.get(i) : null;
47+
VarType superInterface = superinterfaces.get(i);
48+
49+
if (actualSuperInterface == null) {
50+
DecompilerContext.getLogger().writeMessage("Actual superinterface count is less than expected", IFernflowerLogger.Severity.WARN);
51+
break;
52+
}
53+
54+
if (!checker.isProperlyBounded(actualSuperInterface, superInterface)) {
55+
DecompilerContext.getLogger().writeMessage("Mismatched superinterface signature, expected: " + actualSuperInterface + ", actual: " + superInterface.value, IFernflowerLogger.Severity.WARN);
56+
superinterfaces.set(i, actualSuperInterface);
57+
}
58+
}
59+
}
2060
}

0 commit comments

Comments
 (0)