8385114: Prevent creation of invalid TableSwitchInstruction

Reviewed-by: asotona
This commit is contained in:
Chen Liang 2026-05-22 21:22:53 +00:00
parent 31596cfef9
commit 05d925ea56
6 changed files with 56 additions and 10 deletions

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
@ -3429,6 +3429,9 @@ public sealed interface CodeBuilder
* @param defaultTarget the default jump target
* @param cases the switch cases
* @return this builder
* @throws IllegalArgumentException if the low value is greater than the
* high value, or if there are too many targets between the low
* and high values
* @see Opcode#TABLESWITCH
* @see TableSwitchInstruction
*/
@ -3443,6 +3446,7 @@ public sealed interface CodeBuilder
* @param defaultTarget the default jump target
* @param cases the switch cases
* @return this builder
* @throws IllegalArgumentException if {@code cases} is empty
* @see Opcode#TABLESWITCH
* @see #tableswitch(int, int, Label, List)
* @see TableSwitchInstruction

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, 2024, 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
@ -95,6 +95,9 @@ public sealed interface TableSwitchInstruction extends Instruction
* @param defaultTarget the default target of the switch
* @param cases the cases of the switch; duplicate or out of bound case
* handling is not specified
* @throws IllegalArgumentException if the low value is greater than the
* high value, or if there are too many targets between the low
* and high values
*/
static TableSwitchInstruction of(int lowValue, int highValue, Label defaultTarget, List<SwitchCase> cases) {
return new AbstractInstruction.UnboundTableSwitchInstruction(lowValue, highValue, defaultTarget, cases);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, 2024, 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
@ -289,9 +289,7 @@ public abstract sealed class AbstractInstruction
afterPad = code.codeStart + RawBytecodeHelper.align(pos + 1 - code.codeStart);
low = code.classReader.readInt(afterPad + 4);
high = code.classReader.readInt(afterPad + 8);
if (high < low || (long)high - low > code.codeLength >> 2) {
throw new IllegalArgumentException("Invalid tableswitch values low: " + low + " high: " + high);
}
BytecodeHelpers.validateTableSwitchValues(low, high, code.codeLength);
int cnt = high - low + 1;
size = afterPad + 12 + cnt * 4 - pos;
}
@ -926,6 +924,7 @@ public abstract sealed class AbstractInstruction
public UnboundTableSwitchInstruction(int lowValue, int highValue, Label defaultTarget, List<SwitchCase> cases) {
super(Opcode.TABLESWITCH);
BytecodeHelpers.validateTableSwitchValues(lowValue, highValue);
this.lowValue = lowValue;
this.highValue = highValue;
this.defaultTarget = requireNonNull(defaultTarget);

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.
* Copyright (c) 2024, Alibaba Group Holding Limited. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@ -484,6 +484,16 @@ public class BytecodeHelpers {
.concat(Long.toString(value)));
}
public static void validateTableSwitchValues(int low, int high) {
validateTableSwitchValues(low, high, 0xFFFF);
}
public static void validateTableSwitchValues(int low, int high, int codeLength) {
if (high < low || 1L + high - low > codeLength >> 2) {
throw new IllegalArgumentException("Invalid tableswitch values low: " + low + " high: " + high);
}
}
public static MethodHandleEntry handleDescToHandleInfo(ConstantPoolBuilder constantPool, DirectMethodHandleDesc bootstrapMethod) {
ClassEntry bsOwner = constantPool.classEntry(bootstrapMethod.owner());
NameAndTypeEntry bsNameAndType = constantPool.nameAndTypeEntry(constantPool.utf8Entry(bootstrapMethod.methodName()),

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.
* Copyright (c) 2024, Alibaba Group Holding Limited. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@ -1859,6 +1859,7 @@ public final class DirectCodeBuilder
@Override
public CodeBuilder tableswitch(int low, int high, Label defaultTarget, List<SwitchCase> cases) {
BytecodeHelpers.validateTableSwitchValues(low, high);
Objects.requireNonNull(defaultTarget);
// check cases when we write them
writeTableSwitch(low, high, defaultTarget, cases);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, 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
@ -23,7 +23,7 @@
/*
* @test
* @bug 8341277 8361102 8361182 8361614
* @bug 8341277 8361102 8361182 8361614 8385114
* @summary Testing ClassFile (pseudo-)instruction argument validation.
* @run junit InstructionValidationTest
*/
@ -94,15 +94,44 @@ class InstructionValidationTest {
@Test
void testSwitch() {
TestUtil.runCodeHandler(cob -> {
// Null labels/cases
assertThrows(NullPointerException.class, () -> cob.tableswitch(null, List.of(SwitchCase.of(1, cob.startLabel()))));
assertThrows(NullPointerException.class, () -> cob.tableswitch(cob.startLabel(), null));
assertThrows(NullPointerException.class, () -> cob.tableswitch(cob.startLabel(), Collections.singletonList(null)));
assertThrows(NullPointerException.class, () -> cob.tableswitch(-1, 1, cob.startLabel(), null));
assertThrows(NullPointerException.class, () -> cob.lookupswitch(cob.startLabel(), null));
assertThrows(NullPointerException.class, () -> cob.tableswitch(-1, 1, cob.startLabel(), Collections.singletonList(null)));
assertThrows(NullPointerException.class, () -> cob.lookupswitch(cob.startLabel(), Collections.singletonList(null)));
assertThrows(NullPointerException.class, () -> cob.tableswitch(-1, 1, null, List.of()));
assertThrows(NullPointerException.class, () -> cob.lookupswitch(null, List.of()));
// Illegal start/end
assertThrows(IllegalArgumentException.class, () -> cob.tableswitch(cob.startLabel(), List.of()));
assertThrows(IllegalArgumentException.class, () -> cob.tableswitch(1, -1, cob.startLabel(), List.of()));
assertThrows(IllegalArgumentException.class, () -> cob.tableswitch(Integer.MAX_VALUE, Integer.MIN_VALUE, cob.startLabel(), List.of()));
assertThrows(IllegalArgumentException.class, () -> cob.tableswitch(1, 16384, cob.startLabel(), List.of()));
// Ensures nothing redundant is written in case of failure
cob.return_();
});
Label[] capture = new Label[1];
ClassFile.of().build(CD_Object, clb -> clb.withMethodBody("test", MTD_void, 0, cob -> {
capture[0] = cob.startLabel();
cob.return_();
}));
Label dummyLabel = capture[0];
assertNotNull(dummyLabel);
TableSwitchInstruction.of(0, 0, dummyLabel, List.of());
LookupSwitchInstruction.of(dummyLabel, List.of());
assertThrows(NullPointerException.class, () -> TableSwitchInstruction.of(0, 0, dummyLabel, null));
assertThrows(NullPointerException.class, () -> LookupSwitchInstruction.of(dummyLabel, null));
assertThrows(NullPointerException.class, () -> TableSwitchInstruction.of(0, 0, dummyLabel, Collections.singletonList(null)));
assertThrows(NullPointerException.class, () -> LookupSwitchInstruction.of(dummyLabel, Collections.singletonList(null)));
assertThrows(NullPointerException.class, () -> TableSwitchInstruction.of(0, 0, null, List.of()));
assertThrows(NullPointerException.class, () -> LookupSwitchInstruction.of(null, List.of()));
assertThrows(IllegalArgumentException.class, () -> TableSwitchInstruction.of(1, -1, dummyLabel, List.of()));
assertThrows(IllegalArgumentException.class, () -> TableSwitchInstruction.of(Integer.MAX_VALUE, Integer.MIN_VALUE, dummyLabel, List.of()));
assertThrows(IllegalArgumentException.class, () -> TableSwitchInstruction.of(1, 16384, dummyLabel, List.of()));
}
@Test