Skip to content

Commit fb63cba

Browse files
committed
8330684: ClassFile API runs into StackOverflowError while parsing certain class' bytes
Reviewed-by: psandoz
1 parent 4e5c25e commit fb63cba

File tree

2 files changed

+32
-9
lines changed

2 files changed

+32
-9
lines changed

src/java.base/share/classes/jdk/internal/classfile/impl/ClassReaderImpl.java

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ public int flags() {
150150
@Override
151151
public ClassEntry thisClassEntry() {
152152
if (thisClass == null) {
153-
thisClass = readEntry(thisClassPos, ClassEntry.class);
153+
thisClass = readClassEntry(thisClassPos);
154154
}
155155
return thisClass;
156156
}
@@ -339,6 +339,10 @@ void writeConstantPoolEntries(BufWriter buf) {
339339
// Constantpool
340340
@Override
341341
public PoolEntry entryByIndex(int index) {
342+
return entryByIndex(index, 0, 0xff);
343+
}
344+
345+
private PoolEntry entryByIndex(int index, int lowerBoundTag, int upperBoundTag) {
342346
if (index <= 0 || index >= constantPoolCount) {
343347
throw new ConstantPoolException("Bad CP index: " + index);
344348
}
@@ -349,6 +353,10 @@ public PoolEntry entryByIndex(int index) {
349353
throw new ConstantPoolException("Unusable CP index: " + index);
350354
}
351355
int tag = readU1(offset);
356+
if (tag < lowerBoundTag || tag > upperBoundTag) {
357+
throw new ConstantPoolException(
358+
"Bad tag (" + tag + ") at index (" + index + ") position (" + offset + ")");
359+
}
352360
final int q = offset + 1;
353361
info = switch (tag) {
354362
case TAG_UTF8 -> new AbstractPoolEntry.Utf8EntryImpl(this, index, buffer, q + 2, readU2(q));
@@ -367,7 +375,7 @@ public PoolEntry entryByIndex(int index) {
367375
case TAG_NAMEANDTYPE -> new AbstractPoolEntry.NameAndTypeEntryImpl(this, index, (AbstractPoolEntry.Utf8EntryImpl) readUtf8Entry(q),
368376
(AbstractPoolEntry.Utf8EntryImpl) readUtf8Entry(q + 2));
369377
case TAG_METHODHANDLE -> new AbstractPoolEntry.MethodHandleEntryImpl(this, index, readU1(q),
370-
(AbstractPoolEntry.AbstractMemberRefEntry) readEntry(q + 1));
378+
readEntry(q + 1, AbstractPoolEntry.AbstractMemberRefEntry.class, TAG_FIELDREF, TAG_INTERFACEMETHODREF));
371379
case TAG_METHODTYPE -> new AbstractPoolEntry.MethodTypeEntryImpl(this, index, (AbstractPoolEntry.Utf8EntryImpl) readUtf8Entry(q));
372380
case TAG_CONSTANTDYNAMIC -> new AbstractPoolEntry.ConstantDynamicEntryImpl(this, index, readU2(q), (AbstractPoolEntry.NameAndTypeEntryImpl) readNameAndTypeEntry(q + 2));
373381
case TAG_INVOKEDYNAMIC -> new AbstractPoolEntry.InvokeDynamicEntryImpl(this, index, readU2(q), (AbstractPoolEntry.NameAndTypeEntryImpl) readNameAndTypeEntry(q + 2));
@@ -423,7 +431,15 @@ public PoolEntry readEntry(int pos) {
423431

424432
@Override
425433
public <T extends PoolEntry> T readEntry(int pos, Class<T> cls) {
426-
var e = readEntry(pos);
434+
return readEntry(pos, cls, 0, 0xff);
435+
}
436+
437+
private <T extends PoolEntry> T readEntry(int pos, Class<T> cls, int expectedTag) {
438+
return readEntry(pos, cls, expectedTag, expectedTag);
439+
}
440+
441+
private <T extends PoolEntry> T readEntry(int pos, Class<T> cls, int lowerBoundTag, int upperBoundTag) {
442+
var e = entryByIndex(readU2(pos), lowerBoundTag, upperBoundTag);
427443
if (cls.isInstance(e)) return cls.cast(e);
428444
throw new ConstantPoolException("Not a " + cls.getSimpleName() + " at index: " + readU2(pos));
429445
}
@@ -454,27 +470,27 @@ public Utf8Entry readUtf8EntryOrNull(int pos) {
454470

455471
@Override
456472
public ModuleEntry readModuleEntry(int pos) {
457-
return readEntry(pos, ModuleEntry.class);
473+
return readEntry(pos, ModuleEntry.class, TAG_MODULE);
458474
}
459475

460476
@Override
461477
public PackageEntry readPackageEntry(int pos) {
462-
return readEntry(pos, PackageEntry.class);
478+
return readEntry(pos, PackageEntry.class, TAG_PACKAGE);
463479
}
464480

465481
@Override
466482
public ClassEntry readClassEntry(int pos) {
467-
return readEntry(pos, ClassEntry.class);
483+
return readEntry(pos, ClassEntry.class, TAG_CLASS);
468484
}
469485

470486
@Override
471487
public NameAndTypeEntry readNameAndTypeEntry(int pos) {
472-
return readEntry(pos, NameAndTypeEntry.class);
488+
return readEntry(pos, NameAndTypeEntry.class, TAG_NAMEANDTYPE);
473489
}
474490

475491
@Override
476492
public MethodHandleEntry readMethodHandleEntry(int pos) {
477-
return readEntry(pos, MethodHandleEntry.class);
493+
return readEntry(pos, MethodHandleEntry.class, TAG_METHODHANDLE);
478494
}
479495

480496
@Override

test/jdk/jdk/classfile/LimitsTest.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,15 @@
2323

2424
/*
2525
* @test
26-
* @bug 8320360
26+
* @bug 8320360 8330684
2727
* @summary Testing ClassFile limits.
2828
* @run junit LimitsTest
2929
*/
3030
import java.lang.constant.ClassDesc;
3131
import java.lang.constant.ConstantDescs;
3232
import java.lang.constant.MethodTypeDesc;
3333
import java.lang.classfile.ClassFile;
34+
import java.lang.classfile.constantpool.ConstantPoolException;
3435
import jdk.internal.classfile.impl.LabelContext;
3536
import org.junit.jupiter.api.Test;
3637
import static org.junit.jupiter.api.Assertions.*;
@@ -92,4 +93,10 @@ void testReadingOutOfBounds() {
9293
assertThrows(IllegalArgumentException.class, () -> ClassFile.of().parse(new byte[]{(byte)0xCA, (byte)0xFE, (byte)0xBA, (byte)0xBE}), "reading magic only");
9394
assertThrows(IllegalArgumentException.class, () -> ClassFile.of().parse(new byte[]{(byte)0xCA, (byte)0xFE, (byte)0xBA, (byte)0xBE, 0, 0, 0, 0, 0, 2}), "reading invalid CP size");
9495
}
96+
97+
@Test
98+
void testInvalidClassEntry() {
99+
assertThrows(ConstantPoolException.class, () -> ClassFile.of().parse(new byte[]{(byte)0xCA, (byte)0xFE, (byte)0xBA, (byte)0xBE,
100+
0, 0, 0, 0, 0, 2, ClassFile.TAG_METHODREF, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}).thisClass());
101+
}
95102
}

0 commit comments

Comments
 (0)