Skip to content

Commit eed3a53

Browse files
author
Vicente Romero
committed
8215510: j.l.c.ClassDesc is accepting descriptors not allowed by the spec
Reviewed-by: goetz
1 parent f2f7690 commit eed3a53

File tree

9 files changed

+116
-46
lines changed

9 files changed

+116
-46
lines changed

src/java.base/share/classes/java/lang/constant/ClassDesc.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -97,7 +97,10 @@ static ClassDesc of(String name) {
9797
*/
9898
static ClassDesc of(String packageName, String className) {
9999
ConstantUtils.validateBinaryClassName(requireNonNull(packageName));
100-
validateMemberName(requireNonNull(className));
100+
if (packageName.isEmpty()) {
101+
return of(className);
102+
}
103+
validateMemberName(requireNonNull(className), false);
101104
return ofDescriptor(String.format("L%s%s%s;",
102105
binaryToInternal(packageName),
103106
(packageName.length() > 0 ? "/" : ""),
@@ -130,6 +133,9 @@ static ClassDesc of(String packageName, String className) {
130133
*/
131134
static ClassDesc ofDescriptor(String descriptor) {
132135
requireNonNull(descriptor);
136+
if (descriptor.isEmpty()) {
137+
throw new IllegalArgumentException(String.format("not a valid reference type descriptor: %s", descriptor));
138+
}
133139
int depth = ConstantUtils.arrayDepth(descriptor);
134140
if (depth > ConstantUtils.MAX_ARRAY_TYPE_DESC_DIMENSIONS) {
135141
throw new IllegalArgumentException(String.format("Cannot create an array type descriptor with more than %d dimensions",
@@ -192,7 +198,7 @@ default ClassDesc arrayType(int rank) {
192198
* @throws IllegalArgumentException if the nested class name is invalid
193199
*/
194200
default ClassDesc nested(String nestedName) {
195-
validateMemberName(nestedName);
201+
validateMemberName(nestedName, false);
196202
if (!isClassOrInterface())
197203
throw new IllegalStateException("Outer class is not a class or interface type");
198204
return ClassDesc.ofDescriptor(String.format("%s$%s;", dropLastChar(descriptorString()), nestedName));

src/java.base/share/classes/java/lang/constant/ConstantUtils.java

Lines changed: 88 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -65,15 +65,15 @@ static String validateBinaryClassName(String name) {
6565
* @return the name passed if valid
6666
* @throws IllegalArgumentException if the member name is invalid
6767
*/
68-
public static String validateMemberName(String name) {
68+
public static String validateMemberName(String name, boolean method) {
6969
requireNonNull(name);
7070
if (name.length() == 0)
7171
throw new IllegalArgumentException("zero-length member name");
7272
for (int i=0; i<name.length(); i++) {
7373
char ch = name.charAt(i);
7474
if (ch == '.' || ch == ';' || ch == '[' || ch == '/')
7575
throw new IllegalArgumentException("Invalid member name: " + name);
76-
if (ch == '<' || ch == '>') {
76+
if (method && (ch == '<' || ch == '>')) {
7777
if (!pointyNames.contains(name))
7878
throw new IllegalArgumentException("Invalid member name: " + name);
7979
}
@@ -126,8 +126,8 @@ static List<String> parseMethodDescriptor(String descriptor) {
126126

127127
++cur; // skip '('
128128
while (cur < end && descriptor.charAt(cur) != ')') {
129-
int len = matchSig(descriptor, cur, end);
130-
if (len == 0 || descriptor.charAt(cur) == 'V')
129+
int len = skipOverFieldSignature(descriptor, cur, end, false);
130+
if (len == 0)
131131
throw new IllegalArgumentException("Bad method descriptor: " + descriptor);
132132
ptypes.add(descriptor.substring(cur, cur + len));
133133
cur += len;
@@ -136,41 +136,103 @@ static List<String> parseMethodDescriptor(String descriptor) {
136136
throw new IllegalArgumentException("Bad method descriptor: " + descriptor);
137137
++cur; // skip ')'
138138

139-
int rLen = matchSig(descriptor, cur, end);
139+
int rLen = skipOverFieldSignature(descriptor, cur, end, true);
140140
if (rLen == 0 || cur + rLen != end)
141141
throw new IllegalArgumentException("Bad method descriptor: " + descriptor);
142142
ptypes.add(0, descriptor.substring(cur, cur + rLen));
143143
return ptypes;
144144
}
145145

146+
private static final char JVM_SIGNATURE_ARRAY = '[';
147+
private static final char JVM_SIGNATURE_BYTE = 'B';
148+
private static final char JVM_SIGNATURE_CHAR = 'C';
149+
private static final char JVM_SIGNATURE_CLASS = 'L';
150+
private static final char JVM_SIGNATURE_ENDCLASS = ';';
151+
private static final char JVM_SIGNATURE_ENUM = 'E';
152+
private static final char JVM_SIGNATURE_FLOAT = 'F';
153+
private static final char JVM_SIGNATURE_DOUBLE = 'D';
154+
private static final char JVM_SIGNATURE_FUNC = '(';
155+
private static final char JVM_SIGNATURE_ENDFUNC = ')';
156+
private static final char JVM_SIGNATURE_INT = 'I';
157+
private static final char JVM_SIGNATURE_LONG = 'J';
158+
private static final char JVM_SIGNATURE_SHORT = 'S';
159+
private static final char JVM_SIGNATURE_VOID = 'V';
160+
private static final char JVM_SIGNATURE_BOOLEAN = 'Z';
161+
146162
/**
147163
* Validates that the characters at [start, end) within the provided string
148164
* describe a valid field type descriptor.
149-
*
150-
* @param str the descriptor string
165+
* @param descriptor the descriptor string
151166
* @param start the starting index into the string
152167
* @param end the ending index within the string
168+
* @param voidOK is void acceptable?
153169
* @return the length of the descriptor, or 0 if it is not a descriptor
154170
* @throws IllegalArgumentException if the descriptor string is not valid
155171
*/
156-
static int matchSig(String str, int start, int end) {
157-
if (start >= end || start >= str.length() || end > str.length())
158-
return 0;
159-
char c = str.charAt(start);
160-
if (c == 'L') {
161-
int endc = str.indexOf(';', start);
162-
int badc = str.indexOf('.', start);
163-
if (badc >= 0 && badc < endc)
164-
return 0;
165-
badc = str.indexOf('[', start);
166-
if (badc >= 0 && badc < endc)
167-
return 0;
168-
return (endc < 0) ? 0 : endc - start + 1;
169-
} else if (c == '[') {
170-
int t = matchSig(str, start+1, end);
171-
return (t > 0) ? t + 1 : 0;
172-
} else {
173-
return ("IJCSBFDZV".indexOf(c) >= 0) ? 1 : 0;
172+
@SuppressWarnings("fallthrough")
173+
static int skipOverFieldSignature(String descriptor, int start, int end, boolean voidOK) {
174+
int arrayDim = 0;
175+
int index = start;
176+
while (index < end) {
177+
switch (descriptor.charAt(index)) {
178+
case JVM_SIGNATURE_VOID: if (!voidOK) { return index; }
179+
case JVM_SIGNATURE_BOOLEAN:
180+
case JVM_SIGNATURE_BYTE:
181+
case JVM_SIGNATURE_CHAR:
182+
case JVM_SIGNATURE_SHORT:
183+
case JVM_SIGNATURE_INT:
184+
case JVM_SIGNATURE_FLOAT:
185+
case JVM_SIGNATURE_LONG:
186+
case JVM_SIGNATURE_DOUBLE:
187+
return index - start + 1;
188+
case JVM_SIGNATURE_CLASS:
189+
// Skip leading 'L' and ignore first appearance of ';'
190+
index++;
191+
int indexOfSemi = descriptor.indexOf(';', index);
192+
if (indexOfSemi != -1) {
193+
String unqualifiedName = descriptor.substring(index, indexOfSemi);
194+
boolean legal = verifyUnqualifiedClassName(unqualifiedName);
195+
if (!legal) {
196+
return 0;
197+
}
198+
return index - start + unqualifiedName.length() + 1;
199+
}
200+
return 0;
201+
case JVM_SIGNATURE_ARRAY:
202+
arrayDim++;
203+
if (arrayDim > MAX_ARRAY_TYPE_DESC_DIMENSIONS) {
204+
throw new IllegalArgumentException(String.format("Cannot create an array type descriptor with more than %d dimensions",
205+
ConstantUtils.MAX_ARRAY_TYPE_DESC_DIMENSIONS));
206+
}
207+
// The rest of what's there better be a legal descriptor
208+
index++;
209+
voidOK = false;
210+
break;
211+
default:
212+
return 0;
213+
}
214+
}
215+
return 0;
216+
}
217+
218+
static boolean verifyUnqualifiedClassName(String name) {
219+
for (int index = 0; index < name.length(); index++) {
220+
char ch = name.charAt(index);
221+
if (ch < 128) {
222+
if (ch == '.' || ch == ';' || ch == '[' ) {
223+
return false; // do not permit '.', ';', or '['
224+
}
225+
if (ch == '/') {
226+
// check for '//' or leading or trailing '/' which are not legal
227+
// unqualified name must not be empty
228+
if (index == 0 || index + 1 >= name.length() || name.charAt(index + 1) == '/') {
229+
return false;
230+
}
231+
}
232+
} else {
233+
index ++;
234+
}
174235
}
236+
return true;
175237
}
176238
}

src/java.base/share/classes/java/lang/constant/DirectMethodHandleDescImpl.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -68,7 +68,7 @@ final class DirectMethodHandleDescImpl implements DirectMethodHandleDesc {
6868

6969
requireNonNull(kind);
7070
validateClassOrInterface(requireNonNull(owner));
71-
validateMemberName(requireNonNull(name));
71+
validateMemberName(requireNonNull(name), true);
7272
requireNonNull(type);
7373

7474
switch (kind) {

src/java.base/share/classes/java/lang/constant/DynamicCallSiteDesc.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -75,7 +75,7 @@ private DynamicCallSiteDesc(DirectMethodHandleDesc bootstrapMethod,
7575
String invocationName,
7676
MethodTypeDesc invocationType,
7777
ConstantDesc[] bootstrapArgs) {
78-
this.invocationName = validateMemberName(requireNonNull(invocationName));
78+
this.invocationName = validateMemberName(requireNonNull(invocationName), true);
7979
this.invocationType = requireNonNull(invocationType);
8080
this.bootstrapMethod = requireNonNull(bootstrapMethod);
8181
this.bootstrapArgs = requireNonNull(bootstrapArgs.clone());

src/java.base/share/classes/java/lang/constant/DynamicConstantDesc.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -96,7 +96,7 @@ protected DynamicConstantDesc(DirectMethodHandleDesc bootstrapMethod,
9696
ClassDesc constantType,
9797
ConstantDesc... bootstrapArgs) {
9898
this.bootstrapMethod = requireNonNull(bootstrapMethod);
99-
this.constantName = validateMemberName(requireNonNull(constantName));
99+
this.constantName = validateMemberName(requireNonNull(constantName), true);
100100
this.constantType = requireNonNull(constantType);
101101
this.bootstrapArgs = requireNonNull(bootstrapArgs).clone();
102102

src/java.base/share/classes/java/lang/constant/ReferenceClassDescImpl.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -49,7 +49,7 @@ final class ReferenceClassDescImpl implements ClassDesc {
4949
*/
5050
ReferenceClassDescImpl(String descriptor) {
5151
requireNonNull(descriptor);
52-
int len = ConstantUtils.matchSig(descriptor, 0, descriptor.length());
52+
int len = ConstantUtils.skipOverFieldSignature(descriptor, 0, descriptor.length(), false);
5353
if (len == 0 || len == 1
5454
|| len != descriptor.length())
5555
throw new IllegalArgumentException(String.format("not a valid reference type descriptor: %s", descriptor));

test/jdk/java/lang/constant/ClassDescTest.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -42,6 +42,7 @@
4242

4343
/**
4444
* @test
45+
* @bug 8215510
4546
* @compile ClassDescTest.java
4647
* @run testng ClassDescTest
4748
* @summary unit tests for java.lang.constant.ClassDesc
@@ -215,7 +216,7 @@ public void testArrayClassDesc() throws ReflectiveOperationException {
215216
}
216217

217218
public void testBadClassDescs() {
218-
List<String> badDescriptors = List.of("II", "I;", "Q", "L",
219+
List<String> badDescriptors = List.of("II", "I;", "Q", "L", "",
219220
"java.lang.String", "[]", "Ljava/lang/String",
220221
"Ljava.lang.String;", "java/lang/String");
221222

test/jdk/java/lang/constant/NameValidationTest.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -23,6 +23,7 @@
2323

2424
/**
2525
* @test
26+
* @bug 8215510
2627
* @compile NameValidationTest.java
2728
* @run testng NameValidationTest
2829
* @summary unit tests for verifying member names
@@ -45,8 +46,8 @@ public class NameValidationTest {
4546
private static final String[] badMemberNames = new String[] {"xx.xx", "zz;zz", "[l", "aa/aa", "<cinit>"};
4647
private static final String[] goodMemberNames = new String[] {"<clinit>", "<init>", "3", "~", "$", "qq"};
4748

48-
private static final String[] badClassNames = new String[] {"zz;zz", "[l", "aa/aa"};
49-
private static final String[] goodClassNames = new String[] {"3", "~", "$", "qq", ".", "a.a"};
49+
private static final String[] badClassNames = new String[] {"zz;zz", "[l", "aa/aa", ".", "a..b"};
50+
private static final String[] goodClassNames = new String[] {"3", "~", "$", "qq", "a.a"};
5051

5152
public void testMemberNames() {
5253
DirectMethodHandleDesc mh = MethodHandleDesc.of(Kind.VIRTUAL, CD_String, "isEmpty", "()Z");

test/jdk/java/lang/constant/boottest/java.base/java/lang/constant/ConstantUtilsTest.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -43,14 +43,14 @@ public class ConstantUtilsTest {
4343

4444
public void testValidateMemberName() {
4545
try {
46-
ConstantUtils.validateMemberName(null);
46+
ConstantUtils.validateMemberName(null, false);
4747
fail("");
4848
} catch (NullPointerException e) {
4949
// good
5050
}
5151

5252
try {
53-
ConstantUtils.validateMemberName("");
53+
ConstantUtils.validateMemberName("", false);
5454
fail("");
5555
} catch (IllegalArgumentException e) {
5656
// good
@@ -59,7 +59,7 @@ public void testValidateMemberName() {
5959
List<String> badNames = List.of(".", ";", "[", "/", "<", ">");
6060
for (String n : badNames) {
6161
try {
62-
ConstantUtils.validateMemberName(n);
62+
ConstantUtils.validateMemberName(n, true);
6363
fail(n);
6464
} catch (IllegalArgumentException e) {
6565
// good

0 commit comments

Comments
 (0)