8336585: BoundAttribute.readEntryList not type-safe

Reviewed-by: asotona
This commit is contained in:
Chen Liang 2024-07-18 21:46:19 +00:00
parent b44632aa15
commit 902c2afb67
2 changed files with 57 additions and 22 deletions
src/java.base/share/classes/jdk/internal/classfile/impl
test/jdk/jdk/classfile

@ -25,12 +25,6 @@
package jdk.internal.classfile.impl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.lang.classfile.*;
import java.lang.classfile.attribute.*;
import java.lang.classfile.constantpool.ClassEntry;
@ -40,7 +34,14 @@ import java.lang.classfile.constantpool.LoadableConstantEntry;
import java.lang.classfile.constantpool.ModuleEntry;
import java.lang.classfile.constantpool.NameAndTypeEntry;
import java.lang.classfile.constantpool.PackageEntry;
import java.lang.classfile.constantpool.PoolEntry;
import java.lang.classfile.constantpool.Utf8Entry;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import jdk.internal.access.SharedSecrets;
import static java.lang.classfile.Attributes.*;
@ -116,13 +117,13 @@ public abstract sealed class BoundAttribute<T extends Attribute<T>>
return String.format("Attribute[name=%s]", mapper.name());
}
<E> List<E> readEntryList(int p) {
<E extends PoolEntry> List<E> readEntryList(int p, Class<E> type) {
int cnt = classReader.readU2(p);
p += 2;
var entries = new Object[cnt];
int end = p + (cnt * 2);
for (int i = 0; p < end; i++, p += 2) {
entries[i] = classReader.readEntry(p);
entries[i] = classReader.readEntry(p, type);
}
return SharedSecrets.getJavaUtilCollectionAccess().listFromTrustedArray(entries);
}
@ -549,7 +550,7 @@ public abstract sealed class BoundAttribute<T extends Attribute<T>>
@Override
public List<ClassEntry> exceptions() {
if (exceptions == null) {
exceptions = readEntryList(payloadStart);
exceptions = readEntryList(payloadStart, ClassEntry.class);
}
return exceptions;
}
@ -645,7 +646,7 @@ public abstract sealed class BoundAttribute<T extends Attribute<T>>
PackageEntry pe = classReader.readEntry(p, PackageEntry.class);
int exportFlags = classReader.readU2(p + 2);
p += 4;
List<ModuleEntry> exportsTo = readEntryList(p);
List<ModuleEntry> exportsTo = readEntryList(p, ModuleEntry.class);
p += 2 + exportsTo.size() * 2;
elements[i] = ModuleExportInfo.of(pe, exportFlags, exportsTo);
}
@ -660,7 +661,7 @@ public abstract sealed class BoundAttribute<T extends Attribute<T>>
PackageEntry po = classReader.readEntry(p, PackageEntry.class);
int opensFlags = classReader.readU2(p + 2);
p += 4;
List<ModuleEntry> opensTo = readEntryList(p);
List<ModuleEntry> opensTo = readEntryList(p, ModuleEntry.class);
p += 2 + opensTo.size() * 2;
elements[i] = ModuleOpenInfo.of(po, opensFlags, opensTo);
}
@ -668,7 +669,7 @@ public abstract sealed class BoundAttribute<T extends Attribute<T>>
}
{
uses = readEntryList(p);
uses = readEntryList(p, ClassEntry.class);
p += 2 + uses.size() * 2;
int cnt = classReader.readU2(p);
p += 2;
@ -677,7 +678,7 @@ public abstract sealed class BoundAttribute<T extends Attribute<T>>
for (int i = 0; i < cnt; i++) {
ClassEntry c = classReader.readEntry(p, ClassEntry.class);
p += 2;
List<ClassEntry> providesWith = readEntryList(p);
List<ClassEntry> providesWith = readEntryList(p, ClassEntry.class);
p += 2 + providesWith.size() * 2;
elements[i] = ModuleProvideInfo.of(c, providesWith);
}
@ -697,7 +698,7 @@ public abstract sealed class BoundAttribute<T extends Attribute<T>>
@Override
public List<PackageEntry> packages() {
if (packages == null) {
packages = readEntryList(payloadStart);
packages = readEntryList(payloadStart, PackageEntry.class);
}
return packages;
}
@ -715,7 +716,7 @@ public abstract sealed class BoundAttribute<T extends Attribute<T>>
@Override
public List<ClassEntry> nestMembers() {
if (members == null) {
members = readEntryList(payloadStart);
members = readEntryList(payloadStart, ClassEntry.class);
}
return members;
}
@ -744,7 +745,7 @@ public abstract sealed class BoundAttribute<T extends Attribute<T>>
int p = payloadStart + 2;
for (int i = 0; i < size; ++i) {
final var handle = classReader.readEntry(p, AbstractPoolEntry.MethodHandleEntryImpl.class);
final List<LoadableConstantEntry> args = readEntryList(p + 2);
final List<LoadableConstantEntry> args = readEntryList(p + 2, LoadableConstantEntry.class);
p += 4 + args.size() * 2;
int hash = BootstrapMethodEntryImpl.computeHashCode(handle, args);
bs[i] = new BootstrapMethodEntryImpl(classReader, i, hash, handle, args);
@ -924,7 +925,7 @@ public abstract sealed class BoundAttribute<T extends Attribute<T>>
@Override
public List<ClassEntry> permittedSubclasses() {
if (permittedSubclasses == null) {
permittedSubclasses = readEntryList(payloadStart);
permittedSubclasses = readEntryList(payloadStart, ClassEntry.class);
}
return permittedSubclasses;
}

@ -1,5 +1,5 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -23,19 +23,24 @@
/*
* @test
* @bug 8304837
* @bug 8304837 8336585
* @summary Testing BoundAttributes
* @run junit BoundAttributeTest
*/
import jdk.internal.classfile.impl.DirectClassBuilder;
import jdk.internal.classfile.impl.UnboundAttribute;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.opentest4j.AssertionFailedError;
import java.lang.classfile.Attributes;
import java.lang.classfile.BufWriter;
import java.lang.classfile.ClassModel;
import java.lang.classfile.ClassFile;
import java.lang.classfile.CodeBuilder;
import java.lang.classfile.attribute.MethodParameterInfo;
import java.lang.classfile.attribute.MethodParametersAttribute;
import org.junit.jupiter.api.Test;
import org.opentest4j.AssertionFailedError;
import java.lang.classfile.constantpool.ConstantPoolException;
import java.lang.constant.ClassDesc;
import java.lang.constant.ConstantDescs;
import java.lang.constant.MethodTypeDesc;
@ -67,4 +72,33 @@ class BoundAttributeTest {
List<MethodParameterInfo> parameters = assertDoesNotThrow(methodParametersAttribute::parameters);
assertTrue(parameters.get(0).name().isEmpty());
}
@Test
void testBadEntryTypeInList() {
var cf = ClassFile.of();
// Craft an attribute list with index to badly typed attributes
var bytes = cf.build(ClassDesc.of("Test"), clb -> {
var cp = clb.constantPool();
var oneClassString = cp.utf8Entry("Test$Ape");
var oneClass = cp.classEntry(oneClassString);
((DirectClassBuilder) clb).writeAttribute(new UnboundAttribute.AdHocAttribute<>(Attributes.nestMembers()) {
@Override
public void writeBody(BufWriter b) {
b.writeU2(2);
b.writeIndex(oneClass);
b.writeIndex(oneClassString);
}
});
});
var nm = cf.parse(bytes).findAttribute(Attributes.nestMembers()).orElseThrow();
Assertions.assertThrows(ConstantPoolException.class, () -> {
int sum = 0;
// this should throw CPE upon encountering non-ClassEntry
for (var member : nm.nestMembers()) {
sum += member.index();
}
});
}
}