Skip to content

Commit 575806c

Browse files
SirYwellliach
authored andcommitted
8358078: javap crashes with NPE on preview class file
Reviewed-by: liach
1 parent 8f8b367 commit 575806c

2 files changed

Lines changed: 102 additions & 2 deletions

File tree

src/jdk.jdeps/share/classes/com/sun/tools/javap/ClassWriter.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,9 +118,11 @@ protected void setMethod(MethodModel m) {
118118
protected ClassFileFormatVersion cffv() {
119119
var major = classModel.majorVersion();
120120
if (major < JAVA_1_VERSION || major > ClassFile.latestMajorVersion())
121-
return null;
121+
// something not representable by CFFV, let's fall back
122+
return ClassFileFormatVersion.latest();
122123
if (major >= JAVA_12_VERSION && classModel.minorVersion() != 0) {
123-
return null;
124+
// preview versions aren't explicitly supported, but latest is good enough for now
125+
return ClassFileFormatVersion.latest();
124126
}
125127
return ClassFileFormatVersion.fromMajor(major);
126128
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/*
2+
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/*
25+
* @test
26+
* @bug 8358078
27+
* @summary javap should not crash due to class file versions
28+
* @library /tools/lib
29+
* @modules jdk.jdeps/com.sun.tools.javap
30+
* @run junit ClassFileVersionTest
31+
*/
32+
33+
import org.junit.jupiter.params.ParameterizedTest;
34+
import org.junit.jupiter.params.provider.Arguments;
35+
import org.junit.jupiter.params.provider.MethodSource;
36+
import toolbox.JavapTask;
37+
import toolbox.Task;
38+
import toolbox.ToolBox;
39+
40+
import java.lang.classfile.ClassFile;
41+
import java.lang.constant.ClassDesc;
42+
import java.lang.reflect.AccessFlag;
43+
import java.lang.reflect.ClassFileFormatVersion;
44+
import java.nio.file.Files;
45+
import java.nio.file.Path;
46+
import java.util.stream.Stream;
47+
48+
import static org.junit.jupiter.api.Assertions.assertEquals;
49+
import static org.junit.jupiter.params.provider.Arguments.of;
50+
51+
public class ClassFileVersionTest {
52+
53+
final ToolBox toolBox = new ToolBox();
54+
55+
public static Stream<Arguments> classFiles() {
56+
int major17 = ClassFileFormatVersion.RELEASE_17.major();
57+
int preview = Character.MAX_VALUE;
58+
int majorLatest = ClassFileFormatVersion.latest().major();
59+
AccessFlag[] noFlags = {};
60+
return Stream.of(
61+
of(false, major17, 0, noFlags),
62+
of(false, major17, preview, noFlags),
63+
of(false, 0, 0, noFlags),
64+
of(false, major17, 0, new AccessFlag[]{AccessFlag.PUBLIC}),
65+
of(false, major17, preview, new AccessFlag[]{AccessFlag.PUBLIC}),
66+
of(false, majorLatest, preview, new AccessFlag[]{AccessFlag.PUBLIC}),
67+
of(true, majorLatest, 0, new AccessFlag[]{AccessFlag.BRIDGE}), // misplaced access flag
68+
of(true, majorLatest, preview, new AccessFlag[]{AccessFlag.BRIDGE}) // misplaced access flag
69+
);
70+
}
71+
72+
private static byte[] createClassFile(int major, int minor, AccessFlag[] classFlags) {
73+
return ClassFile.of().build(ClassDesc.of("Test"), (builder) -> {
74+
// manually assemble flag bits to avoid exception in ClassFile api
75+
int flags = 0;
76+
for (AccessFlag classFlag : classFlags) {
77+
flags |= classFlag.mask();
78+
}
79+
builder.withVersion(major, minor).withFlags(flags);
80+
});
81+
}
82+
83+
@ParameterizedTest
84+
@MethodSource("classFiles")
85+
void test(boolean shouldError, int major, int minor, AccessFlag[] classFlags) throws Throwable {
86+
87+
Files.write(Path.of("cf.class"), createClassFile(major, minor, classFlags));
88+
89+
var lines = new JavapTask(toolBox)
90+
.classes("cf.class")
91+
.options("-c", "-p", "-v")
92+
.run(shouldError ? Task.Expect.FAIL : Task.Expect.SUCCESS)
93+
.writeAll()
94+
.getOutputLines(Task.OutputKind.DIRECT);
95+
96+
assertEquals(shouldError, lines.stream().anyMatch(l -> l.startsWith("Error: Access Flags:")), "printed error");
97+
}
98+
}

0 commit comments

Comments
 (0)