This is an automated email from the ASF dual-hosted git repository.

garydgregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-bcel.git


The following commit(s) were added to refs/heads/master by this push:
     new b1621d0a Add negative verifier tests for array load/store component 
type checks (#524)
b1621d0a is described below

commit b1621d0a47112d099b699ecbf6d67124bdd3dac0
Author: Naveed Khan <[email protected]>
AuthorDate: Wed Jul 15 21:06:00 2026 +0000

    Add negative verifier tests for array load/store component type checks 
(#524)
    
    TestArrayAccess05 and TestArrayAccess06 only cover iaload and caload,
    so the component type checks for the remaining array load/store
    instructions in InstConstraintVisitor have no negative tests. Add a
    parameterized TestArrayAccess07Creator that applies a given array
    access instruction to an array whose component type does not match
    (castore on an int[], iastore on a char[], and so on) and assert that
    structural verification rejects each of aaload, aastore, baload,
    bastore, castore, daload, dastore, faload, fastore, iastore, laload,
    lastore, saload and sastore. Make TestCreator.getClassName protected
    so the creator can name the generated class per instruction instead
    of adding fourteen creator subclasses. Follow-up to #522.
---
 .../bcel/verifier/VerifierArrayAccessTest.java     |  43 +++++++
 .../verifier/tests/TestArrayAccess07Creator.java   | 129 +++++++++++++++++++++
 .../apache/bcel/verifier/tests/TestCreator.java    |   2 +-
 3 files changed, 173 insertions(+), 1 deletion(-)

diff --git 
a/src/test/java/org/apache/bcel/verifier/VerifierArrayAccessTest.java 
b/src/test/java/org/apache/bcel/verifier/VerifierArrayAccessTest.java
index 0cebe131..227ef29b 100644
--- a/src/test/java/org/apache/bcel/verifier/VerifierArrayAccessTest.java
+++ b/src/test/java/org/apache/bcel/verifier/VerifierArrayAccessTest.java
@@ -22,7 +22,11 @@ package org.apache.bcel.verifier;
 import static org.junit.jupiter.api.Assertions.assertThrowsExactly;
 
 import java.io.IOException;
+import java.util.stream.Stream;
 
+import org.apache.bcel.generic.ArrayInstruction;
+import org.apache.bcel.generic.InstructionConst;
+import org.apache.bcel.generic.Type;
 import org.apache.bcel.verifier.tests.TestArrayAccess02Creator;
 import org.apache.bcel.verifier.tests.TestArrayAccess03Creator;
 import org.apache.bcel.verifier.tests.TestArrayAccess04DoubleCreator;
@@ -33,10 +37,49 @@ import 
org.apache.bcel.verifier.tests.TestArrayAccess04ShortCreator;
 import org.apache.bcel.verifier.tests.TestArrayAccess04UnknownCreator;
 import org.apache.bcel.verifier.tests.TestArrayAccess05Creator;
 import org.apache.bcel.verifier.tests.TestArrayAccess06Creator;
+import org.apache.bcel.verifier.tests.TestArrayAccess07Creator;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
 
 class VerifierArrayAccessTest extends AbstractVerifierTest {
 
+    /**
+     * Array load and store instructions applied to an array whose component 
type does not match the instruction.
+     * {@code caload} and {@code iaload} have their own tests, {@link 
org.apache.bcel.verifier.tests.TestArrayAccess06Creator} and
+     * {@link org.apache.bcel.verifier.tests.TestArrayAccess05Creator}.
+     */
+    static Stream<Arguments> invalidComponentTypeArrayAccess() {
+        return Stream.of(
+        // @formatter:off
+            Arguments.of(InstructionConst.AALOAD, Type.INT),
+            Arguments.of(InstructionConst.AASTORE, Type.INT),
+            Arguments.of(InstructionConst.BALOAD, Type.INT),
+            Arguments.of(InstructionConst.BASTORE, Type.INT),
+            Arguments.of(InstructionConst.CASTORE, Type.INT),
+            Arguments.of(InstructionConst.DALOAD, Type.INT),
+            Arguments.of(InstructionConst.DASTORE, Type.INT),
+            Arguments.of(InstructionConst.FALOAD, Type.INT),
+            Arguments.of(InstructionConst.FASTORE, Type.INT),
+            Arguments.of(InstructionConst.IASTORE, Type.CHAR),
+            Arguments.of(InstructionConst.LALOAD, Type.INT),
+            Arguments.of(InstructionConst.LASTORE, Type.INT),
+            Arguments.of(InstructionConst.SALOAD, Type.INT),
+            Arguments.of(InstructionConst.SASTORE, Type.INT));
+        // @formatter:on
+    }
+
+    @ParameterizedTest
+    @MethodSource("invalidComponentTypeArrayAccess")
+    void testInvalidComponentTypeArrayAccess(final ArrayInstruction 
arrayInstruction, final Type arrayElementType)
+        throws IOException, ClassNotFoundException {
+        final TestArrayAccess07Creator creator = new 
TestArrayAccess07Creator(arrayInstruction, arrayElementType);
+        creator.create();
+        assertVerifyRejected(creator.getSimpleClassName(),
+            "Verification of " + arrayInstruction.getName() + " applied to a " 
+ arrayElementType + "[] must fail.");
+    }
+
     @Test
     void testInvalidArrayAccess() throws IOException, ClassNotFoundException {
         new TestArrayAccess03Creator().create();
diff --git 
a/src/test/java/org/apache/bcel/verifier/tests/TestArrayAccess07Creator.java 
b/src/test/java/org/apache/bcel/verifier/tests/TestArrayAccess07Creator.java
new file mode 100644
index 00000000..da845b57
--- /dev/null
+++ b/src/test/java/org/apache/bcel/verifier/tests/TestArrayAccess07Creator.java
@@ -0,0 +1,129 @@
+/*
+ * 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 java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.bcel.Const;
+import org.apache.bcel.classfile.JavaClass;
+import org.apache.bcel.generic.ArrayInstruction;
+import org.apache.bcel.generic.ClassGen;
+import org.apache.bcel.generic.ConstantPoolGen;
+import org.apache.bcel.generic.Instruction;
+import org.apache.bcel.generic.InstructionConst;
+import org.apache.bcel.generic.InstructionFactory;
+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 the given array load or store instruction to an 
array whose component type does not
+ * match the instruction, for example {@code castore} on an {@code int[]} or 
{@code laload} on an {@code int[]}.
+ * Structural verification must reject every such access.
+ */
+public class TestArrayAccess07Creator extends TestCreator {
+    private final InstructionFactory factory;
+    private final ConstantPoolGen cp;
+    private final ClassGen cg;
+    private final ArrayInstruction arrayInstruction;
+    private final Type arrayElementType;
+    private final String simpleClassName;
+
+    public TestArrayAccess07Creator(final ArrayInstruction arrayInstruction, 
final Type arrayElementType) {
+        this.arrayInstruction = arrayInstruction;
+        this.arrayElementType = arrayElementType;
+        final String name = arrayInstruction.getName();
+        simpleClassName = "TestArrayAccess07" + 
Character.toUpperCase(name.charAt(0)) + name.substring(1);
+        cg = new ClassGen(TEST_PACKAGE + "." + simpleClassName, 
"java.lang.Object", simpleClassName + ".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[] {}, "<init>", TEST_PACKAGE + "." + simpleClassName, 
il,
+            cp);
+        il.append(InstructionFactory.createLoad(Type.OBJECT, 0));
+        il.append(factory.createInvoke("java.lang.Object", "<init>", 
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 + "." + simpleClassName, il, cp);
+
+        il.append(new PUSH(cp, 1));
+        il.append(factory.createNewArray(arrayElementType, (short) 1));
+        il.append(new PUSH(cp, 0));
+        if (arrayInstruction.produceStack(cp) == 0) {
+            // store: push a value of the type the instruction stores, then 
access the mismatched array
+            il.append(storedValue());
+            il.append(arrayInstruction);
+        } else {
+            // load: access the mismatched array, then pop the loaded value
+            il.append(arrayInstruction);
+            il.append(arrayInstruction.produceStack(cp) == 2 ? 
InstructionConst.POP2 : InstructionConst.POP);
+        }
+        il.append(InstructionFactory.createReturn(Type.VOID));
+        method.setMaxStack();
+        method.setMaxLocals();
+        cg.addMethod(method.getMethod());
+        il.dispose();
+    }
+
+    @Override
+    protected String getClassName() {
+        return simpleClassName + JavaClass.EXTENSION;
+    }
+
+    public String getSimpleClassName() {
+        return simpleClassName;
+    }
+
+    private Instruction storedValue() {
+        switch (arrayInstruction.getOpcode()) {
+        case Const.AASTORE:
+            return InstructionConst.ACONST_NULL;
+        case Const.DASTORE:
+            return InstructionConst.DCONST_1;
+        case Const.FASTORE:
+            return InstructionConst.FCONST_1;
+        case Const.LASTORE:
+            return InstructionConst.LCONST_1;
+        default:
+            return InstructionConst.ICONST_1;
+        }
+    }
+}
diff --git a/src/test/java/org/apache/bcel/verifier/tests/TestCreator.java 
b/src/test/java/org/apache/bcel/verifier/tests/TestCreator.java
index 2edbad82..e69a03a4 100644
--- a/src/test/java/org/apache/bcel/verifier/tests/TestCreator.java
+++ b/src/test/java/org/apache/bcel/verifier/tests/TestCreator.java
@@ -49,7 +49,7 @@ public abstract class TestCreator {
         }
     }
 
-    private String getClassName() {
+    protected String getClassName() {
         final String name = getClass().getName();
         return name.substring(name.lastIndexOf('.') + 1).replace("Creator", 
JavaClass.EXTENSION);
     }

Reply via email to