From 587f84725e43e6e2428c19413d93336a485ac75d Mon Sep 17 00:00:00 2001 From: Naveed Khan Date: Sun, 12 Jul 2026 02:58:58 +0530 Subject: [PATCH] use array element type not basic type for primitive array access getBasicType() drops the dimension count, so iaload on int[][] passed structural verification even though its component type is a reference; the ten primitive *aload/*astore constraints in InstConstraintVisitor now use getElementType() like the reference and byte/char siblings. --- .../structurals/InstConstraintVisitor.java | 20 ++-- .../verifier/VerifierArrayAccessTest.java | 3 + .../tests/TestArrayAccess05Creator.java | 92 +++++++++++++++++++ 3 files changed, 105 insertions(+), 10 deletions(-) create mode 100644 src/test/java/org/apache/bcel/verifier/tests/TestArrayAccess05Creator.java diff --git a/src/main/java/org/apache/bcel/verifier/structurals/InstConstraintVisitor.java b/src/main/java/org/apache/bcel/verifier/structurals/InstConstraintVisitor.java index 34c7f19a58..40481fbe98 100644 --- a/src/main/java/org/apache/bcel/verifier/structurals/InstConstraintVisitor.java +++ b/src/main/java/org/apache/bcel/verifier/structurals/InstConstraintVisitor.java @@ -518,7 +518,7 @@ public void visitDALOAD(final DALOAD o) { if (!(stack().peek(1) instanceof ArrayType)) { constraintViolated(o, "Stack next-to-top must be of type double[] but is '" + stack().peek(1) + "'."); } - final Type t = ((ArrayType) stack().peek(1)).getBasicType(); + final Type t = ((ArrayType) stack().peek(1)).getElementType(); if (t != Type.DOUBLE) { constraintViolated(o, "Stack next-to-top must be of type double[] but is '" + stack().peek(1) + "'."); } @@ -539,7 +539,7 @@ public void visitDASTORE(final DASTORE o) { if (!(stack().peek(2) instanceof ArrayType)) { constraintViolated(o, "Stack next-to-next-to-top must be of type double[] but is '" + stack().peek(2) + "'."); } - final Type t = ((ArrayType) stack().peek(2)).getBasicType(); + final Type t = ((ArrayType) stack().peek(2)).getElementType(); if (t != Type.DOUBLE) { constraintViolated(o, "Stack next-to-next-to-top must be of type double[] but is '" + stack().peek(2) + "'."); } @@ -825,7 +825,7 @@ public void visitFALOAD(final FALOAD o) { if (!(stack().peek(1) instanceof ArrayType)) { constraintViolated(o, "Stack next-to-top must be of type float[] but is '" + stack().peek(1) + "'."); } - final Type t = ((ArrayType) stack().peek(1)).getBasicType(); + final Type t = ((ArrayType) stack().peek(1)).getElementType(); if (t != Type.FLOAT) { constraintViolated(o, "Stack next-to-top must be of type float[] but is '" + stack().peek(1) + "'."); } @@ -846,7 +846,7 @@ public void visitFASTORE(final FASTORE o) { if (!(stack().peek(2) instanceof ArrayType)) { constraintViolated(o, "Stack next-to-next-to-top must be of type float[] but is '" + stack().peek(2) + "'."); } - final Type t = ((ArrayType) stack().peek(2)).getBasicType(); + final Type t = ((ArrayType) stack().peek(2)).getElementType(); if (t != Type.FLOAT) { constraintViolated(o, "Stack next-to-next-to-top must be of type float[] but is '" + stack().peek(2) + "'."); } @@ -1199,7 +1199,7 @@ public void visitIALOAD(final IALOAD o) { if (!(stack().peek(1) instanceof ArrayType)) { constraintViolated(o, "Stack next-to-top must be of type int[] but is '" + stack().peek(1) + "'."); } - final Type t = ((ArrayType) stack().peek(1)).getBasicType(); + final Type t = ((ArrayType) stack().peek(1)).getElementType(); if (t != Type.INT) { constraintViolated(o, "Stack next-to-top must be of type int[] but is '" + stack().peek(1) + "'."); } @@ -1233,7 +1233,7 @@ public void visitIASTORE(final IASTORE o) { if (!(stack().peek(2) instanceof ArrayType)) { constraintViolated(o, "Stack next-to-next-to-top must be of type int[] but is '" + stack().peek(2) + "'."); } - final Type t = ((ArrayType) stack().peek(2)).getBasicType(); + final Type t = ((ArrayType) stack().peek(2)).getElementType(); if (t != Type.INT) { constraintViolated(o, "Stack next-to-next-to-top must be of type int[] but is '" + stack().peek(2) + "'."); } @@ -1970,7 +1970,7 @@ public void visitLALOAD(final LALOAD o) { if (!(stack().peek(1) instanceof ArrayType)) { constraintViolated(o, "Stack next-to-top must be of type long[] but is '" + stack().peek(1) + "'."); } - final Type t = ((ArrayType) stack().peek(1)).getBasicType(); + final Type t = ((ArrayType) stack().peek(1)).getElementType(); if (t != Type.LONG) { constraintViolated(o, "Stack next-to-top must be of type long[] but is '" + stack().peek(1) + "'."); } @@ -2004,7 +2004,7 @@ public void visitLASTORE(final LASTORE o) { if (!(stack().peek(2) instanceof ArrayType)) { constraintViolated(o, "Stack next-to-next-to-top must be of type long[] but is '" + stack().peek(2) + "'."); } - final Type t = ((ArrayType) stack().peek(2)).getBasicType(); + final Type t = ((ArrayType) stack().peek(2)).getElementType(); if (t != Type.LONG) { constraintViolated(o, "Stack next-to-next-to-top must be of type long[] but is '" + stack().peek(2) + "'."); } @@ -2552,7 +2552,7 @@ public void visitSALOAD(final SALOAD o) { if (!(stack().peek(1) instanceof ArrayType)) { constraintViolated(o, "Stack next-to-top must be of type short[] but is '" + stack().peek(1) + "'."); } - final Type t = ((ArrayType) stack().peek(1)).getBasicType(); + final Type t = ((ArrayType) stack().peek(1)).getElementType(); if (t != Type.SHORT) { constraintViolated(o, "Stack next-to-top must be of type short[] but is '" + stack().peek(1) + "'."); } @@ -2573,7 +2573,7 @@ public void visitSASTORE(final SASTORE o) { if (!(stack().peek(2) instanceof ArrayType)) { constraintViolated(o, "Stack next-to-next-to-top must be of type short[] but is '" + stack().peek(2) + "'."); } - final Type t = ((ArrayType) stack().peek(2)).getBasicType(); + final Type t = ((ArrayType) stack().peek(2)).getElementType(); if (t != Type.SHORT) { constraintViolated(o, "Stack next-to-next-to-top must be of type short[] but is '" + stack().peek(2) + "'."); } diff --git a/src/test/java/org/apache/bcel/verifier/VerifierArrayAccessTest.java b/src/test/java/org/apache/bcel/verifier/VerifierArrayAccessTest.java index d44bc9d9e1..3f96b04e6f 100644 --- a/src/test/java/org/apache/bcel/verifier/VerifierArrayAccessTest.java +++ b/src/test/java/org/apache/bcel/verifier/VerifierArrayAccessTest.java @@ -31,6 +31,7 @@ import org.apache.bcel.verifier.tests.TestArrayAccess04LongCreator; import org.apache.bcel.verifier.tests.TestArrayAccess04ShortCreator; import org.apache.bcel.verifier.tests.TestArrayAccess04UnknownCreator; +import org.apache.bcel.verifier.tests.TestArrayAccess05Creator; import org.junit.jupiter.api.Test; class VerifierArrayAccessTest extends AbstractVerifierTest { @@ -51,6 +52,8 @@ void testInvalidArrayAccess() throws IOException, ClassNotFoundException { assertVerifyRejected("TestArrayAccess04Short", "Verification of an arraystore instruction of a short on an array of references must fail."); final TestArrayAccess04UnknownCreator testArrayAccess04UnknownCreator = new TestArrayAccess04UnknownCreator(); assertThrowsExactly(IllegalArgumentException.class, testArrayAccess04UnknownCreator::create, "Invalid type "); + new TestArrayAccess05Creator().create(); + assertVerifyRejected("TestArrayAccess05", "Verification of iaload applied to a multidimensional int[][] must fail."); } @Test diff --git a/src/test/java/org/apache/bcel/verifier/tests/TestArrayAccess05Creator.java b/src/test/java/org/apache/bcel/verifier/tests/TestArrayAccess05Creator.java new file mode 100644 index 0000000000..bd01b83f04 --- /dev/null +++ b/src/test/java/org/apache/bcel/verifier/tests/TestArrayAccess05Creator.java @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.bcel.verifier.tests; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.io.IOException; +import java.io.OutputStream; + +import org.apache.bcel.Const; +import org.apache.bcel.generic.ClassGen; +import org.apache.bcel.generic.ConstantPoolGen; +import org.apache.bcel.generic.InstructionConst; +import org.apache.bcel.generic.InstructionFactory; +import org.apache.bcel.generic.InstructionHandle; +import org.apache.bcel.generic.InstructionList; +import org.apache.bcel.generic.MethodGen; +import org.apache.bcel.generic.PUSH; +import org.apache.bcel.generic.Type; + +/** + * Emits a method that applies {@code iaload} directly to an {@code int[][]} reference, that is, without first indexing + * into the outer dimension. The component type of {@code int[][]} is {@code int[]} (a reference), so {@code iaload} is + * illegal and structural verification must reject it. + */ +public class TestArrayAccess05Creator extends TestCreator { + private final InstructionFactory factory; + private final ConstantPoolGen cp; + private final ClassGen cg; + + public TestArrayAccess05Creator() { + cg = new ClassGen(TEST_PACKAGE + ".TestArrayAccess05", "java.lang.Object", "TestArrayAccess05.java", Const.ACC_PUBLIC | Const.ACC_SUPER, + new String[] {}); + cp = cg.getConstantPool(); + factory = new InstructionFactory(cg, cp); + } + + @Override + public void create(final OutputStream out) throws IOException { + createMethod_0(); + createMethod_1(); + cg.getJavaClass().dump(out); + } + + private void createMethod_0() { + final InstructionList il = new InstructionList(); + final MethodGen method = new MethodGen(Const.ACC_PUBLIC, Type.VOID, Type.NO_ARGS, new String[] {}, "", TEST_PACKAGE + ".TestArrayAccess05", il, cp); + il.append(InstructionFactory.createLoad(Type.OBJECT, 0)); + il.append(factory.createInvoke("java.lang.Object", "", Type.VOID, Type.NO_ARGS, Const.INVOKESPECIAL)); + il.append(InstructionFactory.createReturn(Type.VOID)); + method.setMaxStack(); + method.setMaxLocals(); + cg.addMethod(method.getMethod()); + il.dispose(); + } + + private void createMethod_1() { + final InstructionList il = new InstructionList(); + final MethodGen method = new MethodGen(Const.ACC_PUBLIC | Const.ACC_STATIC, Type.VOID, Type.NO_ARGS, new String[] {}, "test", + TEST_PACKAGE + ".TestArrayAccess05", il, cp); + + il.append(new PUSH(cp, 1)); + il.append(new PUSH(cp, 1)); + il.append(factory.createNewArray(Type.INT, (short) 2)); // int[][] + il.append(new PUSH(cp, 0)); + il.append(InstructionConst.IALOAD); // iaload on int[][] is illegal + il.append(InstructionConst.POP); + final InstructionHandle ihReturn = il.append(InstructionFactory.createReturn(Type.VOID)); + assertNotNull(ihReturn); + method.setMaxStack(); + method.setMaxLocals(); + cg.addMethod(method.getMethod()); + il.dispose(); + } +}