8376521: Verifier: disallow acmp & ifnull on 'uninitialized' types

8375483: Should set flagThisUninit whenever uninitializedThis is on the stack

Reviewed-by: liach, coleenp
This commit is contained in:
Matias Saavedra Silva 2026-03-10 20:57:49 +00:00
parent bf28e03eeb
commit e760171da8
13 changed files with 468 additions and 11 deletions

View File

@ -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
(&current_frame, bcs.bci(), bcs.get_offset_s2(), CHECK_VERIFY(this));
no_control_flow = false; break;

View File

@ -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);

View File

@ -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,

View File

@ -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 "<init>":"()V"
stack 2 locals 2
{
new class java/lang/Object;
dup;
invokespecial Method java/lang/Object."<init>":"()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."<init>":"()V";
return;
}
} // end Class UninitThisAcmp

View File

@ -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 "<init>":"()V"
stack 2 locals 2
{
new class java/lang/Object;
dup;
invokespecial Method java/lang/Object."<init>":"()V";
astore_1;
aload_0;
aload_1;
if_acmpne L14;
nop;
L14: aload_0;
invokespecial Method java/lang/Object."<init>":"()V";
return;
}
} // end Class UninitThisAcmpOld

View File

@ -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 "<init>":"()V"
stack 2 locals 2
{
new class java/lang/Object;
dup;
invokespecial Method java/lang/Object."<init>":"()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."<init>":"()V";
return;
}
} // end Class UninitThisIfNull

View File

@ -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 "<init>":"()V"
stack 2 locals 2
{
new class java/lang/Object;
dup;
invokespecial Method java/lang/Object."<init>":"()V";
astore_1;
aload_0;
ifnonnull L14;
nop;
L14: aload_0;
invokespecial Method java/lang/Object."<init>":"()V";
return;
}
} // end Class UninitThisIfNullOld

View File

@ -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 "<init>":"()V"
stack 5 locals 1
{
aload_0;
invokespecial Method java/lang/Object."<init>":"()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."<init>":"()V";
return;
}
} // end Class UninitializedAcmp

View File

@ -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 "<init>":"()V"
stack 5 locals 1
{
aload_0;
invokespecial Method java/lang/Object."<init>":"()V";
aload_0;
L1: new class java/lang/Object;
dup;
dup;
dup;
if_acmpne L18;
nop;
L18: invokespecial Method java/lang/Object."<init>":"()V";
return;
}
} // end Class UninitializedAcmpOld

View File

@ -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 "<init>":"()V"
stack 3 locals 1
{
aload_0;
invokespecial Method java/lang/Object."<init>":"()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."<init>":"()V";
return;
}
} // end Class UninitializedIfNull

View File

@ -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 "<init>":"()V"
stack 3 locals 1
{
aload_0;
invokespecial Method java/lang/Object."<init>":"()V";
L1: new class java/lang/Object;
dup;
dup;
ifnonnull L18;
nop;
L18: invokespecial Method java/lang/Object."<init>":"()V";
return;
}
} // end Class UninitializedIfNullOld

View File

@ -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");
}
}
}

View File

@ -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<Arguments> 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");