From e760171da8825c315869e16edc01c4e450aebb0a Mon Sep 17 00:00:00 2001 From: Matias Saavedra Silva Date: Tue, 10 Mar 2026 20:57:49 +0000 Subject: [PATCH] 8376521: Verifier: disallow acmp & ifnull on 'uninitialized' types 8375483: Should set flagThisUninit whenever uninitializedThis is on the stack Reviewed-by: liach, coleenp --- src/hotspot/share/classfile/verifier.cpp | 4 +- .../classfile/impl/verifier/VerifierImpl.java | 8 +-- .../share/native/libverify/check_code.c | 5 +- .../runtime/verifier/UninitThisAcmp.jasm | 43 +++++++++++ .../runtime/verifier/UninitThisAcmpOld.jasm | 41 +++++++++++ .../runtime/verifier/UninitThisIfNull.jasm | 42 +++++++++++ .../runtime/verifier/UninitThisIfNullOld.jasm | 40 +++++++++++ .../runtime/verifier/UninitializedAcmp.jasm | 44 ++++++++++++ .../verifier/UninitializedAcmpOld.jasm | 41 +++++++++++ .../runtime/verifier/UninitializedIfNull.jasm | 42 +++++++++++ .../verifier/UninitializedIfNullOld.jasm | 39 ++++++++++ .../UninitializedThisVerificationTest.java | 58 +++++++++++++++ test/jdk/jdk/classfile/VerifierSelfTest.java | 72 ++++++++++++++++++- 13 files changed, 468 insertions(+), 11 deletions(-) create mode 100644 test/hotspot/jtreg/runtime/verifier/UninitThisAcmp.jasm create mode 100644 test/hotspot/jtreg/runtime/verifier/UninitThisAcmpOld.jasm create mode 100644 test/hotspot/jtreg/runtime/verifier/UninitThisIfNull.jasm create mode 100644 test/hotspot/jtreg/runtime/verifier/UninitThisIfNullOld.jasm create mode 100644 test/hotspot/jtreg/runtime/verifier/UninitializedAcmp.jasm create mode 100644 test/hotspot/jtreg/runtime/verifier/UninitializedAcmpOld.jasm create mode 100644 test/hotspot/jtreg/runtime/verifier/UninitializedIfNull.jasm create mode 100644 test/hotspot/jtreg/runtime/verifier/UninitializedIfNullOld.jasm create mode 100644 test/hotspot/jtreg/runtime/verifier/UninitializedThisVerificationTest.java diff --git a/src/hotspot/share/classfile/verifier.cpp b/src/hotspot/share/classfile/verifier.cpp index 76d09161fdd..48be24c20dc 100644 --- a/src/hotspot/share/classfile/verifier.cpp +++ b/src/hotspot/share/classfile/verifier.cpp @@ -1607,12 +1607,12 @@ void ClassVerifier::verify_method(const methodHandle& m, TRAPS) { case Bytecodes::_if_acmpeq : case Bytecodes::_if_acmpne : current_frame.pop_stack( - VerificationType::reference_check(), CHECK_VERIFY(this)); + object_type(), CHECK_VERIFY(this)); // fall through case Bytecodes::_ifnull : case Bytecodes::_ifnonnull : current_frame.pop_stack( - VerificationType::reference_check(), CHECK_VERIFY(this)); + object_type(), CHECK_VERIFY(this)); stackmap_table.check_jump_target (¤t_frame, bcs.bci(), bcs.get_offset_s2(), CHECK_VERIFY(this)); no_control_flow = false; break; diff --git a/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerifierImpl.java b/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerifierImpl.java index 07406b2ee7f..adc595813ee 100644 --- a/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerifierImpl.java +++ b/src/java.base/share/classes/jdk/internal/classfile/impl/verifier/VerifierImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, 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 @@ -1047,13 +1047,11 @@ public final class VerifierImpl { no_control_flow = false; break; case IF_ACMPEQ : case IF_ACMPNE : - current_frame.pop_stack( - VerificationType.reference_check); + current_frame.pop_stack(object_type()); // fall through case IFNULL : case IFNONNULL : - current_frame.pop_stack( - VerificationType.reference_check); + current_frame.pop_stack(object_type()); target = bcs.dest(); stackmap_table.check_jump_target (current_frame, target); diff --git a/src/java.base/share/native/libverify/check_code.c b/src/java.base/share/native/libverify/check_code.c index 5a8f50cd0a0..4830fedb97b 100644 --- a/src/java.base/share/native/libverify/check_code.c +++ b/src/java.base/share/native/libverify/check_code.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2026, 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 @@ -2162,8 +2162,7 @@ pop_stack(context_type *context, unsigned int inumber, stack_info_type *new_stac break; if ( (GET_ITEM_TYPE(top_type) == ITEM_NewObject || (GET_ITEM_TYPE(top_type) == ITEM_InitObject)) - && ((opcode == JVM_OPC_astore) || (opcode == JVM_OPC_aload) - || (opcode == JVM_OPC_ifnull) || (opcode == JVM_OPC_ifnonnull))) + && ((opcode == JVM_OPC_astore) || (opcode == JVM_OPC_aload))) break; /* The 2nd edition VM of the specification allows field * initializations before the superclass initializer, diff --git a/test/hotspot/jtreg/runtime/verifier/UninitThisAcmp.jasm b/test/hotspot/jtreg/runtime/verifier/UninitThisAcmp.jasm new file mode 100644 index 00000000000..c1729a78653 --- /dev/null +++ b/test/hotspot/jtreg/runtime/verifier/UninitThisAcmp.jasm @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2025, 2026, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +class UninitThisAcmp version 69:0 +{ + public Method "":"()V" + stack 2 locals 2 + { + new class java/lang/Object; + dup; + invokespecial Method java/lang/Object."":"()V"; + astore_1; + aload_0; + aload_1; + if_acmpne L14; + nop; + L14: stack_frame_type append; + locals_map class java/lang/Object; + aload_0; + invokespecial Method java/lang/Object."":"()V"; + return; + } +} // end Class UninitThisAcmp diff --git a/test/hotspot/jtreg/runtime/verifier/UninitThisAcmpOld.jasm b/test/hotspot/jtreg/runtime/verifier/UninitThisAcmpOld.jasm new file mode 100644 index 00000000000..3bcc1e09b67 --- /dev/null +++ b/test/hotspot/jtreg/runtime/verifier/UninitThisAcmpOld.jasm @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2025, 2026, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +class UninitThisAcmpOld version 49:0 +{ + public Method "":"()V" + stack 2 locals 2 + { + new class java/lang/Object; + dup; + invokespecial Method java/lang/Object."":"()V"; + astore_1; + aload_0; + aload_1; + if_acmpne L14; + nop; + L14: aload_0; + invokespecial Method java/lang/Object."":"()V"; + return; + } +} // end Class UninitThisAcmpOld diff --git a/test/hotspot/jtreg/runtime/verifier/UninitThisIfNull.jasm b/test/hotspot/jtreg/runtime/verifier/UninitThisIfNull.jasm new file mode 100644 index 00000000000..57a82a9f1e4 --- /dev/null +++ b/test/hotspot/jtreg/runtime/verifier/UninitThisIfNull.jasm @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2025, 2026, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +class UninitThisIfNull version 69:0 +{ + public Method "":"()V" + stack 2 locals 2 + { + new class java/lang/Object; + dup; + invokespecial Method java/lang/Object."":"()V"; + astore_1; + aload_0; + ifnonnull L14; + nop; + L14: stack_frame_type append; + locals_map class java/lang/Object; + aload_0; + invokespecial Method java/lang/Object."":"()V"; + return; + } +} // end Class UninitThisIfNull diff --git a/test/hotspot/jtreg/runtime/verifier/UninitThisIfNullOld.jasm b/test/hotspot/jtreg/runtime/verifier/UninitThisIfNullOld.jasm new file mode 100644 index 00000000000..fbe96c2eef0 --- /dev/null +++ b/test/hotspot/jtreg/runtime/verifier/UninitThisIfNullOld.jasm @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2025, 2026, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +class UninitThisIfNullOld version 49:0 +{ + public Method "":"()V" + stack 2 locals 2 + { + new class java/lang/Object; + dup; + invokespecial Method java/lang/Object."":"()V"; + astore_1; + aload_0; + ifnonnull L14; + nop; + L14: aload_0; + invokespecial Method java/lang/Object."":"()V"; + return; + } +} // end Class UninitThisIfNullOld diff --git a/test/hotspot/jtreg/runtime/verifier/UninitializedAcmp.jasm b/test/hotspot/jtreg/runtime/verifier/UninitializedAcmp.jasm new file mode 100644 index 00000000000..b8ce17cb16b --- /dev/null +++ b/test/hotspot/jtreg/runtime/verifier/UninitializedAcmp.jasm @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2025, 2026, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +class UninitializedAcmp version 69:0 +{ + Method "":"()V" + stack 5 locals 1 + { + aload_0; + invokespecial Method java/lang/Object."":"()V"; + aload_0; + L1: new class java/lang/Object; + dup; + dup; + dup; + if_acmpne L18; + nop; + L18: stack_frame_type full; + locals_map class UninitializedAcmp; + stack_map class UninitializedAcmp, at L1, at L1; + invokespecial Method java/lang/Object."":"()V"; + return; + } +} // end Class UninitializedAcmp diff --git a/test/hotspot/jtreg/runtime/verifier/UninitializedAcmpOld.jasm b/test/hotspot/jtreg/runtime/verifier/UninitializedAcmpOld.jasm new file mode 100644 index 00000000000..d16cc5c6779 --- /dev/null +++ b/test/hotspot/jtreg/runtime/verifier/UninitializedAcmpOld.jasm @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2025, 2026, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +class UninitializedAcmpOld version 49:0 +{ + Method "":"()V" + stack 5 locals 1 + { + aload_0; + invokespecial Method java/lang/Object."":"()V"; + aload_0; + L1: new class java/lang/Object; + dup; + dup; + dup; + if_acmpne L18; + nop; + L18: invokespecial Method java/lang/Object."":"()V"; + return; + } +} // end Class UninitializedAcmpOld diff --git a/test/hotspot/jtreg/runtime/verifier/UninitializedIfNull.jasm b/test/hotspot/jtreg/runtime/verifier/UninitializedIfNull.jasm new file mode 100644 index 00000000000..4e7251f073d --- /dev/null +++ b/test/hotspot/jtreg/runtime/verifier/UninitializedIfNull.jasm @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2025, 2026, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +class UninitializedIfNull version 69:0 +{ + Method "":"()V" + stack 3 locals 1 + { + aload_0; + invokespecial Method java/lang/Object."":"()V"; + L1: new class java/lang/Object; + dup; + dup; + ifnonnull L18; + nop; + L18: stack_frame_type full; + locals_map class UninitializedIfNull; + stack_map at L1, at L1; + invokespecial Method java/lang/Object."":"()V"; + return; + } +} // end Class UninitializedIfNull diff --git a/test/hotspot/jtreg/runtime/verifier/UninitializedIfNullOld.jasm b/test/hotspot/jtreg/runtime/verifier/UninitializedIfNullOld.jasm new file mode 100644 index 00000000000..5bffba887a8 --- /dev/null +++ b/test/hotspot/jtreg/runtime/verifier/UninitializedIfNullOld.jasm @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2025, 2026, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +class UninitializedIfNullOld version 49:0 +{ + Method "":"()V" + stack 3 locals 1 + { + aload_0; + invokespecial Method java/lang/Object."":"()V"; + L1: new class java/lang/Object; + dup; + dup; + ifnonnull L18; + nop; + L18: invokespecial Method java/lang/Object."":"()V"; + return; + } +} // end Class UninitializedIfNullOld diff --git a/test/hotspot/jtreg/runtime/verifier/UninitializedThisVerificationTest.java b/test/hotspot/jtreg/runtime/verifier/UninitializedThisVerificationTest.java new file mode 100644 index 00000000000..075e9f2058e --- /dev/null +++ b/test/hotspot/jtreg/runtime/verifier/UninitializedThisVerificationTest.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2025, 2026, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8366743 + * @summary Test spec rules for uninitialized + * @compile UninitThisAcmp.jasm UninitThisIfNull.jasm + * UninitializedIfNull.jasm UninitializedAcmp.jasm + * UninitThisAcmpOld.jasm UninitThisIfNullOld.jasm + * UninitializedAcmpOld.jasm UninitializedIfNullOld.jasm + * @run main/othervm -Xlog:verification UninitializedThisVerificationTest + */ + +public class UninitializedThisVerificationTest { + + public static void main(String[] args) throws Exception { + String[] testNames = { "UninitThisAcmp", "UninitThisIfNull", + "UninitializedAcmp", "UninitializedIfNull", + "UninitThisAcmpOld", "UninitThisIfNullOld", + "UninitializedAcmpOld", "UninitializedIfNullOld" }; + int fails = 0; + for (String test : testNames) { + System.out.println("Testing " + test); + try { + Class c = Class.forName(test); + System.out.println("Failed"); + fails++; + } catch (java.lang.VerifyError e) { + System.out.println("Passed"); + } + } + + if (fails > 0) { + throw new RuntimeException("Failed: Expected VerifyError in " + fails + " tests"); + } + } +} diff --git a/test/jdk/jdk/classfile/VerifierSelfTest.java b/test/jdk/jdk/classfile/VerifierSelfTest.java index 7d1dae6f519..12458b1586e 100644 --- a/test/jdk/jdk/classfile/VerifierSelfTest.java +++ b/test/jdk/jdk/classfile/VerifierSelfTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, 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 @@ -37,12 +37,14 @@ import static java.lang.constant.ConstantDescs.*; import java.lang.constant.MethodTypeDesc; import java.lang.invoke.MethodHandleInfo; +import java.lang.invoke.MethodHandles; import java.net.URI; import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -59,6 +61,9 @@ import jdk.internal.classfile.impl.BufWriterImpl; import jdk.internal.classfile.impl.DirectClassBuilder; import jdk.internal.classfile.impl.UnboundAttribute; 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; import static org.junit.jupiter.api.Assertions.*; @@ -418,6 +423,71 @@ class VerifierSelfTest { return lst; } + enum ComparisonInstruction { + IF_ACMPEQ(Opcode.IF_ACMPEQ, 2), + IF_ACMPNE(Opcode.IF_ACMPNE, 2), + IFNONNULL(Opcode.IFNONNULL, 1), + IFNULL(Opcode.IFNULL, 1); + final Opcode opcode; + final int argCount; + ComparisonInstruction(Opcode opcode, int argCount) { + this.opcode = opcode; + this.argCount = argCount; + } + } + + enum UninitializeKind { + UNINITIALIZED, UNINITIALIZED_THIS + } + + @ParameterizedTest + @MethodSource("uninitializedInBytecodeClasses") + public void testUninitializedInComparisons(ComparisonInstruction inst, UninitializeKind kind) throws Throwable { + var bytes = ClassFile.of(ClassFile.StackMapsOption.DROP_STACK_MAPS).build(ClassDesc.of("Test"), clb -> clb + .withMethodBody(INIT_NAME, MTD_void, 0, cob -> { + StackMapFrameInfo.VerificationTypeInfo uninitializeInfo; + if (kind == UninitializeKind.UNINITIALIZED) { + uninitializeInfo = StackMapFrameInfo.UninitializedVerificationTypeInfo.of(cob.newBoundLabel()); + cob.new_(CD_Object); + } else { + uninitializeInfo = StackMapFrameInfo.SimpleVerificationTypeInfo.UNINITIALIZED_THIS; + cob.aload(0); + } + + // Stack: uninitializeInfo + for (int i = 0; i < inst.argCount; i++) { + cob.dup(); + } + var dest = cob.newLabel(); + cob.branch(inst.opcode, dest) + .nop() + .labelBinding(dest) + .with(StackMapTableAttribute.of(List.of(StackMapFrameInfo.of(dest, + List.of(StackMapFrameInfo.SimpleVerificationTypeInfo.UNINITIALIZED_THIS), + List.of(uninitializeInfo))))) + .invokespecial(CD_Object, INIT_NAME, MTD_void); + if (kind == UninitializeKind.UNINITIALIZED) { + // still need to call super constructor + cob.aload(0) + .invokespecial(CD_Object, INIT_NAME, MTD_void); + } + cob.return_(); + })); + var errors = ClassFile.of().verify(bytes); + assertNotEquals(List.of(), errors, () -> errors + " : " + ClassFile.of().parse(bytes).toDebugString()); + var lookup = MethodHandles.lookup(); + assertThrows(VerifyError.class, () -> lookup.defineHiddenClass(bytes, true)); // force JVM verification + } + + public static Stream uninitializedInBytecodeClasses() { + return Arrays.stream(ComparisonInstruction.values()) + .mapMulti((inst, sink) -> { + for (var kind : UninitializeKind.values()) { + sink.accept(Arguments.of(inst, kind)); + } + }); + } + @Test // JDK-8350029 void testInvokeSpecialInterfacePatch() { var runClass = ClassDesc.of("Run");