Message ID | 20240701115411.15574-1-hprajapati@mvista.com |
---|---|
State | New |
Headers | show |
Series | [meta-java,master] openjdk-8: Fix multiple CVEs | expand |
Hi Team, Any Update on this issue ? Regards, Hitendra On 01/07/24 5:24 pm, Hitendra Prajapati wrote: > Backport fixes for: > > * CVE-2022-40433 - Upstream-Status: Backport fromhttps://github.com/openjdk/jdk8u/commit/961ab463974b7d05600b826303f9111c4f367a04 > * CVE-2024-20919 - Upstream-Status: Backport fromhttps://github.com/openjdk/jdk8u/commit/77d38b78e4993381ddb113d012b30ab2d7cd9215 > * CVE-2024-20921 - Upstream-Status: Backport fromhttps://github.com/openjdk/jdk8u/commit/f7cb28de01117f598ecf48ad58e277c3a4590436 > > Signed-off-by: Hitendra Prajapati<hprajapati@mvista.com> > --- > .../openjdk/openjdk-8-release-common.inc | 3 + > .../patches-openjdk-8/CVE-2022-40433.patch | 233 +++++++ > .../patches-openjdk-8/CVE-2024-20919.patch | 126 ++++ > .../patches-openjdk-8/CVE-2024-20921.patch | 657 ++++++++++++++++++ > 4 files changed, 1019 insertions(+) > create mode 100644 recipes-core/openjdk/patches-openjdk-8/CVE-2022-40433.patch > create mode 100644 recipes-core/openjdk/patches-openjdk-8/CVE-2024-20919.patch > create mode 100644 recipes-core/openjdk/patches-openjdk-8/CVE-2024-20921.patch > > diff --git a/recipes-core/openjdk/openjdk-8-release-common.inc b/recipes-core/openjdk/openjdk-8-release-common.inc > index ff8d96e..985d0d7 100644 > --- a/recipes-core/openjdk/openjdk-8-release-common.inc > +++ b/recipes-core/openjdk/openjdk-8-release-common.inc > @@ -21,6 +21,9 @@ PATCHES_URI = "\ > file://2007-jdk-no-genx11-in-headless.patch \ > file://2008-jdk-no-unused-deps.patch \ > file://2009-jdk-make-use-gcc-instead-of-ld-for-genSocketOptionRe.patch \ > +file://CVE-2022-40433.patch \ > +file://CVE-2024-20919.patch \ > +file://CVE-2024-20921.patch \ > " > HOTSPOT_UB_PATCH = "\ > file://1001-hotspot-fix-crash-on-JNI_CreateJavaVM.patch \ > diff --git a/recipes-core/openjdk/patches-openjdk-8/CVE-2022-40433.patch b/recipes-core/openjdk/patches-openjdk-8/CVE-2022-40433.patch > new file mode 100644 > index 0000000..fcae4f4 > --- /dev/null > +++ b/recipes-core/openjdk/patches-openjdk-8/CVE-2022-40433.patch > @@ -0,0 +1,233 @@ > +From 961ab463974b7d05600b826303f9111c4f367a04 Mon Sep 17 00:00:00 2001 > +From: =?UTF-8?q?Ji=C5=99=C3=AD=20Van=C4=9Bk?=<jvanek@openjdk.org> > +Date: Mon, 25 Sep 2023 14:05:03 +0000 > +Subject: [PATCH] 8283441: C2: segmentation fault in > + ciMethodBlocks::make_block_at(int) > + > +Reviewed-by: mbalao > +Backport-of: 947869609ce6b74d4d28f79724b823d8781adbed > + > +Upstream-Status: Backport [https://github.com/openjdk/jdk8u/commit/961ab463974b7d05600b826303f9111c4f367a04] > +CVE: CVE-2022-40433 > +Signed-off-by: Hitendra Prajapati<hprajapati@mvista.com> > +--- > + hotspot/src/share/vm/c1/c1_GraphBuilder.cpp | 12 ++++-- > + hotspot/src/share/vm/ci/ciMethodBlocks.cpp | 17 +++++--- > + .../src/share/vm/compiler/methodLiveness.cpp | 9 ++-- > + hotspot/test/compiler/parsing/Custom.jasm | 38 +++++++++++++++++ > + ...UnreachableBlockFallsThroughEndOfCode.java | 42 +++++++++++++++++++ > + 5 files changed, 106 insertions(+), 12 deletions(-) > + create mode 100644 hotspot/test/compiler/parsing/Custom.jasm > + create mode 100644 hotspot/test/compiler/parsing/UnreachableBlockFallsThroughEndOfCode.java > + > +diff --git a/hotspot/src/share/vm/c1/c1_GraphBuilder.cpp b/hotspot/src/share/vm/c1/c1_GraphBuilder.cpp > +index 99f1c510..6e3e4cc4 100644 > +--- a/hotspot/src/share/vm/c1/c1_GraphBuilder.cpp > ++++ b/hotspot/src/share/vm/c1/c1_GraphBuilder.cpp > +@@ -206,8 +206,10 @@ void BlockListBuilder::handle_exceptions(BlockBegin* current, int cur_bci) { > + } > + > + void BlockListBuilder::handle_jsr(BlockBegin* current, int sr_bci, int next_bci) { > +- // start a new block after jsr-bytecode and link this block into cfg > +- make_block_at(next_bci, current); > ++ if (next_bci < method()->code_size()) { > ++ // start a new block after jsr-bytecode and link this block into cfg > ++ make_block_at(next_bci, current); > ++ } > + > + // start a new block at the subroutine entry at mark it with special flag > + BlockBegin* sr_block = make_block_at(sr_bci, current); > +@@ -227,6 +229,8 @@ void BlockListBuilder::set_leaders() { > + // branch target and a modification of the successor lists. > + BitMap bci_block_start = method()->bci_block_start(); > + > ++ int end_bci = method()->code_size(); > ++ > + ciBytecodeStream s(method()); > + while (s.next() != ciBytecodeStream::EOBC()) { > + int cur_bci = s.cur_bci(); > +@@ -297,7 +301,9 @@ void BlockListBuilder::set_leaders() { > + case Bytecodes::_if_acmpne: // fall through > + case Bytecodes::_ifnull: // fall through > + case Bytecodes::_ifnonnull: > +- make_block_at(s.next_bci(), current); > ++ if (s.next_bci() < end_bci) { > ++ make_block_at(s.next_bci(), current); > ++ } > + make_block_at(s.get_dest(), current); > + current = NULL; > + break; > +diff --git a/hotspot/src/share/vm/ci/ciMethodBlocks.cpp b/hotspot/src/share/vm/ci/ciMethodBlocks.cpp > +index 614e75dc..2285eb0a 100644 > +--- a/hotspot/src/share/vm/ci/ciMethodBlocks.cpp > ++++ b/hotspot/src/share/vm/ci/ciMethodBlocks.cpp > +@@ -1,5 +1,5 @@ > + /* > +- * Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved. > ++ * Copyright (c) 2006, 2022, 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 > +@@ -33,12 +33,13 @@ > + > + > + ciBlock *ciMethodBlocks::block_containing(int bci) { > ++ assert(bci >= 0 && bci < _code_size, "valid bytecode range"); > + ciBlock *blk = _bci_to_block[bci]; > + return blk; > + } > + > + bool ciMethodBlocks::is_block_start(int bci) { > +- assert(bci >=0 && bci < _code_size, "valid bytecode range"); > ++ assert(bci >= 0 && bci < _code_size, "valid bytecode range"); > + ciBlock *b = _bci_to_block[bci]; > + assert(b != NULL, "must have block for bytecode"); > + return b->start_bci() == bci; > +@@ -146,7 +147,9 @@ void ciMethodBlocks::do_analysis() { > + case Bytecodes::_ifnonnull : > + { > + cur_block->set_control_bci(bci); > +- ciBlock *fall_through = make_block_at(s.next_bci()); > ++ if (s.next_bci() < limit_bci) { > ++ ciBlock *fall_through = make_block_at(s.next_bci()); > ++ } > + int dest_bci = s.get_dest(); > + ciBlock *dest = make_block_at(dest_bci); > + break; > +@@ -166,7 +169,9 @@ void ciMethodBlocks::do_analysis() { > + case Bytecodes::_jsr : > + { > + cur_block->set_control_bci(bci); > +- ciBlock *ret = make_block_at(s.next_bci()); > ++ if (s.next_bci() < limit_bci) { > ++ ciBlock *ret = make_block_at(s.next_bci()); > ++ } > + int dest_bci = s.get_dest(); > + ciBlock *dest = make_block_at(dest_bci); > + break; > +@@ -224,7 +229,9 @@ void ciMethodBlocks::do_analysis() { > + case Bytecodes::_jsr_w : > + { > + cur_block->set_control_bci(bci); > +- ciBlock *ret = make_block_at(s.next_bci()); > ++ if (s.next_bci() < limit_bci) { > ++ ciBlock *ret = make_block_at(s.next_bci()); > ++ } > + int dest_bci = s.get_far_dest(); > + ciBlock *dest = make_block_at(dest_bci); > + break; > +diff --git a/hotspot/src/share/vm/compiler/methodLiveness.cpp b/hotspot/src/share/vm/compiler/methodLiveness.cpp > +index eda1ab15..7fb496dc 100644 > +--- a/hotspot/src/share/vm/compiler/methodLiveness.cpp > ++++ b/hotspot/src/share/vm/compiler/methodLiveness.cpp > +@@ -268,10 +268,11 @@ void MethodLiveness::init_basic_blocks() { > + case Bytecodes::_ifnull: > + case Bytecodes::_ifnonnull: > + // Two way branch. Set predecessors at each destination. > +- dest = _block_map->at(bytes.next_bci()); > +- assert(dest != NULL, "must be a block immediately following this one."); > +- dest->add_normal_predecessor(current_block); > +- > ++ if (bytes.next_bci() < method_len) { > ++ dest = _block_map->at(bytes.next_bci()); > ++ assert(dest != NULL, "must be a block immediately following this one."); > ++ dest->add_normal_predecessor(current_block); > ++ } > + dest = _block_map->at(bytes.get_dest()); > + assert(dest != NULL, "branch desination must start a block."); > + dest->add_normal_predecessor(current_block); > +diff --git a/hotspot/test/compiler/parsing/Custom.jasm b/hotspot/test/compiler/parsing/Custom.jasm > +new file mode 100644 > +index 00000000..73a2b1ff > +--- /dev/null > ++++ b/hotspot/test/compiler/parsing/Custom.jasm > +@@ -0,0 +1,38 @@ > ++/* > ++ * Copyright (c) 2022, 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 visitwww.oracle.com if you need additional information or have any > ++ * questions. > ++ */ > ++ > ++package compiler/parsing; > ++ > ++super public class Custom { > ++ > ++ public static Method test:"(I)V" stack 2 locals 1 { > ++ return; > ++Loop: > ++ // Unreachable block > ++ iload_0; > ++ bipush 100; > ++ if_icmpge Loop; > ++ // Falls through > ++ } > ++ > ++} > +diff --git a/hotspot/test/compiler/parsing/UnreachableBlockFallsThroughEndOfCode.java b/hotspot/test/compiler/parsing/UnreachableBlockFallsThroughEndOfCode.java > +new file mode 100644 > +index 00000000..9dfb488d > +--- /dev/null > ++++ b/hotspot/test/compiler/parsing/UnreachableBlockFallsThroughEndOfCode.java > +@@ -0,0 +1,42 @@ > ++/* > ++ * Copyright (c) 2022, 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 visitwww.oracle.com if you need additional information or have any > ++ * questions. > ++ * > ++ */ > ++ > ++/* > ++ * @test UnreachableBlockFallsThroughEndOfCode.java > ++ * @bug 8283441 > ++ * @compile Custom.jasm UnreachableBlockFallsThroughEndOfCode.java > ++ * @summary Compiling method that falls off the end of the code array > ++ * @run main/othervm -XX:TieredStopAtLevel=1 -Xbatch compiler.parsing.UnreachableBlockFallsThroughEndOfCode > ++ * @run main/othervm -XX:-TieredCompilation -Xbatch compiler.parsing.UnreachableBlockFallsThroughEndOfCode > ++ */ > ++ > ++package compiler.parsing; > ++ > ++public class UnreachableBlockFallsThroughEndOfCode { > ++ public static void main(String[] strArr) { > ++ for (int i = 0; i < 20000; i++) { > ++ Custom.test(i); > ++ } > ++ } > ++} > +-- > +2.25.1 > + > diff --git a/recipes-core/openjdk/patches-openjdk-8/CVE-2024-20919.patch b/recipes-core/openjdk/patches-openjdk-8/CVE-2024-20919.patch > new file mode 100644 > index 0000000..a3fc8b6 > --- /dev/null > +++ b/recipes-core/openjdk/patches-openjdk-8/CVE-2024-20919.patch > @@ -0,0 +1,126 @@ > +From 77d38b78e4993381ddb113d012b30ab2d7cd9215 Mon Sep 17 00:00:00 2001 > +From: Martin Balao<mbalao@openjdk.org> > +Date: Fri, 29 Sep 2023 13:01:13 +0000 > +Subject: [PATCH] 8314295: Enhance verification of verifier > + > +Reviewed-by: yan, andrew > +Backport-of: 08980a0a60bc48c17eacd57fd2d7065ac2d986a8 > + > +Upstream-Status: Backport [https://github.com/openjdk/jdk8u/commit/77d38b78e4993381ddb113d012b30ab2d7cd9215] > +CVE: CVE-2024-20919 > +Signed-off-by: Hitendra Prajapati<hprajapati@mvista.com> > +--- > + hotspot/src/share/vm/classfile/verifier.cpp | 5 +++-- > + .../src/share/vm/interpreter/bytecodes.cpp | 22 ++++++++++++++----- > + jdk/src/share/native/common/check_code.c | 11 ++++++---- > + 3 files changed, 26 insertions(+), 12 deletions(-) > + > +diff --git a/hotspot/src/share/vm/classfile/verifier.cpp b/hotspot/src/share/vm/classfile/verifier.cpp > +index 2a572058..3a3e04ba 100644 > +--- a/hotspot/src/share/vm/classfile/verifier.cpp > ++++ b/hotspot/src/share/vm/classfile/verifier.cpp > +@@ -2078,11 +2078,12 @@ void ClassVerifier::verify_switch( > + "low must be less than or equal to high in tableswitch"); > + return; > + } > +- keys = high - low + 1; > +- if (keys < 0) { > ++ int64_t keys64 = ((int64_t)high - low) + 1; > ++ if (keys64 > 65535) { // Max code length > + verify_error(ErrorContext::bad_code(bci), "too many keys in tableswitch"); > + return; > + } > ++ keys = (int)keys64; > + delta = 1; > + } else { > + keys = (int)Bytes::get_Java_u4(aligned_bcp + jintSize); > +diff --git a/hotspot/src/share/vm/interpreter/bytecodes.cpp b/hotspot/src/share/vm/interpreter/bytecodes.cpp > +index 24adc4d9..f0753735 100644 > +--- a/hotspot/src/share/vm/interpreter/bytecodes.cpp > ++++ b/hotspot/src/share/vm/interpreter/bytecodes.cpp > +@@ -111,12 +111,18 @@ int Bytecodes::special_length_at(Bytecodes::Code code, address bcp, address end) > + if (end != NULL && aligned_bcp + 3*jintSize >= end) { > + return -1; // don't read past end of code buffer > + } > ++ // Promote calculation to signed 64 bits to do range checks, used by the verifier. > + jlong lo = (jint)Bytes::get_Java_u4(aligned_bcp + 1*jintSize); > + jlong hi = (jint)Bytes::get_Java_u4(aligned_bcp + 2*jintSize); > + jlong len = (aligned_bcp - bcp) + (3 + hi - lo + 1)*jintSize; > +- // only return len if it can be represented as a positive int; > +- // return -1 otherwise > +- return (len > 0 && len == (int)len) ? len : -1; > ++ // Only return len if it can be represented as a positive int and lo <= hi. > ++ // The caller checks for bytecode stream overflow. > ++ if (lo <= hi && len == (int)len) { > ++ assert(len > 0, "must be"); > ++ return (int)len; > ++ } else { > ++ return -1; > ++ } > + } > + > + case _lookupswitch: // fall through > +@@ -128,9 +134,13 @@ int Bytecodes::special_length_at(Bytecodes::Code code, address bcp, address end) > + } > + jlong npairs = (jint)Bytes::get_Java_u4(aligned_bcp + jintSize); > + jlong len = (aligned_bcp - bcp) + (2 + 2*npairs)*jintSize; > +- // only return len if it can be represented as a positive int; > +- // return -1 otherwise > +- return (len > 0 && len == (int)len) ? len : -1; > ++ // Only return len if it can be represented as a positive int and npairs >= 0. > ++ if (npairs >= 0 && len == (int)len) { > ++ assert(len > 0, "must be"); > ++ return (int)len; > ++ } else { > ++ return -1; > ++ } > + } > + } > + // Note: Length functions must return <=0 for invalid bytecodes. > +diff --git a/jdk/src/share/native/common/check_code.c b/jdk/src/share/native/common/check_code.c > +index 96091720..491b95cd 100644 > +--- a/jdk/src/share/native/common/check_code.c > ++++ b/jdk/src/share/native/common/check_code.c > +@@ -1,5 +1,5 @@ > + /* > +- * Copyright (c) 1994, 2014, Oracle and/or its affiliates. All rights reserved. > ++ * Copyright (c) 1994, 2023, 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 > +@@ -84,6 +84,7 @@ > + #include <assert.h> > + #include <limits.h> > + #include <stdlib.h> > ++#include <stdint.h> > + > + #include "jni.h" > + #include "jvm.h" > +@@ -1202,7 +1203,7 @@ verify_opcode_operands(context_type *context, unsigned int inumber, int offset) > + } > + } > + if (opcode == JVM_OPC_tableswitch) { > +- keys = _ck_ntohl(lpc[2]) - _ck_ntohl(lpc[1]) + 1; > ++ keys = _ck_ntohl(lpc[2]) - _ck_ntohl(lpc[1]) + 1; > + delta = 1; > + } else { > + keys = _ck_ntohl(lpc[1]); /* number of pairs */ > +@@ -1682,11 +1683,13 @@ static int instruction_length(unsigned char *iptr, unsigned char *end) > + switch (instruction) { > + case JVM_OPC_tableswitch: { > + int *lpc = (int *)UCALIGN(iptr + 1); > +- int index; > + if (lpc + 2 >= (int *)end) { > + return -1; /* do not read pass the end */ > + } > +- index = _ck_ntohl(lpc[2]) - _ck_ntohl(lpc[1]); > ++ int64_t low = _ck_ntohl(lpc[1]); > ++ int64_t high = _ck_ntohl(lpc[2]); > ++ int64_t index = high - low; > ++ // The value of low must be less than or equal to high - i.e. index >= 0 > + if ((index < 0) || (index > 65535)) { > + return -1; /* illegal */ > + } else { > +-- > +2.25.1 > + > diff --git a/recipes-core/openjdk/patches-openjdk-8/CVE-2024-20921.patch b/recipes-core/openjdk/patches-openjdk-8/CVE-2024-20921.patch > new file mode 100644 > index 0000000..edce6f0 > --- /dev/null > +++ b/recipes-core/openjdk/patches-openjdk-8/CVE-2024-20921.patch > @@ -0,0 +1,657 @@ > +From f7cb28de01117f598ecf48ad58e277c3a4590436 Mon Sep 17 00:00:00 2001 > +From: Roland Westrelin<rwestrel@redhat.com> > +Date: Tue, 7 Nov 2023 11:08:30 +0000 > +Subject: [PATCH] 8314307: Improve loop handling > + > +Reviewed-by: mbalao, fferrari, andrew > +Backport-of: 62ac93d145ca9fa1ab0c040533c62c42c202703a > + > +Upstream-Status: Backport [https://github.com/openjdk/jdk8u/commit/f7cb28de01117f598ecf48ad58e277c3a4590436] > +CVE: CVE-2024-20921 > +Signed-off-by: Hitendra Prajapati<hprajapati@mvista.com> > +--- > + hotspot/src/share/vm/opto/ifnode.cpp | 59 +++- > + hotspot/src/share/vm/opto/loopnode.cpp | 422 ++++++++++++++++++++----- > + hotspot/src/share/vm/opto/loopnode.hpp | 4 + > + hotspot/src/share/vm/opto/phaseX.hpp | 6 +- > + 4 files changed, 404 insertions(+), 87 deletions(-) > + > +diff --git a/hotspot/src/share/vm/opto/ifnode.cpp b/hotspot/src/share/vm/opto/ifnode.cpp > +index 68f068d0..51579032 100644 > +--- a/hotspot/src/share/vm/opto/ifnode.cpp > ++++ b/hotspot/src/share/vm/opto/ifnode.cpp > +@@ -882,6 +882,46 @@ Node *IfNode::Ideal(PhaseGVN *phase, bool can_reshape) { > + // then we are guaranteed to fail, so just start interpreting there. > + // We 'expand' the top 3 range checks to include all post-dominating > + // checks. > ++ // > ++ // Example: > ++ // a[i+x] // (1) 1 < x < 6 > ++ // a[i+3] // (2) > ++ // a[i+4] // (3) > ++ // a[i+6] // max = max of all constants > ++ // a[i+2] > ++ // a[i+1] // min = min of all constants > ++ // > ++ // If x < 3: > ++ // (1) a[i+x]: Leave unchanged > ++ // (2) a[i+3]: Replace with a[i+max] = a[i+6]: i+x < i+3 <= i+6 -> (2) is covered > ++ // (3) a[i+4]: Replace with a[i+min] = a[i+1]: i+1 < i+4 <= i+6 -> (3) and all following checks are covered > ++ // Remove all other a[i+c] checks > ++ // > ++ // If x >= 3: > ++ // (1) a[i+x]: Leave unchanged > ++ // (2) a[i+3]: Replace with a[i+min] = a[i+1]: i+1 < i+3 <= i+x -> (2) is covered > ++ // (3) a[i+4]: Replace with a[i+max] = a[i+6]: i+1 < i+4 <= i+6 -> (3) and all following checks are covered > ++ // Remove all other a[i+c] checks > ++ // > ++ // We only need the top 2 range checks if x is the min or max of all constants. > ++ // > ++ // This, however, only works if the interval [i+min,i+max] is not larger than max_int (i.e. abs(max - min) < max_int): > ++ // The theoretical max size of an array is max_int with: > ++ // - Valid index space: [0,max_int-1] > ++ // - Invalid index space: [max_int,-1] // max_int, min_int, min_int - 1 ..., -1 > ++ // > ++ // The size of the consecutive valid index space is smaller than the size of the consecutive invalid index space. > ++ // If we choose min and max in such a way that: > ++ // - abs(max - min) < max_int > ++ // - i+max and i+min are inside the valid index space > ++ // then all indices [i+min,i+max] must be in the valid index space. Otherwise, the invalid index space must be > ++ // smaller than the valid index space which is never the case for any array size. > ++ // > ++ // Choosing a smaller array size only makes the valid index space smaller and the invalid index space larger and > ++ // the argument above still holds. > ++ // > ++ // Note that the same optimization with the same maximal accepted interval size can also be found in C1. > ++ const jlong maximum_number_of_min_max_interval_indices = (jlong)max_jint; > + > + // The top 3 range checks seen > + const int NRC =3; > +@@ -915,13 +955,18 @@ Node *IfNode::Ideal(PhaseGVN *phase, bool can_reshape) { > + found_immediate_dominator = true; > + break; > + } > +- // Gather expanded bounds > +- off_lo = MIN2(off_lo,offset2); > +- off_hi = MAX2(off_hi,offset2); > +- // Record top NRC range checks > +- prev_checks[nb_checks%NRC].ctl = prev_dom; > +- prev_checks[nb_checks%NRC].off = offset2; > +- nb_checks++; > ++ > ++ // "x - y" -> must add one to the difference for number of elements in [x,y] > ++ const jlong diff = (jlong)MIN2(offset2, off_lo) - (jlong)MAX2(offset2, off_hi); > ++ if (ABS(diff) < maximum_number_of_min_max_interval_indices) { > ++ // Gather expanded bounds > ++ off_lo = MIN2(off_lo, offset2); > ++ off_hi = MAX2(off_hi, offset2); > ++ // Record top NRC range checks > ++ prev_checks[nb_checks % NRC].ctl = prev_dom; > ++ prev_checks[nb_checks % NRC].off = offset2; > ++ nb_checks++; > ++ } > + } > + } > + prev_dom = dom; > +diff --git a/hotspot/src/share/vm/opto/loopnode.cpp b/hotspot/src/share/vm/opto/loopnode.cpp > +index b2d5dccd..8bc9dd29 100644 > +--- a/hotspot/src/share/vm/opto/loopnode.cpp > ++++ b/hotspot/src/share/vm/opto/loopnode.cpp > +@@ -260,6 +260,49 @@ void PhaseIdealLoop::set_subtree_ctrl( Node *n ) { > + set_early_ctrl( n ); > + } > + > ++void PhaseIdealLoop::insert_loop_limit_check(ProjNode* limit_check_proj, Node* cmp_limit, Node* bol) { > ++ Node* new_predicate_proj = create_new_if_for_predicate(limit_check_proj, NULL, > ++ Deoptimization::Reason_loop_limit_check); > ++ Node* iff = new_predicate_proj->in(0); > ++ assert(iff->Opcode() == Op_If, "bad graph shape"); > ++ Node* conv = iff->in(1); > ++ assert(conv->Opcode() == Op_Conv2B, "bad graph shape"); > ++ Node* opaq = conv->in(1); > ++ assert(opaq->Opcode() == Op_Opaque1, "bad graph shape"); > ++ cmp_limit = _igvn.register_new_node_with_optimizer(cmp_limit); > ++ bol = _igvn.register_new_node_with_optimizer(bol); > ++ set_subtree_ctrl(bol); > ++ _igvn.replace_input_of(iff, 1, bol); > ++ > ++#ifndef PRODUCT > ++ // report that the loop predication has been actually performed > ++ // for this loop > ++ if (TraceLoopLimitCheck) { > ++ tty->print_cr("Counted Loop Limit Check generated:"); > ++ debug_only( bol->dump(2); ) > ++ } > ++#endif > ++} > ++ > ++static int check_stride_overflow(jlong final_correction, const TypeInt* limit_t) { > ++ if (final_correction > 0) { > ++ if (limit_t->_lo > (max_jint - final_correction)) { > ++ return -1; > ++ } > ++ if (limit_t->_hi > (max_jint - final_correction)) { > ++ return 1; > ++ } > ++ } else { > ++ if (limit_t->_hi < (min_jint - final_correction)) { > ++ return -1; > ++ } > ++ if (limit_t->_lo < (min_jint - final_correction)) { > ++ return 1; > ++ } > ++ } > ++ return 0; > ++} > ++ > + //------------------------------is_counted_loop-------------------------------- > + bool PhaseIdealLoop::is_counted_loop( Node *x, IdealLoopTree *loop ) { > + PhaseGVN *gvn = &_igvn; > +@@ -463,51 +506,256 @@ bool PhaseIdealLoop::is_counted_loop( Node *x, IdealLoopTree *loop ) { > + assert(x->Opcode() == Op_Loop, "regular loops only"); > + C->print_method(PHASE_BEFORE_CLOOPS, 3); > + > +- Node *hook = new (C) Node(6); > ++ Node* adjusted_limit = limit; > + > + if (LoopLimitCheck) { > + > + // =================================================== > +- // Generate loop limit check to avoid integer overflow > +- // in cases like next (cyclic loops): > ++ // We can only convert this loop to a counted loop if we can guarantee that the iv phi will never overflow at runtime. > ++ // This is an implicit assumption taken by some loop optimizations. We therefore must ensure this property at all cost. > ++ // At this point, we've already excluded some trivial cases where an overflow could have been proven statically. > ++ // But even though we cannot prove that an overflow will *not* happen, we still want to speculatively convert this loop > ++ // to a counted loop. This can be achieved by adding additional iv phi overflow checks before the loop. If they fail, > ++ // we trap and resume execution before the loop without having executed any iteration of the loop, yet. > + // > +- // for (i=0; i <= max_jint; i++) {} > +- // for (i=0; i < max_jint; i+=2) {} > ++ // These additional iv phi overflow checks can be inserted as Loop Limit Check Predicates above the Loop Limit Check > ++ // Parse Predicate which captures a JVM state just before the entry of the loop. If there is no such Parse Predicate, > ++ // we cannot generate a Loop Limit Check Predicate and thus cannot speculatively convert the loop to a counted loop. > + // > ++ // In the following, we only focus on int loops with stride > 0 to keep things simple. The argumentation and proof > ++ // for stride < 0 is analogously. For long loops, we would replace max_int with max_long. > + // > +- // Limit check predicate depends on the loop test: > + // > +- // for(;i != limit; i++) --> limit <= (max_jint) > +- // for(;i < limit; i+=stride) --> limit <= (max_jint - stride + 1) > +- // for(;i <= limit; i+=stride) --> limit <= (max_jint - stride ) > ++ // The loop to be converted does not always need to have the often used shape: > + // > ++ // i = init > ++ // i = init loop: > ++ // do { ... > ++ // // ... equivalent i+=stride > ++ // i+=stride <==> if (i < limit) > ++ // } while (i < limit); goto loop > ++ // exit: > ++ // ... > ++ // > ++ // where the loop exit check uses the post-incremented iv phi and a '<'-operator. > ++ // > ++ // We could also have '<='-operator (or '>='-operator for negative strides) or use the pre-incremented iv phi value > ++ // in the loop exit check: > ++ // > ++ // i = init > ++ // loop: > ++ // ... > ++ // if (i <= limit) > ++ // i+=stride > ++ // goto loop > ++ // exit: > ++ // ... > ++ // > ++ // Let's define the following terms: > ++ // - iv_pre_i: The pre-incremented iv phi before the i-th iteration. > ++ // - iv_post_i: The post-incremented iv phi after the i-th iteration. > ++ // > ++ // The iv_pre_i and iv_post_i have the following relation: > ++ // iv_pre_i + stride = iv_post_i > ++ // > ++ // When converting a loop to a counted loop, we want to have a canonicalized loop exit check of the form: > ++ // iv_post_i < adjusted_limit > ++ // > ++ // If that is not the case, we need to canonicalize the loop exit check by using different values for adjusted_limit: > ++ // (LE1) iv_post_i < limit: Already canonicalized. We can directly use limit as adjusted_limit. > ++ // -> adjusted_limit = limit. > ++ // (LE2) iv_post_i <= limit: > ++ // iv_post_i < limit + 1 > ++ // -> adjusted limit = limit + 1 > ++ // (LE3) iv_pre_i < limit: > ++ // iv_pre_i + stride < limit + stride > ++ // iv_post_i < limit + stride > ++ // -> adjusted_limit = limit + stride > ++ // (LE4) iv_pre_i <= limit: > ++ // iv_pre_i < limit + 1 > ++ // iv_pre_i + stride < limit + stride + 1 > ++ // iv_post_i < limit + stride + 1 > ++ // -> adjusted_limit = limit + stride + 1 > ++ // > ++ // Note that: > ++ // (AL) limit <= adjusted_limit. > ++ // > ++ // The following loop invariant has to hold for counted loops with n iterations (i.e. loop exit check true after n-th > ++ // loop iteration) and a canonicalized loop exit check to guarantee that no iv_post_i over- or underflows: > ++ // (INV) For i = 1..n, min_int <= iv_post_i <= max_int > ++ // > ++ // To prove (INV), we require the following two conditions/assumptions: > ++ // (i): adjusted_limit - 1 + stride <= max_int > ++ // (ii): init < limit > ++ // > ++ // If we can prove (INV), we know that there can be no over- or underflow of any iv phi value. We prove (INV) by > ++ // induction by assuming (i) and (ii). > ++ // > ++ // Proof by Induction > ++ // ------------------ > ++ // > Base case (i = 1): We show that (INV) holds after the first iteration: > ++ // min_int <= iv_post_1 = init + stride <= max_int > ++ // Proof: > ++ // First, we note that (ii) implies > ++ // (iii) init <= limit - 1 > ++ // max_int >= adjusted_limit - 1 + stride [using (i)] > ++ // >= limit - 1 + stride [using (AL)] > ++ // >= init + stride [using (iii)] > ++ // >= min_int [using stride > 0, no underflow] > ++ // Thus, no overflow happens after the first iteration and (INV) holds for i = 1. > ++ // > ++ // Note that to prove the base case we need (i) and (ii). > ++ // > ++ // > Induction Hypothesis (i = j, j > 1): Assume that (INV) holds after the j-th iteration: > ++ // min_int <= iv_post_j <= max_int > ++ // > Step case (i = j + 1): We show that (INV) also holds after the j+1-th iteration: > ++ // min_int <= iv_post_{j+1} = iv_post_j + stride <= max_int > ++ // Proof: > ++ // If iv_post_j >= adjusted_limit: > ++ // We exit the loop after the j-th iteration, and we don't execute the j+1-th iteration anymore. Thus, there is > ++ // also no iv_{j+1}. Since (INV) holds for iv_j, there is nothing left to prove. > ++ // If iv_post_j < adjusted_limit: > ++ // First, we note that: > ++ // (iv) iv_post_j <= adjusted_limit - 1 > ++ // max_int >= adjusted_limit - 1 + stride [using (i)] > ++ // >= iv_post_j + stride [using (iv)] > ++ // >= min_int [using stride > 0, no underflow] > ++ // > ++ // Note that to prove the step case we only need (i). > ++ // > ++ // Thus, by assuming (i) and (ii), we proved (INV). > ++ // > ++ // > ++ // It is therefore enough to add the following two Loop Limit Check Predicates to check assumptions (i) and (ii): > ++ // > ++ // (1) Loop Limit Check Predicate for (i): > ++ // Using (i): adjusted_limit - 1 + stride <= max_int > ++ // > ++ // This condition is now restated to use limit instead of adjusted_limit: > ++ // > ++ // To prevent an overflow of adjusted_limit -1 + stride itself, we rewrite this check to > ++ // max_int - stride + 1 >= adjusted_limit > ++ // We can merge the two constants into > ++ // canonicalized_correction = stride - 1 > ++ // which gives us > ++ // max_int - canonicalized_correction >= adjusted_limit > ++ // > ++ // To directly use limit instead of adjusted_limit in the predicate condition, we split adjusted_limit into: > ++ // adjusted_limit = limit + limit_correction > ++ // Since stride > 0 and limit_correction <= stride + 1, we can restate this with no over- or underflow into: > ++ // max_int - canonicalized_correction - limit_correction >= limit > ++ // Since canonicalized_correction and limit_correction are both constants, we can replace them with a new constant: > ++ // final_correction = canonicalized_correction + limit_correction > ++ // which gives us: > ++ // > ++ // Final predicate condition: > ++ // max_int - final_correction >= limit > ++ // > ++ // (2) Loop Limit Check Predicate for (ii): > ++ // Using (ii): init < limit > ++ // > ++ // This Loop Limit Check Predicate is not required if we can prove at compile time that either: > ++ // (2.1) type(init) < type(limit) > ++ // In this case, we know: > ++ // all possible values of init < all possible values of limit > ++ // and we can skip the predicate. > ++ // > ++ // (2.2) init < limit is already checked before (i.e. found as a dominating check) > ++ // In this case, we do not need to re-check the condition and can skip the predicate. > ++ // This is often found for while- and for-loops which have the following shape: > ++ // > ++ // if (init < limit) { // Dominating test. Do not need the Loop Limit Check Predicate below. > ++ // i = init; > ++ // if (init >= limit) { trap(); } // Here we would insert the Loop Limit Check Predicate > ++ // do { > ++ // i += stride; > ++ // } while (i < limit); > ++ // } > ++ // > ++ // (2.3) init + stride <= max_int > ++ // In this case, there is no overflow of the iv phi after the first loop iteration. > ++ // In the proof of the base case above we showed that init + stride <= max_int by using assumption (ii): > ++ // init < limit > ++ // In the proof of the step case above, we did not need (ii) anymore. Therefore, if we already know at > ++ // compile time that init + stride <= max_int then we have trivially proven the base case and that > ++ // there is no overflow of the iv phi after the first iteration. In this case, we don't need to check (ii) > ++ // again and can skip the predicate. > ++ > ++ > ++ // Accounting for (LE3) and (LE4) where we use pre-incremented phis in the loop exit check. > ++ const jlong limit_correction_for_pre_iv_exit_check = (phi_incr != NULL) ? stride_con : 0; > ++ > ++ // Accounting for (LE2) and (LE4) where we use <= or >= in the loop exit check. > ++ const bool includes_limit = (bt == BoolTest::le || bt == BoolTest::ge); > ++ const jlong limit_correction_for_le_ge_exit_check = (includes_limit ? (stride_con > 0 ? 1 : -1) : 0); > ++ > ++ const jlong limit_correction = limit_correction_for_pre_iv_exit_check + limit_correction_for_le_ge_exit_check; > ++ const jlong canonicalized_correction = stride_con + (stride_con > 0 ? -1 : 1); > ++ const jlong final_correction = canonicalized_correction + limit_correction; > ++ > ++ int sov = check_stride_overflow(final_correction, limit_t); > ++ > ++ // If sov==0, limit's type always satisfies the condition, for > ++ // example, when it is an array length. > ++ if (sov != 0) { > ++ if (sov < 0) { > ++ return false; // Bailout: integer overflow is certain. > ++ } > ++ // (1) Loop Limit Check Predicate is required because we could not statically prove that > ++ // limit + final_correction = adjusted_limit - 1 + stride <= max_int > ++ ProjNode *limit_check_proj = find_predicate_insertion_point(init_control, Deoptimization::Reason_loop_limit_check); > ++ if (!limit_check_proj) { > ++ // The Loop Limit Check Parse Predicate is not generated if this method trapped here before. > ++#ifdef ASSERT > ++ if (TraceLoopLimitCheck) { > ++ tty->print("missing loop limit check:"); > ++ loop->dump_head(); > ++ x->dump(1); > ++ } > ++#endif > ++ return false; > ++ } > + > +- // Check if limit is excluded to do more precise int overflow check. > +- bool incl_limit = (bt == BoolTest::le || bt == BoolTest::ge); > +- int stride_m = stride_con - (incl_limit ? 0 : (stride_con > 0 ? 1 : -1)); > +- > +- // If compare points directly to the phi we need to adjust > +- // the compare so that it points to the incr. Limit have > +- // to be adjusted to keep trip count the same and the > +- // adjusted limit should be checked for int overflow. > +- if (phi_incr != NULL) { > +- stride_m += stride_con; > +- } > ++ IfNode* check_iff = limit_check_proj->in(0)->as_If(); > + > +- if (limit->is_Con()) { > +- int limit_con = limit->get_int(); > +- if ((stride_con > 0 && limit_con > (max_jint - stride_m)) || > +- (stride_con < 0 && limit_con < (min_jint - stride_m))) { > +- // Bailout: it could be integer overflow. > ++ if (!is_dominator(get_ctrl(limit), check_iff->in(0))) { > + return false; > + } > +- } else if ((stride_con > 0 && limit_t->_hi <= (max_jint - stride_m)) || > +- (stride_con < 0 && limit_t->_lo >= (min_jint - stride_m))) { > +- // Limit's type may satisfy the condition, for example, > +- // when it is an array length. > +- } else { > +- // Generate loop's limit check. > +- // Loop limit check predicate should be near the loop. > ++ > ++ Node* cmp_limit; > ++ Node* bol; > ++ > ++ if (stride_con > 0) { > ++ cmp_limit = new (C) CmpINode(limit, _igvn.intcon(max_jint - final_correction)); > ++ bol = new (C) BoolNode(cmp_limit, BoolTest::le); > ++ } else { > ++ cmp_limit = new (C) CmpINode(limit, _igvn.intcon(min_jint - final_correction)); > ++ bol = new (C) BoolNode(cmp_limit, BoolTest::ge); > ++ } > ++ > ++ insert_loop_limit_check(limit_check_proj, cmp_limit, bol); > ++ } > ++ > ++ // (2.3) > ++ const bool init_plus_stride_could_overflow = > ++ (stride_con > 0 && init_t->_hi > max_jint - stride_con) || > ++ (stride_con < 0 && init_t->_lo < min_jint - stride_con); > ++ // (2.1) > ++ const bool init_gte_limit = (stride_con > 0 && init_t->_hi >= limit_t->_lo) || > ++ (stride_con < 0 && init_t->_lo <= limit_t->_hi); > ++ > ++ if (init_gte_limit && // (2.1) > ++ ((bt == BoolTest::ne || init_plus_stride_could_overflow) && // (2.3) > ++ !has_dominating_loop_limit_check(init_trip, limit, stride_con, init_control))) { // (2.2) > ++ // (2) Iteration Loop Limit Check Predicate is required because neither (2.1), (2.2), nor (2.3) holds. > ++ // We use the following condition: > ++ // - stride > 0: init < limit > ++ // - stride < 0: init > limit > ++ // > ++ // This predicate is always required if we have a non-equal-operator in the loop exit check (where stride = 1 is > ++ // a requirement). We transform the loop exit check by using a less-than-operator. By doing so, we must always > ++ // check that init < limit. Otherwise, we could have a different number of iterations at runtime. > ++ > + ProjNode *limit_check_proj = find_predicate_insertion_point(init_control, Deoptimization::Reason_loop_limit_check); > + if (!limit_check_proj) { > + // The limit check predicate is not generated if this method trapped here before. > +@@ -520,41 +768,38 @@ bool PhaseIdealLoop::is_counted_loop( Node *x, IdealLoopTree *loop ) { > + #endif > + return false; > + } > +- > + IfNode* check_iff = limit_check_proj->in(0)->as_If(); > ++ > ++ if (!is_dominator(get_ctrl(limit), check_iff->in(0)) || > ++ !is_dominator(get_ctrl(init_trip), check_iff->in(0))) { > ++ return false; > ++ } > ++ > + Node* cmp_limit; > + Node* bol; > + > + if (stride_con > 0) { > +- cmp_limit = new (C) CmpINode(limit, _igvn.intcon(max_jint - stride_m)); > +- bol = new (C) BoolNode(cmp_limit, BoolTest::le); > ++ cmp_limit = new (C) CmpINode(init_trip, limit); > ++ bol = new (C) BoolNode(cmp_limit, BoolTest::lt); > + } else { > +- cmp_limit = new (C) CmpINode(limit, _igvn.intcon(min_jint - stride_m)); > +- bol = new (C) BoolNode(cmp_limit, BoolTest::ge); > ++ cmp_limit = new (C) CmpINode(init_trip, limit); > ++ bol = new (C) BoolNode(cmp_limit, BoolTest::gt); > + } > +- cmp_limit = _igvn.register_new_node_with_optimizer(cmp_limit); > +- bol = _igvn.register_new_node_with_optimizer(bol); > +- set_subtree_ctrl(bol); > +- > +- // Replace condition in original predicate but preserve Opaque node > +- // so that previous predicates could be found. > +- assert(check_iff->in(1)->Opcode() == Op_Conv2B && > +- check_iff->in(1)->in(1)->Opcode() == Op_Opaque1, ""); > +- Node* opq = check_iff->in(1)->in(1); > +- _igvn.hash_delete(opq); > +- opq->set_req(1, bol); > +- // Update ctrl. > +- set_ctrl(opq, check_iff->in(0)); > +- set_ctrl(check_iff->in(1), check_iff->in(0)); > + > +-#ifndef PRODUCT > +- // report that the loop predication has been actually performed > +- // for this loop > +- if (TraceLoopLimitCheck) { > +- tty->print_cr("Counted Loop Limit Check generated:"); > +- debug_only( bol->dump(2); ) > ++ insert_loop_limit_check(limit_check_proj, cmp_limit, bol); > ++ } > ++ > ++ if (bt == BoolTest::ne) { > ++ // Now we need to canonicalize the loop condition if it is 'ne'. > ++ assert(stride_con == 1 || stride_con == -1, "simple increment only - checked before"); > ++ if (stride_con > 0) { > ++ // 'ne' can be replaced with 'lt' only when init < limit. This is ensured by the inserted predicate above. > ++ bt = BoolTest::lt; > ++ } else { > ++ assert(stride_con < 0, "must be"); > ++ // 'ne' can be replaced with 'gt' only when init > limit. This is ensured by the inserted predicate above. > ++ bt = BoolTest::gt; > + } > +-#endif > + } > + > + if (phi_incr != NULL) { > +@@ -567,26 +812,15 @@ bool PhaseIdealLoop::is_counted_loop( Node *x, IdealLoopTree *loop ) { > + // is converted to > + // i = init; do {} while(++i < limit+1); > + // > +- limit = gvn->transform(new (C) AddINode(limit, stride)); > +- } > +- > +- // Now we need to canonicalize loop condition. > +- if (bt == BoolTest::ne) { > +- assert(stride_con == 1 || stride_con == -1, "simple increment only"); > +- // 'ne' can be replaced with 'lt' only when init < limit. > +- if (stride_con > 0 && init_t->_hi < limit_t->_lo) > +- bt = BoolTest::lt; > +- // 'ne' can be replaced with 'gt' only when init > limit. > +- if (stride_con < 0 && init_t->_lo > limit_t->_hi) > +- bt = BoolTest::gt; > ++ adjusted_limit = gvn->transform(new (C) AddINode(limit, stride)); > + } > + > +- if (incl_limit) { > ++ if (includes_limit) { > + // The limit check guaranties that 'limit <= (max_jint - stride)' so > + // we can convert 'i <= limit' to 'i < limit+1' since stride != 0. > + // > + Node* one = (stride_con > 0) ? gvn->intcon( 1) : gvn->intcon(-1); > +- limit = gvn->transform(new (C) AddINode(limit, one)); > ++ adjusted_limit = gvn->transform(new (C) AddINode(adjusted_limit, one)); > + if (bt == BoolTest::le) > + bt = BoolTest::lt; > + else if (bt == BoolTest::ge) > +@@ -594,10 +828,11 @@ bool PhaseIdealLoop::is_counted_loop( Node *x, IdealLoopTree *loop ) { > + else > + ShouldNotReachHere(); > + } > +- set_subtree_ctrl( limit ); > ++ set_subtree_ctrl(adjusted_limit); > + > + } else { // LoopLimitCheck > + > ++ Node *hook = new (C) Node(6); > + // If compare points to incr, we are ok. Otherwise the compare > + // can directly point to the phi; in this case adjust the compare so that > + // it points to the incr by adjusting the limit. > +@@ -691,6 +926,11 @@ bool PhaseIdealLoop::is_counted_loop( Node *x, IdealLoopTree *loop ) { > + limit = gvn->transform(new (C) AddINode(span,init_trip)); > + set_subtree_ctrl( limit ); > + > ++ adjusted_limit = limit; > ++ > ++ // Free up intermediate goo > ++ _igvn.remove_dead_node(hook); > ++ > + } // LoopLimitCheck > + > + if (!UseCountedLoopSafepoints) { > +@@ -728,7 +968,7 @@ bool PhaseIdealLoop::is_counted_loop( Node *x, IdealLoopTree *loop ) { > + } > + cmp = cmp->clone(); > + cmp->set_req(1,incr); > +- cmp->set_req(2,limit); > ++ cmp->set_req(2, adjusted_limit); > + cmp = _igvn.register_new_node_with_optimizer(cmp); > + set_ctrl(cmp, iff->in(0)); > + > +@@ -802,9 +1042,6 @@ bool PhaseIdealLoop::is_counted_loop( Node *x, IdealLoopTree *loop ) { > + } > + } > + > +- // Free up intermediate goo > +- _igvn.remove_dead_node(hook); > +- > + #ifdef ASSERT > + assert(l->is_valid_counted_loop(), "counted loop shape is messed up"); > + assert(l == loop->_head && l->phi() == phi && l->loopexit() == lex, "" ); > +@@ -821,6 +1058,37 @@ bool PhaseIdealLoop::is_counted_loop( Node *x, IdealLoopTree *loop ) { > + return true; > + } > + > ++// Check if there is a dominating loop limit check of the form 'init < limit' starting at the loop entry. > ++// If there is one, then we do not need to create an additional Loop Limit Check Predicate. > ++bool PhaseIdealLoop::has_dominating_loop_limit_check(Node* init_trip, Node* limit, const int stride_con, > ++ Node* loop_entry) { > ++ // Eagerly call transform() on the Cmp and Bool node to common them up if possible. This is required in order to > ++ // successfully find a dominated test with the If node below. > ++ Node* cmp_limit; > ++ Node* bol; > ++ if (stride_con > 0) { > ++ cmp_limit = _igvn.transform(new (C) CmpINode(init_trip, limit)); > ++ bol = _igvn.transform(new (C) BoolNode(cmp_limit, BoolTest::lt)); > ++ } else { > ++ cmp_limit = _igvn.transform(new (C) CmpINode(init_trip, limit)); > ++ bol = _igvn.transform(new (C) BoolNode(cmp_limit, BoolTest::gt)); > ++ } > ++ > ++ // Check if there is already a dominating init < limit check. If so, we do not need a Loop Limit Check Predicate. > ++ IfNode* iff = new (C) IfNode(loop_entry, bol, PROB_MIN, COUNT_UNKNOWN); > ++ // Also add fake IfProj nodes in order to call transform() on the newly created IfNode. > ++ IfFalseNode* if_false = new (C) IfFalseNode(iff); > ++ IfTrueNode* if_true = new (C) IfTrueNode(iff); > ++ Node* dominated_iff = _igvn.transform(iff); > ++ // ConI node? Found dominating test (IfNode::dominated_by() returns a ConI node). > ++ const bool found_dominating_test = dominated_iff != NULL && dominated_iff->Opcode() == Op_ConI; > ++ > ++ // Kill the If with its projections again in the next IGVN round by cutting it off from the graph. > ++ _igvn.replace_input_of(iff, 0, C->top()); > ++ _igvn.replace_input_of(iff, 1, C->top()); > ++ return found_dominating_test; > ++} > ++ > + //----------------------exact_limit------------------------------------------- > + Node* PhaseIdealLoop::exact_limit( IdealLoopTree *loop ) { > + assert(loop->_head->is_CountedLoop(), ""); > +diff --git a/hotspot/src/share/vm/opto/loopnode.hpp b/hotspot/src/share/vm/opto/loopnode.hpp > +index 55a7def3..3ae41f8d 100644 > +--- a/hotspot/src/share/vm/opto/loopnode.hpp > ++++ b/hotspot/src/share/vm/opto/loopnode.hpp > +@@ -896,6 +896,10 @@ public: > + // Create a new if above the uncommon_trap_if_pattern for the predicate to be promoted > + ProjNode* create_new_if_for_predicate(ProjNode* cont_proj, Node* new_entry, > + Deoptimization::DeoptReason reason); > ++ void insert_loop_limit_check(ProjNode* limit_check_proj, Node* cmp_limit, Node* bol); > ++ bool has_dominating_loop_limit_check(Node* init_trip, Node* limit, int stride_con, > ++ Node* loop_entry); > ++ > + void register_control(Node* n, IdealLoopTree *loop, Node* pred); > + > + // Clone loop predicates to cloned loops (peeled, unswitched) > +diff --git a/hotspot/src/share/vm/opto/phaseX.hpp b/hotspot/src/share/vm/opto/phaseX.hpp > +index a2a2a538..bd7393ac 100644 > +--- a/hotspot/src/share/vm/opto/phaseX.hpp > ++++ b/hotspot/src/share/vm/opto/phaseX.hpp > +@@ -431,9 +431,6 @@ private: > + > + protected: > + > +- // Idealize new Node 'n' with respect to its inputs and its value > +- virtual Node *transform( Node *a_node ); > +- > + // Warm up hash table, type table and initial worklist > + void init_worklist( Node *a_root ); > + > +@@ -447,6 +444,9 @@ public: > + PhaseIterGVN( PhaseGVN *gvn ); // Used after Parser > + PhaseIterGVN( PhaseIterGVN *igvn, const char *dummy ); // Used after +VerifyOpto > + > ++ // Idealize new Node 'n' with respect to its inputs and its value > ++ virtual Node *transform( Node *a_node ); > ++ > + virtual PhaseIterGVN *is_IterGVN() { return this; } > + > + Unique_Node_List _worklist; // Iterative worklist > +-- > +2.25.1 > +
Hi Team, Any Update on this issue ? Regards, Hitendra On 11/07/24 10:06 am, Hitendra Prajapati via lists.yoctoproject.org wrote: > > Hi Team, > > Any Update on this issue ? > > Regards, > > Hitendra > > On 01/07/24 5:24 pm, Hitendra Prajapati wrote: >> Backport fixes for: >> >> * CVE-2022-40433 - Upstream-Status: Backport fromhttps://github.com/openjdk/jdk8u/commit/961ab463974b7d05600b826303f9111c4f367a04 >> * CVE-2024-20919 - Upstream-Status: Backport fromhttps://github.com/openjdk/jdk8u/commit/77d38b78e4993381ddb113d012b30ab2d7cd9215 >> * CVE-2024-20921 - Upstream-Status: Backport fromhttps://github.com/openjdk/jdk8u/commit/f7cb28de01117f598ecf48ad58e277c3a4590436 >> >> Signed-off-by: Hitendra Prajapati<hprajapati@mvista.com> >> --- >> .../openjdk/openjdk-8-release-common.inc | 3 + >> .../patches-openjdk-8/CVE-2022-40433.patch | 233 +++++++ >> .../patches-openjdk-8/CVE-2024-20919.patch | 126 ++++ >> .../patches-openjdk-8/CVE-2024-20921.patch | 657 ++++++++++++++++++ >> 4 files changed, 1019 insertions(+) >> create mode 100644 recipes-core/openjdk/patches-openjdk-8/CVE-2022-40433.patch >> create mode 100644 recipes-core/openjdk/patches-openjdk-8/CVE-2024-20919.patch >> create mode 100644 recipes-core/openjdk/patches-openjdk-8/CVE-2024-20921.patch >> >> diff --git a/recipes-core/openjdk/openjdk-8-release-common.inc b/recipes-core/openjdk/openjdk-8-release-common.inc >> index ff8d96e..985d0d7 100644 >> --- a/recipes-core/openjdk/openjdk-8-release-common.inc >> +++ b/recipes-core/openjdk/openjdk-8-release-common.inc >> @@ -21,6 +21,9 @@ PATCHES_URI = "\ >> file://2007-jdk-no-genx11-in-headless.patch \ >> file://2008-jdk-no-unused-deps.patch \ >> file://2009-jdk-make-use-gcc-instead-of-ld-for-genSocketOptionRe.patch \ >> +file://CVE-2022-40433.patch \ >> +file://CVE-2024-20919.patch \ >> +file://CVE-2024-20921.patch \ >> " >> HOTSPOT_UB_PATCH = "\ >> file://1001-hotspot-fix-crash-on-JNI_CreateJavaVM.patch \ >> diff --git a/recipes-core/openjdk/patches-openjdk-8/CVE-2022-40433.patch b/recipes-core/openjdk/patches-openjdk-8/CVE-2022-40433.patch >> new file mode 100644 >> index 0000000..fcae4f4 >> --- /dev/null >> +++ b/recipes-core/openjdk/patches-openjdk-8/CVE-2022-40433.patch >> @@ -0,0 +1,233 @@ >> +From 961ab463974b7d05600b826303f9111c4f367a04 Mon Sep 17 00:00:00 2001 >> +From: =?UTF-8?q?Ji=C5=99=C3=AD=20Van=C4=9Bk?=<jvanek@openjdk.org> >> +Date: Mon, 25 Sep 2023 14:05:03 +0000 >> +Subject: [PATCH] 8283441: C2: segmentation fault in >> + ciMethodBlocks::make_block_at(int) >> + >> +Reviewed-by: mbalao >> +Backport-of: 947869609ce6b74d4d28f79724b823d8781adbed >> + >> +Upstream-Status: Backport [https://github.com/openjdk/jdk8u/commit/961ab463974b7d05600b826303f9111c4f367a04] >> +CVE: CVE-2022-40433 >> +Signed-off-by: Hitendra Prajapati<hprajapati@mvista.com> >> +--- >> + hotspot/src/share/vm/c1/c1_GraphBuilder.cpp | 12 ++++-- >> + hotspot/src/share/vm/ci/ciMethodBlocks.cpp | 17 +++++--- >> + .../src/share/vm/compiler/methodLiveness.cpp | 9 ++-- >> + hotspot/test/compiler/parsing/Custom.jasm | 38 +++++++++++++++++ >> + ...UnreachableBlockFallsThroughEndOfCode.java | 42 +++++++++++++++++++ >> + 5 files changed, 106 insertions(+), 12 deletions(-) >> + create mode 100644 hotspot/test/compiler/parsing/Custom.jasm >> + create mode 100644 hotspot/test/compiler/parsing/UnreachableBlockFallsThroughEndOfCode.java >> + >> +diff --git a/hotspot/src/share/vm/c1/c1_GraphBuilder.cpp b/hotspot/src/share/vm/c1/c1_GraphBuilder.cpp >> +index 99f1c510..6e3e4cc4 100644 >> +--- a/hotspot/src/share/vm/c1/c1_GraphBuilder.cpp >> ++++ b/hotspot/src/share/vm/c1/c1_GraphBuilder.cpp >> +@@ -206,8 +206,10 @@ void BlockListBuilder::handle_exceptions(BlockBegin* current, int cur_bci) { >> + } >> + >> + void BlockListBuilder::handle_jsr(BlockBegin* current, int sr_bci, int next_bci) { >> +- // start a new block after jsr-bytecode and link this block into cfg >> +- make_block_at(next_bci, current); >> ++ if (next_bci < method()->code_size()) { >> ++ // start a new block after jsr-bytecode and link this block into cfg >> ++ make_block_at(next_bci, current); >> ++ } >> + >> + // start a new block at the subroutine entry at mark it with special flag >> + BlockBegin* sr_block = make_block_at(sr_bci, current); >> +@@ -227,6 +229,8 @@ void BlockListBuilder::set_leaders() { >> + // branch target and a modification of the successor lists. >> + BitMap bci_block_start = method()->bci_block_start(); >> + >> ++ int end_bci = method()->code_size(); >> ++ >> + ciBytecodeStream s(method()); >> + while (s.next() != ciBytecodeStream::EOBC()) { >> + int cur_bci = s.cur_bci(); >> +@@ -297,7 +301,9 @@ void BlockListBuilder::set_leaders() { >> + case Bytecodes::_if_acmpne: // fall through >> + case Bytecodes::_ifnull: // fall through >> + case Bytecodes::_ifnonnull: >> +- make_block_at(s.next_bci(), current); >> ++ if (s.next_bci() < end_bci) { >> ++ make_block_at(s.next_bci(), current); >> ++ } >> + make_block_at(s.get_dest(), current); >> + current = NULL; >> + break; >> +diff --git a/hotspot/src/share/vm/ci/ciMethodBlocks.cpp b/hotspot/src/share/vm/ci/ciMethodBlocks.cpp >> +index 614e75dc..2285eb0a 100644 >> +--- a/hotspot/src/share/vm/ci/ciMethodBlocks.cpp >> ++++ b/hotspot/src/share/vm/ci/ciMethodBlocks.cpp >> +@@ -1,5 +1,5 @@ >> + /* >> +- * Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved. >> ++ * Copyright (c) 2006, 2022, 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 >> +@@ -33,12 +33,13 @@ >> + >> + >> + ciBlock *ciMethodBlocks::block_containing(int bci) { >> ++ assert(bci >= 0 && bci < _code_size, "valid bytecode range"); >> + ciBlock *blk = _bci_to_block[bci]; >> + return blk; >> + } >> + >> + bool ciMethodBlocks::is_block_start(int bci) { >> +- assert(bci >=0 && bci < _code_size, "valid bytecode range"); >> ++ assert(bci >= 0 && bci < _code_size, "valid bytecode range"); >> + ciBlock *b = _bci_to_block[bci]; >> + assert(b != NULL, "must have block for bytecode"); >> + return b->start_bci() == bci; >> +@@ -146,7 +147,9 @@ void ciMethodBlocks::do_analysis() { >> + case Bytecodes::_ifnonnull : >> + { >> + cur_block->set_control_bci(bci); >> +- ciBlock *fall_through = make_block_at(s.next_bci()); >> ++ if (s.next_bci() < limit_bci) { >> ++ ciBlock *fall_through = make_block_at(s.next_bci()); >> ++ } >> + int dest_bci = s.get_dest(); >> + ciBlock *dest = make_block_at(dest_bci); >> + break; >> +@@ -166,7 +169,9 @@ void ciMethodBlocks::do_analysis() { >> + case Bytecodes::_jsr : >> + { >> + cur_block->set_control_bci(bci); >> +- ciBlock *ret = make_block_at(s.next_bci()); >> ++ if (s.next_bci() < limit_bci) { >> ++ ciBlock *ret = make_block_at(s.next_bci()); >> ++ } >> + int dest_bci = s.get_dest(); >> + ciBlock *dest = make_block_at(dest_bci); >> + break; >> +@@ -224,7 +229,9 @@ void ciMethodBlocks::do_analysis() { >> + case Bytecodes::_jsr_w : >> + { >> + cur_block->set_control_bci(bci); >> +- ciBlock *ret = make_block_at(s.next_bci()); >> ++ if (s.next_bci() < limit_bci) { >> ++ ciBlock *ret = make_block_at(s.next_bci()); >> ++ } >> + int dest_bci = s.get_far_dest(); >> + ciBlock *dest = make_block_at(dest_bci); >> + break; >> +diff --git a/hotspot/src/share/vm/compiler/methodLiveness.cpp b/hotspot/src/share/vm/compiler/methodLiveness.cpp >> +index eda1ab15..7fb496dc 100644 >> +--- a/hotspot/src/share/vm/compiler/methodLiveness.cpp >> ++++ b/hotspot/src/share/vm/compiler/methodLiveness.cpp >> +@@ -268,10 +268,11 @@ void MethodLiveness::init_basic_blocks() { >> + case Bytecodes::_ifnull: >> + case Bytecodes::_ifnonnull: >> + // Two way branch. Set predecessors at each destination. >> +- dest = _block_map->at(bytes.next_bci()); >> +- assert(dest != NULL, "must be a block immediately following this one."); >> +- dest->add_normal_predecessor(current_block); >> +- >> ++ if (bytes.next_bci() < method_len) { >> ++ dest = _block_map->at(bytes.next_bci()); >> ++ assert(dest != NULL, "must be a block immediately following this one."); >> ++ dest->add_normal_predecessor(current_block); >> ++ } >> + dest = _block_map->at(bytes.get_dest()); >> + assert(dest != NULL, "branch desination must start a block."); >> + dest->add_normal_predecessor(current_block); >> +diff --git a/hotspot/test/compiler/parsing/Custom.jasm b/hotspot/test/compiler/parsing/Custom.jasm >> +new file mode 100644 >> +index 00000000..73a2b1ff >> +--- /dev/null >> ++++ b/hotspot/test/compiler/parsing/Custom.jasm >> +@@ -0,0 +1,38 @@ >> ++/* >> ++ * Copyright (c) 2022, 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 visitwww.oracle.com if you need additional information or have any >> ++ * questions. >> ++ */ >> ++ >> ++package compiler/parsing; >> ++ >> ++super public class Custom { >> ++ >> ++ public static Method test:"(I)V" stack 2 locals 1 { >> ++ return; >> ++Loop: >> ++ // Unreachable block >> ++ iload_0; >> ++ bipush 100; >> ++ if_icmpge Loop; >> ++ // Falls through >> ++ } >> ++ >> ++} >> +diff --git a/hotspot/test/compiler/parsing/UnreachableBlockFallsThroughEndOfCode.java b/hotspot/test/compiler/parsing/UnreachableBlockFallsThroughEndOfCode.java >> +new file mode 100644 >> +index 00000000..9dfb488d >> +--- /dev/null >> ++++ b/hotspot/test/compiler/parsing/UnreachableBlockFallsThroughEndOfCode.java >> +@@ -0,0 +1,42 @@ >> ++/* >> ++ * Copyright (c) 2022, 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 visitwww.oracle.com if you need additional information or have any >> ++ * questions. >> ++ * >> ++ */ >> ++ >> ++/* >> ++ * @test UnreachableBlockFallsThroughEndOfCode.java >> ++ * @bug 8283441 >> ++ * @compile Custom.jasm UnreachableBlockFallsThroughEndOfCode.java >> ++ * @summary Compiling method that falls off the end of the code array >> ++ * @run main/othervm -XX:TieredStopAtLevel=1 -Xbatch compiler.parsing.UnreachableBlockFallsThroughEndOfCode >> ++ * @run main/othervm -XX:-TieredCompilation -Xbatch compiler.parsing.UnreachableBlockFallsThroughEndOfCode >> ++ */ >> ++ >> ++package compiler.parsing; >> ++ >> ++public class UnreachableBlockFallsThroughEndOfCode { >> ++ public static void main(String[] strArr) { >> ++ for (int i = 0; i < 20000; i++) { >> ++ Custom.test(i); >> ++ } >> ++ } >> ++} >> +-- >> +2.25.1 >> + >> diff --git a/recipes-core/openjdk/patches-openjdk-8/CVE-2024-20919.patch b/recipes-core/openjdk/patches-openjdk-8/CVE-2024-20919.patch >> new file mode 100644 >> index 0000000..a3fc8b6 >> --- /dev/null >> +++ b/recipes-core/openjdk/patches-openjdk-8/CVE-2024-20919.patch >> @@ -0,0 +1,126 @@ >> +From 77d38b78e4993381ddb113d012b30ab2d7cd9215 Mon Sep 17 00:00:00 2001 >> +From: Martin Balao<mbalao@openjdk.org> >> +Date: Fri, 29 Sep 2023 13:01:13 +0000 >> +Subject: [PATCH] 8314295: Enhance verification of verifier >> + >> +Reviewed-by: yan, andrew >> +Backport-of: 08980a0a60bc48c17eacd57fd2d7065ac2d986a8 >> + >> +Upstream-Status: Backport [https://github.com/openjdk/jdk8u/commit/77d38b78e4993381ddb113d012b30ab2d7cd9215] >> +CVE: CVE-2024-20919 >> +Signed-off-by: Hitendra Prajapati<hprajapati@mvista.com> >> +--- >> + hotspot/src/share/vm/classfile/verifier.cpp | 5 +++-- >> + .../src/share/vm/interpreter/bytecodes.cpp | 22 ++++++++++++++----- >> + jdk/src/share/native/common/check_code.c | 11 ++++++---- >> + 3 files changed, 26 insertions(+), 12 deletions(-) >> + >> +diff --git a/hotspot/src/share/vm/classfile/verifier.cpp b/hotspot/src/share/vm/classfile/verifier.cpp >> +index 2a572058..3a3e04ba 100644 >> +--- a/hotspot/src/share/vm/classfile/verifier.cpp >> ++++ b/hotspot/src/share/vm/classfile/verifier.cpp >> +@@ -2078,11 +2078,12 @@ void ClassVerifier::verify_switch( >> + "low must be less than or equal to high in tableswitch"); >> + return; >> + } >> +- keys = high - low + 1; >> +- if (keys < 0) { >> ++ int64_t keys64 = ((int64_t)high - low) + 1; >> ++ if (keys64 > 65535) { // Max code length >> + verify_error(ErrorContext::bad_code(bci), "too many keys in tableswitch"); >> + return; >> + } >> ++ keys = (int)keys64; >> + delta = 1; >> + } else { >> + keys = (int)Bytes::get_Java_u4(aligned_bcp + jintSize); >> +diff --git a/hotspot/src/share/vm/interpreter/bytecodes.cpp b/hotspot/src/share/vm/interpreter/bytecodes.cpp >> +index 24adc4d9..f0753735 100644 >> +--- a/hotspot/src/share/vm/interpreter/bytecodes.cpp >> ++++ b/hotspot/src/share/vm/interpreter/bytecodes.cpp >> +@@ -111,12 +111,18 @@ int Bytecodes::special_length_at(Bytecodes::Code code, address bcp, address end) >> + if (end != NULL && aligned_bcp + 3*jintSize >= end) { >> + return -1; // don't read past end of code buffer >> + } >> ++ // Promote calculation to signed 64 bits to do range checks, used by the verifier. >> + jlong lo = (jint)Bytes::get_Java_u4(aligned_bcp + 1*jintSize); >> + jlong hi = (jint)Bytes::get_Java_u4(aligned_bcp + 2*jintSize); >> + jlong len = (aligned_bcp - bcp) + (3 + hi - lo + 1)*jintSize; >> +- // only return len if it can be represented as a positive int; >> +- // return -1 otherwise >> +- return (len > 0 && len == (int)len) ? len : -1; >> ++ // Only return len if it can be represented as a positive int and lo <= hi. >> ++ // The caller checks for bytecode stream overflow. >> ++ if (lo <= hi && len == (int)len) { >> ++ assert(len > 0, "must be"); >> ++ return (int)len; >> ++ } else { >> ++ return -1; >> ++ } >> + } >> + >> + case _lookupswitch: // fall through >> +@@ -128,9 +134,13 @@ int Bytecodes::special_length_at(Bytecodes::Code code, address bcp, address end) >> + } >> + jlong npairs = (jint)Bytes::get_Java_u4(aligned_bcp + jintSize); >> + jlong len = (aligned_bcp - bcp) + (2 + 2*npairs)*jintSize; >> +- // only return len if it can be represented as a positive int; >> +- // return -1 otherwise >> +- return (len > 0 && len == (int)len) ? len : -1; >> ++ // Only return len if it can be represented as a positive int and npairs >= 0. >> ++ if (npairs >= 0 && len == (int)len) { >> ++ assert(len > 0, "must be"); >> ++ return (int)len; >> ++ } else { >> ++ return -1; >> ++ } >> + } >> + } >> + // Note: Length functions must return <=0 for invalid bytecodes. >> +diff --git a/jdk/src/share/native/common/check_code.c b/jdk/src/share/native/common/check_code.c >> +index 96091720..491b95cd 100644 >> +--- a/jdk/src/share/native/common/check_code.c >> ++++ b/jdk/src/share/native/common/check_code.c >> +@@ -1,5 +1,5 @@ >> + /* >> +- * Copyright (c) 1994, 2014, Oracle and/or its affiliates. All rights reserved. >> ++ * Copyright (c) 1994, 2023, 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 >> +@@ -84,6 +84,7 @@ >> + #include <assert.h> >> + #include <limits.h> >> + #include <stdlib.h> >> ++#include <stdint.h> >> + >> + #include "jni.h" >> + #include "jvm.h" >> +@@ -1202,7 +1203,7 @@ verify_opcode_operands(context_type *context, unsigned int inumber, int offset) >> + } >> + } >> + if (opcode == JVM_OPC_tableswitch) { >> +- keys = _ck_ntohl(lpc[2]) - _ck_ntohl(lpc[1]) + 1; >> ++ keys = _ck_ntohl(lpc[2]) - _ck_ntohl(lpc[1]) + 1; >> + delta = 1; >> + } else { >> + keys = _ck_ntohl(lpc[1]); /* number of pairs */ >> +@@ -1682,11 +1683,13 @@ static int instruction_length(unsigned char *iptr, unsigned char *end) >> + switch (instruction) { >> + case JVM_OPC_tableswitch: { >> + int *lpc = (int *)UCALIGN(iptr + 1); >> +- int index; >> + if (lpc + 2 >= (int *)end) { >> + return -1; /* do not read pass the end */ >> + } >> +- index = _ck_ntohl(lpc[2]) - _ck_ntohl(lpc[1]); >> ++ int64_t low = _ck_ntohl(lpc[1]); >> ++ int64_t high = _ck_ntohl(lpc[2]); >> ++ int64_t index = high - low; >> ++ // The value of low must be less than or equal to high - i.e. index >= 0 >> + if ((index < 0) || (index > 65535)) { >> + return -1; /* illegal */ >> + } else { >> +-- >> +2.25.1 >> + >> diff --git a/recipes-core/openjdk/patches-openjdk-8/CVE-2024-20921.patch b/recipes-core/openjdk/patches-openjdk-8/CVE-2024-20921.patch >> new file mode 100644 >> index 0000000..edce6f0 >> --- /dev/null >> +++ b/recipes-core/openjdk/patches-openjdk-8/CVE-2024-20921.patch >> @@ -0,0 +1,657 @@ >> +From f7cb28de01117f598ecf48ad58e277c3a4590436 Mon Sep 17 00:00:00 2001 >> +From: Roland Westrelin<rwestrel@redhat.com> >> +Date: Tue, 7 Nov 2023 11:08:30 +0000 >> +Subject: [PATCH] 8314307: Improve loop handling >> + >> +Reviewed-by: mbalao, fferrari, andrew >> +Backport-of: 62ac93d145ca9fa1ab0c040533c62c42c202703a >> + >> +Upstream-Status: Backport [https://github.com/openjdk/jdk8u/commit/f7cb28de01117f598ecf48ad58e277c3a4590436] >> +CVE: CVE-2024-20921 >> +Signed-off-by: Hitendra Prajapati<hprajapati@mvista.com> >> +--- >> + hotspot/src/share/vm/opto/ifnode.cpp | 59 +++- >> + hotspot/src/share/vm/opto/loopnode.cpp | 422 ++++++++++++++++++++----- >> + hotspot/src/share/vm/opto/loopnode.hpp | 4 + >> + hotspot/src/share/vm/opto/phaseX.hpp | 6 +- >> + 4 files changed, 404 insertions(+), 87 deletions(-) >> + >> +diff --git a/hotspot/src/share/vm/opto/ifnode.cpp b/hotspot/src/share/vm/opto/ifnode.cpp >> +index 68f068d0..51579032 100644 >> +--- a/hotspot/src/share/vm/opto/ifnode.cpp >> ++++ b/hotspot/src/share/vm/opto/ifnode.cpp >> +@@ -882,6 +882,46 @@ Node *IfNode::Ideal(PhaseGVN *phase, bool can_reshape) { >> + // then we are guaranteed to fail, so just start interpreting there. >> + // We 'expand' the top 3 range checks to include all post-dominating >> + // checks. >> ++ // >> ++ // Example: >> ++ // a[i+x] // (1) 1 < x < 6 >> ++ // a[i+3] // (2) >> ++ // a[i+4] // (3) >> ++ // a[i+6] // max = max of all constants >> ++ // a[i+2] >> ++ // a[i+1] // min = min of all constants >> ++ // >> ++ // If x < 3: >> ++ // (1) a[i+x]: Leave unchanged >> ++ // (2) a[i+3]: Replace with a[i+max] = a[i+6]: i+x < i+3 <= i+6 -> (2) is covered >> ++ // (3) a[i+4]: Replace with a[i+min] = a[i+1]: i+1 < i+4 <= i+6 -> (3) and all following checks are covered >> ++ // Remove all other a[i+c] checks >> ++ // >> ++ // If x >= 3: >> ++ // (1) a[i+x]: Leave unchanged >> ++ // (2) a[i+3]: Replace with a[i+min] = a[i+1]: i+1 < i+3 <= i+x -> (2) is covered >> ++ // (3) a[i+4]: Replace with a[i+max] = a[i+6]: i+1 < i+4 <= i+6 -> (3) and all following checks are covered >> ++ // Remove all other a[i+c] checks >> ++ // >> ++ // We only need the top 2 range checks if x is the min or max of all constants. >> ++ // >> ++ // This, however, only works if the interval [i+min,i+max] is not larger than max_int (i.e. abs(max - min) < max_int): >> ++ // The theoretical max size of an array is max_int with: >> ++ // - Valid index space: [0,max_int-1] >> ++ // - Invalid index space: [max_int,-1] // max_int, min_int, min_int - 1 ..., -1 >> ++ // >> ++ // The size of the consecutive valid index space is smaller than the size of the consecutive invalid index space. >> ++ // If we choose min and max in such a way that: >> ++ // - abs(max - min) < max_int >> ++ // - i+max and i+min are inside the valid index space >> ++ // then all indices [i+min,i+max] must be in the valid index space. Otherwise, the invalid index space must be >> ++ // smaller than the valid index space which is never the case for any array size. >> ++ // >> ++ // Choosing a smaller array size only makes the valid index space smaller and the invalid index space larger and >> ++ // the argument above still holds. >> ++ // >> ++ // Note that the same optimization with the same maximal accepted interval size can also be found in C1. >> ++ const jlong maximum_number_of_min_max_interval_indices = (jlong)max_jint; >> + >> + // The top 3 range checks seen >> + const int NRC =3; >> +@@ -915,13 +955,18 @@ Node *IfNode::Ideal(PhaseGVN *phase, bool can_reshape) { >> + found_immediate_dominator = true; >> + break; >> + } >> +- // Gather expanded bounds >> +- off_lo = MIN2(off_lo,offset2); >> +- off_hi = MAX2(off_hi,offset2); >> +- // Record top NRC range checks >> +- prev_checks[nb_checks%NRC].ctl = prev_dom; >> +- prev_checks[nb_checks%NRC].off = offset2; >> +- nb_checks++; >> ++ >> ++ // "x - y" -> must add one to the difference for number of elements in [x,y] >> ++ const jlong diff = (jlong)MIN2(offset2, off_lo) - (jlong)MAX2(offset2, off_hi); >> ++ if (ABS(diff) < maximum_number_of_min_max_interval_indices) { >> ++ // Gather expanded bounds >> ++ off_lo = MIN2(off_lo, offset2); >> ++ off_hi = MAX2(off_hi, offset2); >> ++ // Record top NRC range checks >> ++ prev_checks[nb_checks % NRC].ctl = prev_dom; >> ++ prev_checks[nb_checks % NRC].off = offset2; >> ++ nb_checks++; >> ++ } >> + } >> + } >> + prev_dom = dom; >> +diff --git a/hotspot/src/share/vm/opto/loopnode.cpp b/hotspot/src/share/vm/opto/loopnode.cpp >> +index b2d5dccd..8bc9dd29 100644 >> +--- a/hotspot/src/share/vm/opto/loopnode.cpp >> ++++ b/hotspot/src/share/vm/opto/loopnode.cpp >> +@@ -260,6 +260,49 @@ void PhaseIdealLoop::set_subtree_ctrl( Node *n ) { >> + set_early_ctrl( n ); >> + } >> + >> ++void PhaseIdealLoop::insert_loop_limit_check(ProjNode* limit_check_proj, Node* cmp_limit, Node* bol) { >> ++ Node* new_predicate_proj = create_new_if_for_predicate(limit_check_proj, NULL, >> ++ Deoptimization::Reason_loop_limit_check); >> ++ Node* iff = new_predicate_proj->in(0); >> ++ assert(iff->Opcode() == Op_If, "bad graph shape"); >> ++ Node* conv = iff->in(1); >> ++ assert(conv->Opcode() == Op_Conv2B, "bad graph shape"); >> ++ Node* opaq = conv->in(1); >> ++ assert(opaq->Opcode() == Op_Opaque1, "bad graph shape"); >> ++ cmp_limit = _igvn.register_new_node_with_optimizer(cmp_limit); >> ++ bol = _igvn.register_new_node_with_optimizer(bol); >> ++ set_subtree_ctrl(bol); >> ++ _igvn.replace_input_of(iff, 1, bol); >> ++ >> ++#ifndef PRODUCT >> ++ // report that the loop predication has been actually performed >> ++ // for this loop >> ++ if (TraceLoopLimitCheck) { >> ++ tty->print_cr("Counted Loop Limit Check generated:"); >> ++ debug_only( bol->dump(2); ) >> ++ } >> ++#endif >> ++} >> ++ >> ++static int check_stride_overflow(jlong final_correction, const TypeInt* limit_t) { >> ++ if (final_correction > 0) { >> ++ if (limit_t->_lo > (max_jint - final_correction)) { >> ++ return -1; >> ++ } >> ++ if (limit_t->_hi > (max_jint - final_correction)) { >> ++ return 1; >> ++ } >> ++ } else { >> ++ if (limit_t->_hi < (min_jint - final_correction)) { >> ++ return -1; >> ++ } >> ++ if (limit_t->_lo < (min_jint - final_correction)) { >> ++ return 1; >> ++ } >> ++ } >> ++ return 0; >> ++} >> ++ >> + //------------------------------is_counted_loop-------------------------------- >> + bool PhaseIdealLoop::is_counted_loop( Node *x, IdealLoopTree *loop ) { >> + PhaseGVN *gvn = &_igvn; >> +@@ -463,51 +506,256 @@ bool PhaseIdealLoop::is_counted_loop( Node *x, IdealLoopTree *loop ) { >> + assert(x->Opcode() == Op_Loop, "regular loops only"); >> + C->print_method(PHASE_BEFORE_CLOOPS, 3); >> + >> +- Node *hook = new (C) Node(6); >> ++ Node* adjusted_limit = limit; >> + >> + if (LoopLimitCheck) { >> + >> + // =================================================== >> +- // Generate loop limit check to avoid integer overflow >> +- // in cases like next (cyclic loops): >> ++ // We can only convert this loop to a counted loop if we can guarantee that the iv phi will never overflow at runtime. >> ++ // This is an implicit assumption taken by some loop optimizations. We therefore must ensure this property at all cost. >> ++ // At this point, we've already excluded some trivial cases where an overflow could have been proven statically. >> ++ // But even though we cannot prove that an overflow will *not* happen, we still want to speculatively convert this loop >> ++ // to a counted loop. This can be achieved by adding additional iv phi overflow checks before the loop. If they fail, >> ++ // we trap and resume execution before the loop without having executed any iteration of the loop, yet. >> + // >> +- // for (i=0; i <= max_jint; i++) {} >> +- // for (i=0; i < max_jint; i+=2) {} >> ++ // These additional iv phi overflow checks can be inserted as Loop Limit Check Predicates above the Loop Limit Check >> ++ // Parse Predicate which captures a JVM state just before the entry of the loop. If there is no such Parse Predicate, >> ++ // we cannot generate a Loop Limit Check Predicate and thus cannot speculatively convert the loop to a counted loop. >> + // >> ++ // In the following, we only focus on int loops with stride > 0 to keep things simple. The argumentation and proof >> ++ // for stride < 0 is analogously. For long loops, we would replace max_int with max_long. >> + // >> +- // Limit check predicate depends on the loop test: >> + // >> +- // for(;i != limit; i++) --> limit <= (max_jint) >> +- // for(;i < limit; i+=stride) --> limit <= (max_jint - stride + 1) >> +- // for(;i <= limit; i+=stride) --> limit <= (max_jint - stride ) >> ++ // The loop to be converted does not always need to have the often used shape: >> + // >> ++ // i = init >> ++ // i = init loop: >> ++ // do { ... >> ++ // // ... equivalent i+=stride >> ++ // i+=stride <==> if (i < limit) >> ++ // } while (i < limit); goto loop >> ++ // exit: >> ++ // ... >> ++ // >> ++ // where the loop exit check uses the post-incremented iv phi and a '<'-operator. >> ++ // >> ++ // We could also have '<='-operator (or '>='-operator for negative strides) or use the pre-incremented iv phi value >> ++ // in the loop exit check: >> ++ // >> ++ // i = init >> ++ // loop: >> ++ // ... >> ++ // if (i <= limit) >> ++ // i+=stride >> ++ // goto loop >> ++ // exit: >> ++ // ... >> ++ // >> ++ // Let's define the following terms: >> ++ // - iv_pre_i: The pre-incremented iv phi before the i-th iteration. >> ++ // - iv_post_i: The post-incremented iv phi after the i-th iteration. >> ++ // >> ++ // The iv_pre_i and iv_post_i have the following relation: >> ++ // iv_pre_i + stride = iv_post_i >> ++ // >> ++ // When converting a loop to a counted loop, we want to have a canonicalized loop exit check of the form: >> ++ // iv_post_i < adjusted_limit >> ++ // >> ++ // If that is not the case, we need to canonicalize the loop exit check by using different values for adjusted_limit: >> ++ // (LE1) iv_post_i < limit: Already canonicalized. We can directly use limit as adjusted_limit. >> ++ // -> adjusted_limit = limit. >> ++ // (LE2) iv_post_i <= limit: >> ++ // iv_post_i < limit + 1 >> ++ // -> adjusted limit = limit + 1 >> ++ // (LE3) iv_pre_i < limit: >> ++ // iv_pre_i + stride < limit + stride >> ++ // iv_post_i < limit + stride >> ++ // -> adjusted_limit = limit + stride >> ++ // (LE4) iv_pre_i <= limit: >> ++ // iv_pre_i < limit + 1 >> ++ // iv_pre_i + stride < limit + stride + 1 >> ++ // iv_post_i < limit + stride + 1 >> ++ // -> adjusted_limit = limit + stride + 1 >> ++ // >> ++ // Note that: >> ++ // (AL) limit <= adjusted_limit. >> ++ // >> ++ // The following loop invariant has to hold for counted loops with n iterations (i.e. loop exit check true after n-th >> ++ // loop iteration) and a canonicalized loop exit check to guarantee that no iv_post_i over- or underflows: >> ++ // (INV) For i = 1..n, min_int <= iv_post_i <= max_int >> ++ // >> ++ // To prove (INV), we require the following two conditions/assumptions: >> ++ // (i): adjusted_limit - 1 + stride <= max_int >> ++ // (ii): init < limit >> ++ // >> ++ // If we can prove (INV), we know that there can be no over- or underflow of any iv phi value. We prove (INV) by >> ++ // induction by assuming (i) and (ii). >> ++ // >> ++ // Proof by Induction >> ++ // ------------------ >> ++ // > Base case (i = 1): We show that (INV) holds after the first iteration: >> ++ // min_int <= iv_post_1 = init + stride <= max_int >> ++ // Proof: >> ++ // First, we note that (ii) implies >> ++ // (iii) init <= limit - 1 >> ++ // max_int >= adjusted_limit - 1 + stride [using (i)] >> ++ // >= limit - 1 + stride [using (AL)] >> ++ // >= init + stride [using (iii)] >> ++ // >= min_int [using stride > 0, no underflow] >> ++ // Thus, no overflow happens after the first iteration and (INV) holds for i = 1. >> ++ // >> ++ // Note that to prove the base case we need (i) and (ii). >> ++ // >> ++ // > Induction Hypothesis (i = j, j > 1): Assume that (INV) holds after the j-th iteration: >> ++ // min_int <= iv_post_j <= max_int >> ++ // > Step case (i = j + 1): We show that (INV) also holds after the j+1-th iteration: >> ++ // min_int <= iv_post_{j+1} = iv_post_j + stride <= max_int >> ++ // Proof: >> ++ // If iv_post_j >= adjusted_limit: >> ++ // We exit the loop after the j-th iteration, and we don't execute the j+1-th iteration anymore. Thus, there is >> ++ // also no iv_{j+1}. Since (INV) holds for iv_j, there is nothing left to prove. >> ++ // If iv_post_j < adjusted_limit: >> ++ // First, we note that: >> ++ // (iv) iv_post_j <= adjusted_limit - 1 >> ++ // max_int >= adjusted_limit - 1 + stride [using (i)] >> ++ // >= iv_post_j + stride [using (iv)] >> ++ // >= min_int [using stride > 0, no underflow] >> ++ // >> ++ // Note that to prove the step case we only need (i). >> ++ // >> ++ // Thus, by assuming (i) and (ii), we proved (INV). >> ++ // >> ++ // >> ++ // It is therefore enough to add the following two Loop Limit Check Predicates to check assumptions (i) and (ii): >> ++ // >> ++ // (1) Loop Limit Check Predicate for (i): >> ++ // Using (i): adjusted_limit - 1 + stride <= max_int >> ++ // >> ++ // This condition is now restated to use limit instead of adjusted_limit: >> ++ // >> ++ // To prevent an overflow of adjusted_limit -1 + stride itself, we rewrite this check to >> ++ // max_int - stride + 1 >= adjusted_limit >> ++ // We can merge the two constants into >> ++ // canonicalized_correction = stride - 1 >> ++ // which gives us >> ++ // max_int - canonicalized_correction >= adjusted_limit >> ++ // >> ++ // To directly use limit instead of adjusted_limit in the predicate condition, we split adjusted_limit into: >> ++ // adjusted_limit = limit + limit_correction >> ++ // Since stride > 0 and limit_correction <= stride + 1, we can restate this with no over- or underflow into: >> ++ // max_int - canonicalized_correction - limit_correction >= limit >> ++ // Since canonicalized_correction and limit_correction are both constants, we can replace them with a new constant: >> ++ // final_correction = canonicalized_correction + limit_correction >> ++ // which gives us: >> ++ // >> ++ // Final predicate condition: >> ++ // max_int - final_correction >= limit >> ++ // >> ++ // (2) Loop Limit Check Predicate for (ii): >> ++ // Using (ii): init < limit >> ++ // >> ++ // This Loop Limit Check Predicate is not required if we can prove at compile time that either: >> ++ // (2.1) type(init) < type(limit) >> ++ // In this case, we know: >> ++ // all possible values of init < all possible values of limit >> ++ // and we can skip the predicate. >> ++ // >> ++ // (2.2) init < limit is already checked before (i.e. found as a dominating check) >> ++ // In this case, we do not need to re-check the condition and can skip the predicate. >> ++ // This is often found for while- and for-loops which have the following shape: >> ++ // >> ++ // if (init < limit) { // Dominating test. Do not need the Loop Limit Check Predicate below. >> ++ // i = init; >> ++ // if (init >= limit) { trap(); } // Here we would insert the Loop Limit Check Predicate >> ++ // do { >> ++ // i += stride; >> ++ // } while (i < limit); >> ++ // } >> ++ // >> ++ // (2.3) init + stride <= max_int >> ++ // In this case, there is no overflow of the iv phi after the first loop iteration. >> ++ // In the proof of the base case above we showed that init + stride <= max_int by using assumption (ii): >> ++ // init < limit >> ++ // In the proof of the step case above, we did not need (ii) anymore. Therefore, if we already know at >> ++ // compile time that init + stride <= max_int then we have trivially proven the base case and that >> ++ // there is no overflow of the iv phi after the first iteration. In this case, we don't need to check (ii) >> ++ // again and can skip the predicate. >> ++ >> ++ >> ++ // Accounting for (LE3) and (LE4) where we use pre-incremented phis in the loop exit check. >> ++ const jlong limit_correction_for_pre_iv_exit_check = (phi_incr != NULL) ? stride_con : 0; >> ++ >> ++ // Accounting for (LE2) and (LE4) where we use <= or >= in the loop exit check. >> ++ const bool includes_limit = (bt == BoolTest::le || bt == BoolTest::ge); >> ++ const jlong limit_correction_for_le_ge_exit_check = (includes_limit ? (stride_con > 0 ? 1 : -1) : 0); >> ++ >> ++ const jlong limit_correction = limit_correction_for_pre_iv_exit_check + limit_correction_for_le_ge_exit_check; >> ++ const jlong canonicalized_correction = stride_con + (stride_con > 0 ? -1 : 1); >> ++ const jlong final_correction = canonicalized_correction + limit_correction; >> ++ >> ++ int sov = check_stride_overflow(final_correction, limit_t); >> ++ >> ++ // If sov==0, limit's type always satisfies the condition, for >> ++ // example, when it is an array length. >> ++ if (sov != 0) { >> ++ if (sov < 0) { >> ++ return false; // Bailout: integer overflow is certain. >> ++ } >> ++ // (1) Loop Limit Check Predicate is required because we could not statically prove that >> ++ // limit + final_correction = adjusted_limit - 1 + stride <= max_int >> ++ ProjNode *limit_check_proj = find_predicate_insertion_point(init_control, Deoptimization::Reason_loop_limit_check); >> ++ if (!limit_check_proj) { >> ++ // The Loop Limit Check Parse Predicate is not generated if this method trapped here before. >> ++#ifdef ASSERT >> ++ if (TraceLoopLimitCheck) { >> ++ tty->print("missing loop limit check:"); >> ++ loop->dump_head(); >> ++ x->dump(1); >> ++ } >> ++#endif >> ++ return false; >> ++ } >> + >> +- // Check if limit is excluded to do more precise int overflow check. >> +- bool incl_limit = (bt == BoolTest::le || bt == BoolTest::ge); >> +- int stride_m = stride_con - (incl_limit ? 0 : (stride_con > 0 ? 1 : -1)); >> +- >> +- // If compare points directly to the phi we need to adjust >> +- // the compare so that it points to the incr. Limit have >> +- // to be adjusted to keep trip count the same and the >> +- // adjusted limit should be checked for int overflow. >> +- if (phi_incr != NULL) { >> +- stride_m += stride_con; >> +- } >> ++ IfNode* check_iff = limit_check_proj->in(0)->as_If(); >> + >> +- if (limit->is_Con()) { >> +- int limit_con = limit->get_int(); >> +- if ((stride_con > 0 && limit_con > (max_jint - stride_m)) || >> +- (stride_con < 0 && limit_con < (min_jint - stride_m))) { >> +- // Bailout: it could be integer overflow. >> ++ if (!is_dominator(get_ctrl(limit), check_iff->in(0))) { >> + return false; >> + } >> +- } else if ((stride_con > 0 && limit_t->_hi <= (max_jint - stride_m)) || >> +- (stride_con < 0 && limit_t->_lo >= (min_jint - stride_m))) { >> +- // Limit's type may satisfy the condition, for example, >> +- // when it is an array length. >> +- } else { >> +- // Generate loop's limit check. >> +- // Loop limit check predicate should be near the loop. >> ++ >> ++ Node* cmp_limit; >> ++ Node* bol; >> ++ >> ++ if (stride_con > 0) { >> ++ cmp_limit = new (C) CmpINode(limit, _igvn.intcon(max_jint - final_correction)); >> ++ bol = new (C) BoolNode(cmp_limit, BoolTest::le); >> ++ } else { >> ++ cmp_limit = new (C) CmpINode(limit, _igvn.intcon(min_jint - final_correction)); >> ++ bol = new (C) BoolNode(cmp_limit, BoolTest::ge); >> ++ } >> ++ >> ++ insert_loop_limit_check(limit_check_proj, cmp_limit, bol); >> ++ } >> ++ >> ++ // (2.3) >> ++ const bool init_plus_stride_could_overflow = >> ++ (stride_con > 0 && init_t->_hi > max_jint - stride_con) || >> ++ (stride_con < 0 && init_t->_lo < min_jint - stride_con); >> ++ // (2.1) >> ++ const bool init_gte_limit = (stride_con > 0 && init_t->_hi >= limit_t->_lo) || >> ++ (stride_con < 0 && init_t->_lo <= limit_t->_hi); >> ++ >> ++ if (init_gte_limit && // (2.1) >> ++ ((bt == BoolTest::ne || init_plus_stride_could_overflow) && // (2.3) >> ++ !has_dominating_loop_limit_check(init_trip, limit, stride_con, init_control))) { // (2.2) >> ++ // (2) Iteration Loop Limit Check Predicate is required because neither (2.1), (2.2), nor (2.3) holds. >> ++ // We use the following condition: >> ++ // - stride > 0: init < limit >> ++ // - stride < 0: init > limit >> ++ // >> ++ // This predicate is always required if we have a non-equal-operator in the loop exit check (where stride = 1 is >> ++ // a requirement). We transform the loop exit check by using a less-than-operator. By doing so, we must always >> ++ // check that init < limit. Otherwise, we could have a different number of iterations at runtime. >> ++ >> + ProjNode *limit_check_proj = find_predicate_insertion_point(init_control, Deoptimization::Reason_loop_limit_check); >> + if (!limit_check_proj) { >> + // The limit check predicate is not generated if this method trapped here before. >> +@@ -520,41 +768,38 @@ bool PhaseIdealLoop::is_counted_loop( Node *x, IdealLoopTree *loop ) { >> + #endif >> + return false; >> + } >> +- >> + IfNode* check_iff = limit_check_proj->in(0)->as_If(); >> ++ >> ++ if (!is_dominator(get_ctrl(limit), check_iff->in(0)) || >> ++ !is_dominator(get_ctrl(init_trip), check_iff->in(0))) { >> ++ return false; >> ++ } >> ++ >> + Node* cmp_limit; >> + Node* bol; >> + >> + if (stride_con > 0) { >> +- cmp_limit = new (C) CmpINode(limit, _igvn.intcon(max_jint - stride_m)); >> +- bol = new (C) BoolNode(cmp_limit, BoolTest::le); >> ++ cmp_limit = new (C) CmpINode(init_trip, limit); >> ++ bol = new (C) BoolNode(cmp_limit, BoolTest::lt); >> + } else { >> +- cmp_limit = new (C) CmpINode(limit, _igvn.intcon(min_jint - stride_m)); >> +- bol = new (C) BoolNode(cmp_limit, BoolTest::ge); >> ++ cmp_limit = new (C) CmpINode(init_trip, limit); >> ++ bol = new (C) BoolNode(cmp_limit, BoolTest::gt); >> + } >> +- cmp_limit = _igvn.register_new_node_with_optimizer(cmp_limit); >> +- bol = _igvn.register_new_node_with_optimizer(bol); >> +- set_subtree_ctrl(bol); >> +- >> +- // Replace condition in original predicate but preserve Opaque node >> +- // so that previous predicates could be found. >> +- assert(check_iff->in(1)->Opcode() == Op_Conv2B && >> +- check_iff->in(1)->in(1)->Opcode() == Op_Opaque1, ""); >> +- Node* opq = check_iff->in(1)->in(1); >> +- _igvn.hash_delete(opq); >> +- opq->set_req(1, bol); >> +- // Update ctrl. >> +- set_ctrl(opq, check_iff->in(0)); >> +- set_ctrl(check_iff->in(1), check_iff->in(0)); >> + >> +-#ifndef PRODUCT >> +- // report that the loop predication has been actually performed >> +- // for this loop >> +- if (TraceLoopLimitCheck) { >> +- tty->print_cr("Counted Loop Limit Check generated:"); >> +- debug_only( bol->dump(2); ) >> ++ insert_loop_limit_check(limit_check_proj, cmp_limit, bol); >> ++ } >> ++ >> ++ if (bt == BoolTest::ne) { >> ++ // Now we need to canonicalize the loop condition if it is 'ne'. >> ++ assert(stride_con == 1 || stride_con == -1, "simple increment only - checked before"); >> ++ if (stride_con > 0) { >> ++ // 'ne' can be replaced with 'lt' only when init < limit. This is ensured by the inserted predicate above. >> ++ bt = BoolTest::lt; >> ++ } else { >> ++ assert(stride_con < 0, "must be"); >> ++ // 'ne' can be replaced with 'gt' only when init > limit. This is ensured by the inserted predicate above. >> ++ bt = BoolTest::gt; >> + } >> +-#endif >> + } >> + >> + if (phi_incr != NULL) { >> +@@ -567,26 +812,15 @@ bool PhaseIdealLoop::is_counted_loop( Node *x, IdealLoopTree *loop ) { >> + // is converted to >> + // i = init; do {} while(++i < limit+1); >> + // >> +- limit = gvn->transform(new (C) AddINode(limit, stride)); >> +- } >> +- >> +- // Now we need to canonicalize loop condition. >> +- if (bt == BoolTest::ne) { >> +- assert(stride_con == 1 || stride_con == -1, "simple increment only"); >> +- // 'ne' can be replaced with 'lt' only when init < limit. >> +- if (stride_con > 0 && init_t->_hi < limit_t->_lo) >> +- bt = BoolTest::lt; >> +- // 'ne' can be replaced with 'gt' only when init > limit. >> +- if (stride_con < 0 && init_t->_lo > limit_t->_hi) >> +- bt = BoolTest::gt; >> ++ adjusted_limit = gvn->transform(new (C) AddINode(limit, stride)); >> + } >> + >> +- if (incl_limit) { >> ++ if (includes_limit) { >> + // The limit check guaranties that 'limit <= (max_jint - stride)' so >> + // we can convert 'i <= limit' to 'i < limit+1' since stride != 0. >> + // >> + Node* one = (stride_con > 0) ? gvn->intcon( 1) : gvn->intcon(-1); >> +- limit = gvn->transform(new (C) AddINode(limit, one)); >> ++ adjusted_limit = gvn->transform(new (C) AddINode(adjusted_limit, one)); >> + if (bt == BoolTest::le) >> + bt = BoolTest::lt; >> + else if (bt == BoolTest::ge) >> +@@ -594,10 +828,11 @@ bool PhaseIdealLoop::is_counted_loop( Node *x, IdealLoopTree *loop ) { >> + else >> + ShouldNotReachHere(); >> + } >> +- set_subtree_ctrl( limit ); >> ++ set_subtree_ctrl(adjusted_limit); >> + >> + } else { // LoopLimitCheck >> + >> ++ Node *hook = new (C) Node(6); >> + // If compare points to incr, we are ok. Otherwise the compare >> + // can directly point to the phi; in this case adjust the compare so that >> + // it points to the incr by adjusting the limit. >> +@@ -691,6 +926,11 @@ bool PhaseIdealLoop::is_counted_loop( Node *x, IdealLoopTree *loop ) { >> + limit = gvn->transform(new (C) AddINode(span,init_trip)); >> + set_subtree_ctrl( limit ); >> + >> ++ adjusted_limit = limit; >> ++ >> ++ // Free up intermediate goo >> ++ _igvn.remove_dead_node(hook); >> ++ >> + } // LoopLimitCheck >> + >> + if (!UseCountedLoopSafepoints) { >> +@@ -728,7 +968,7 @@ bool PhaseIdealLoop::is_counted_loop( Node *x, IdealLoopTree *loop ) { >> + } >> + cmp = cmp->clone(); >> + cmp->set_req(1,incr); >> +- cmp->set_req(2,limit); >> ++ cmp->set_req(2, adjusted_limit); >> + cmp = _igvn.register_new_node_with_optimizer(cmp); >> + set_ctrl(cmp, iff->in(0)); >> + >> +@@ -802,9 +1042,6 @@ bool PhaseIdealLoop::is_counted_loop( Node *x, IdealLoopTree *loop ) { >> + } >> + } >> + >> +- // Free up intermediate goo >> +- _igvn.remove_dead_node(hook); >> +- >> + #ifdef ASSERT >> + assert(l->is_valid_counted_loop(), "counted loop shape is messed up"); >> + assert(l == loop->_head && l->phi() == phi && l->loopexit() == lex, "" ); >> +@@ -821,6 +1058,37 @@ bool PhaseIdealLoop::is_counted_loop( Node *x, IdealLoopTree *loop ) { >> + return true; >> + } >> + >> ++// Check if there is a dominating loop limit check of the form 'init < limit' starting at the loop entry. >> ++// If there is one, then we do not need to create an additional Loop Limit Check Predicate. >> ++bool PhaseIdealLoop::has_dominating_loop_limit_check(Node* init_trip, Node* limit, const int stride_con, >> ++ Node* loop_entry) { >> ++ // Eagerly call transform() on the Cmp and Bool node to common them up if possible. This is required in order to >> ++ // successfully find a dominated test with the If node below. >> ++ Node* cmp_limit; >> ++ Node* bol; >> ++ if (stride_con > 0) { >> ++ cmp_limit = _igvn.transform(new (C) CmpINode(init_trip, limit)); >> ++ bol = _igvn.transform(new (C) BoolNode(cmp_limit, BoolTest::lt)); >> ++ } else { >> ++ cmp_limit = _igvn.transform(new (C) CmpINode(init_trip, limit)); >> ++ bol = _igvn.transform(new (C) BoolNode(cmp_limit, BoolTest::gt)); >> ++ } >> ++ >> ++ // Check if there is already a dominating init < limit check. If so, we do not need a Loop Limit Check Predicate. >> ++ IfNode* iff = new (C) IfNode(loop_entry, bol, PROB_MIN, COUNT_UNKNOWN); >> ++ // Also add fake IfProj nodes in order to call transform() on the newly created IfNode. >> ++ IfFalseNode* if_false = new (C) IfFalseNode(iff); >> ++ IfTrueNode* if_true = new (C) IfTrueNode(iff); >> ++ Node* dominated_iff = _igvn.transform(iff); >> ++ // ConI node? Found dominating test (IfNode::dominated_by() returns a ConI node). >> ++ const bool found_dominating_test = dominated_iff != NULL && dominated_iff->Opcode() == Op_ConI; >> ++ >> ++ // Kill the If with its projections again in the next IGVN round by cutting it off from the graph. >> ++ _igvn.replace_input_of(iff, 0, C->top()); >> ++ _igvn.replace_input_of(iff, 1, C->top()); >> ++ return found_dominating_test; >> ++} >> ++ >> + //----------------------exact_limit------------------------------------------- >> + Node* PhaseIdealLoop::exact_limit( IdealLoopTree *loop ) { >> + assert(loop->_head->is_CountedLoop(), ""); >> +diff --git a/hotspot/src/share/vm/opto/loopnode.hpp b/hotspot/src/share/vm/opto/loopnode.hpp >> +index 55a7def3..3ae41f8d 100644 >> +--- a/hotspot/src/share/vm/opto/loopnode.hpp >> ++++ b/hotspot/src/share/vm/opto/loopnode.hpp >> +@@ -896,6 +896,10 @@ public: >> + // Create a new if above the uncommon_trap_if_pattern for the predicate to be promoted >> + ProjNode* create_new_if_for_predicate(ProjNode* cont_proj, Node* new_entry, >> + Deoptimization::DeoptReason reason); >> ++ void insert_loop_limit_check(ProjNode* limit_check_proj, Node* cmp_limit, Node* bol); >> ++ bool has_dominating_loop_limit_check(Node* init_trip, Node* limit, int stride_con, >> ++ Node* loop_entry); >> ++ >> + void register_control(Node* n, IdealLoopTree *loop, Node* pred); >> + >> + // Clone loop predicates to cloned loops (peeled, unswitched) >> +diff --git a/hotspot/src/share/vm/opto/phaseX.hpp b/hotspot/src/share/vm/opto/phaseX.hpp >> +index a2a2a538..bd7393ac 100644 >> +--- a/hotspot/src/share/vm/opto/phaseX.hpp >> ++++ b/hotspot/src/share/vm/opto/phaseX.hpp >> +@@ -431,9 +431,6 @@ private: >> + >> + protected: >> + >> +- // Idealize new Node 'n' with respect to its inputs and its value >> +- virtual Node *transform( Node *a_node ); >> +- >> + // Warm up hash table, type table and initial worklist >> + void init_worklist( Node *a_root ); >> + >> +@@ -447,6 +444,9 @@ public: >> + PhaseIterGVN( PhaseGVN *gvn ); // Used after Parser >> + PhaseIterGVN( PhaseIterGVN *igvn, const char *dummy ); // Used after +VerifyOpto >> + >> ++ // Idealize new Node 'n' with respect to its inputs and its value >> ++ virtual Node *transform( Node *a_node ); >> ++ >> + virtual PhaseIterGVN *is_IterGVN() { return this; } >> + >> + Unique_Node_List _worklist; // Iterative worklist >> +-- >> +2.25.1 >> + > -- > Regards, > Hitendra Prajapati > MontaVista Software LLC > Mo: +91 9998906483 > _._,_._,_ > ------------------------------------------------------------------------ > Links: > > You receive all messages sent to this group. > > View/Reply Online (#423) > <https://lists.yoctoproject.org/g/yocto-patches/message/423> | Reply > To Group > <mailto:yocto-patches@lists.yoctoproject.org?subject=Re:%20Re%3A%20%5Byocto-patches%5D%20%5Bmeta-java%2C%20master%5D%5BPATCH%5D%20openjdk-8%3A%20Fix%20multiple%20CVEs> > | Reply To Sender > <mailto:hprajapati@mvista.com?subject=Private:%20Re:%20Re%3A%20%5Byocto-patches%5D%20%5Bmeta-java%2C%20master%5D%5BPATCH%5D%20openjdk-8%3A%20Fix%20multiple%20CVEs> > | Mute This Topic > <https://lists.yoctoproject.org/mt/106976234/6955432> | New Topic > <https://lists.yoctoproject.org/g/yocto-patches/post> > Your Subscription > <https://lists.yoctoproject.org/g/yocto-patches/editsub/6955432> | > Contact Group Owner > <mailto:yocto-patches+owner@lists.yoctoproject.org> | Unsubscribe > <https://lists.yoctoproject.org/g/yocto-patches/leave/13410361/6955432/643796302/xyzzy> > [hprajapati@mvista.com] > > _._,_._,_
On Wed, 2024-07-17 at 17:35 +0530, Hitendra Prajapati via lists.yoctoproject.org wrote: > Hi Team, > Any Update on this issue ? > Regards, > Hitendra > The maintenance of meta-java is struggling. The code doesn't have good/working automated testing which makes it hard to know which patches are safe to apply and which are not. It has struggled along for a while now but has reached a point where nobody knows what to do with it. We can't just "blindly" merge patches and hope for the best. So I doubt there will be an update on this issue any time soon. We've simply run out of people with time to spent on maintaining it. Cheers, Richard > >
diff --git a/recipes-core/openjdk/openjdk-8-release-common.inc b/recipes-core/openjdk/openjdk-8-release-common.inc index ff8d96e..985d0d7 100644 --- a/recipes-core/openjdk/openjdk-8-release-common.inc +++ b/recipes-core/openjdk/openjdk-8-release-common.inc @@ -21,6 +21,9 @@ PATCHES_URI = "\ file://2007-jdk-no-genx11-in-headless.patch \ file://2008-jdk-no-unused-deps.patch \ file://2009-jdk-make-use-gcc-instead-of-ld-for-genSocketOptionRe.patch \ + file://CVE-2022-40433.patch \ + file://CVE-2024-20919.patch \ + file://CVE-2024-20921.patch \ " HOTSPOT_UB_PATCH = "\ file://1001-hotspot-fix-crash-on-JNI_CreateJavaVM.patch \ diff --git a/recipes-core/openjdk/patches-openjdk-8/CVE-2022-40433.patch b/recipes-core/openjdk/patches-openjdk-8/CVE-2022-40433.patch new file mode 100644 index 0000000..fcae4f4 --- /dev/null +++ b/recipes-core/openjdk/patches-openjdk-8/CVE-2022-40433.patch @@ -0,0 +1,233 @@ +From 961ab463974b7d05600b826303f9111c4f367a04 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ji=C5=99=C3=AD=20Van=C4=9Bk?= <jvanek@openjdk.org> +Date: Mon, 25 Sep 2023 14:05:03 +0000 +Subject: [PATCH] 8283441: C2: segmentation fault in + ciMethodBlocks::make_block_at(int) + +Reviewed-by: mbalao +Backport-of: 947869609ce6b74d4d28f79724b823d8781adbed + +Upstream-Status: Backport [https://github.com/openjdk/jdk8u/commit/961ab463974b7d05600b826303f9111c4f367a04] +CVE: CVE-2022-40433 +Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com> +--- + hotspot/src/share/vm/c1/c1_GraphBuilder.cpp | 12 ++++-- + hotspot/src/share/vm/ci/ciMethodBlocks.cpp | 17 +++++--- + .../src/share/vm/compiler/methodLiveness.cpp | 9 ++-- + hotspot/test/compiler/parsing/Custom.jasm | 38 +++++++++++++++++ + ...UnreachableBlockFallsThroughEndOfCode.java | 42 +++++++++++++++++++ + 5 files changed, 106 insertions(+), 12 deletions(-) + create mode 100644 hotspot/test/compiler/parsing/Custom.jasm + create mode 100644 hotspot/test/compiler/parsing/UnreachableBlockFallsThroughEndOfCode.java + +diff --git a/hotspot/src/share/vm/c1/c1_GraphBuilder.cpp b/hotspot/src/share/vm/c1/c1_GraphBuilder.cpp +index 99f1c510..6e3e4cc4 100644 +--- a/hotspot/src/share/vm/c1/c1_GraphBuilder.cpp ++++ b/hotspot/src/share/vm/c1/c1_GraphBuilder.cpp +@@ -206,8 +206,10 @@ void BlockListBuilder::handle_exceptions(BlockBegin* current, int cur_bci) { + } + + void BlockListBuilder::handle_jsr(BlockBegin* current, int sr_bci, int next_bci) { +- // start a new block after jsr-bytecode and link this block into cfg +- make_block_at(next_bci, current); ++ if (next_bci < method()->code_size()) { ++ // start a new block after jsr-bytecode and link this block into cfg ++ make_block_at(next_bci, current); ++ } + + // start a new block at the subroutine entry at mark it with special flag + BlockBegin* sr_block = make_block_at(sr_bci, current); +@@ -227,6 +229,8 @@ void BlockListBuilder::set_leaders() { + // branch target and a modification of the successor lists. + BitMap bci_block_start = method()->bci_block_start(); + ++ int end_bci = method()->code_size(); ++ + ciBytecodeStream s(method()); + while (s.next() != ciBytecodeStream::EOBC()) { + int cur_bci = s.cur_bci(); +@@ -297,7 +301,9 @@ void BlockListBuilder::set_leaders() { + case Bytecodes::_if_acmpne: // fall through + case Bytecodes::_ifnull: // fall through + case Bytecodes::_ifnonnull: +- make_block_at(s.next_bci(), current); ++ if (s.next_bci() < end_bci) { ++ make_block_at(s.next_bci(), current); ++ } + make_block_at(s.get_dest(), current); + current = NULL; + break; +diff --git a/hotspot/src/share/vm/ci/ciMethodBlocks.cpp b/hotspot/src/share/vm/ci/ciMethodBlocks.cpp +index 614e75dc..2285eb0a 100644 +--- a/hotspot/src/share/vm/ci/ciMethodBlocks.cpp ++++ b/hotspot/src/share/vm/ci/ciMethodBlocks.cpp +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 2006, 2022, 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 +@@ -33,12 +33,13 @@ + + + ciBlock *ciMethodBlocks::block_containing(int bci) { ++ assert(bci >= 0 && bci < _code_size, "valid bytecode range"); + ciBlock *blk = _bci_to_block[bci]; + return blk; + } + + bool ciMethodBlocks::is_block_start(int bci) { +- assert(bci >=0 && bci < _code_size, "valid bytecode range"); ++ assert(bci >= 0 && bci < _code_size, "valid bytecode range"); + ciBlock *b = _bci_to_block[bci]; + assert(b != NULL, "must have block for bytecode"); + return b->start_bci() == bci; +@@ -146,7 +147,9 @@ void ciMethodBlocks::do_analysis() { + case Bytecodes::_ifnonnull : + { + cur_block->set_control_bci(bci); +- ciBlock *fall_through = make_block_at(s.next_bci()); ++ if (s.next_bci() < limit_bci) { ++ ciBlock *fall_through = make_block_at(s.next_bci()); ++ } + int dest_bci = s.get_dest(); + ciBlock *dest = make_block_at(dest_bci); + break; +@@ -166,7 +169,9 @@ void ciMethodBlocks::do_analysis() { + case Bytecodes::_jsr : + { + cur_block->set_control_bci(bci); +- ciBlock *ret = make_block_at(s.next_bci()); ++ if (s.next_bci() < limit_bci) { ++ ciBlock *ret = make_block_at(s.next_bci()); ++ } + int dest_bci = s.get_dest(); + ciBlock *dest = make_block_at(dest_bci); + break; +@@ -224,7 +229,9 @@ void ciMethodBlocks::do_analysis() { + case Bytecodes::_jsr_w : + { + cur_block->set_control_bci(bci); +- ciBlock *ret = make_block_at(s.next_bci()); ++ if (s.next_bci() < limit_bci) { ++ ciBlock *ret = make_block_at(s.next_bci()); ++ } + int dest_bci = s.get_far_dest(); + ciBlock *dest = make_block_at(dest_bci); + break; +diff --git a/hotspot/src/share/vm/compiler/methodLiveness.cpp b/hotspot/src/share/vm/compiler/methodLiveness.cpp +index eda1ab15..7fb496dc 100644 +--- a/hotspot/src/share/vm/compiler/methodLiveness.cpp ++++ b/hotspot/src/share/vm/compiler/methodLiveness.cpp +@@ -268,10 +268,11 @@ void MethodLiveness::init_basic_blocks() { + case Bytecodes::_ifnull: + case Bytecodes::_ifnonnull: + // Two way branch. Set predecessors at each destination. +- dest = _block_map->at(bytes.next_bci()); +- assert(dest != NULL, "must be a block immediately following this one."); +- dest->add_normal_predecessor(current_block); +- ++ if (bytes.next_bci() < method_len) { ++ dest = _block_map->at(bytes.next_bci()); ++ assert(dest != NULL, "must be a block immediately following this one."); ++ dest->add_normal_predecessor(current_block); ++ } + dest = _block_map->at(bytes.get_dest()); + assert(dest != NULL, "branch desination must start a block."); + dest->add_normal_predecessor(current_block); +diff --git a/hotspot/test/compiler/parsing/Custom.jasm b/hotspot/test/compiler/parsing/Custom.jasm +new file mode 100644 +index 00000000..73a2b1ff +--- /dev/null ++++ b/hotspot/test/compiler/parsing/Custom.jasm +@@ -0,0 +1,38 @@ ++/* ++ * Copyright (c) 2022, 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. ++ */ ++ ++package compiler/parsing; ++ ++super public class Custom { ++ ++ public static Method test:"(I)V" stack 2 locals 1 { ++ return; ++Loop: ++ // Unreachable block ++ iload_0; ++ bipush 100; ++ if_icmpge Loop; ++ // Falls through ++ } ++ ++} +diff --git a/hotspot/test/compiler/parsing/UnreachableBlockFallsThroughEndOfCode.java b/hotspot/test/compiler/parsing/UnreachableBlockFallsThroughEndOfCode.java +new file mode 100644 +index 00000000..9dfb488d +--- /dev/null ++++ b/hotspot/test/compiler/parsing/UnreachableBlockFallsThroughEndOfCode.java +@@ -0,0 +1,42 @@ ++/* ++ * Copyright (c) 2022, 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 UnreachableBlockFallsThroughEndOfCode.java ++ * @bug 8283441 ++ * @compile Custom.jasm UnreachableBlockFallsThroughEndOfCode.java ++ * @summary Compiling method that falls off the end of the code array ++ * @run main/othervm -XX:TieredStopAtLevel=1 -Xbatch compiler.parsing.UnreachableBlockFallsThroughEndOfCode ++ * @run main/othervm -XX:-TieredCompilation -Xbatch compiler.parsing.UnreachableBlockFallsThroughEndOfCode ++ */ ++ ++package compiler.parsing; ++ ++public class UnreachableBlockFallsThroughEndOfCode { ++ public static void main(String[] strArr) { ++ for (int i = 0; i < 20000; i++) { ++ Custom.test(i); ++ } ++ } ++} +-- +2.25.1 + diff --git a/recipes-core/openjdk/patches-openjdk-8/CVE-2024-20919.patch b/recipes-core/openjdk/patches-openjdk-8/CVE-2024-20919.patch new file mode 100644 index 0000000..a3fc8b6 --- /dev/null +++ b/recipes-core/openjdk/patches-openjdk-8/CVE-2024-20919.patch @@ -0,0 +1,126 @@ +From 77d38b78e4993381ddb113d012b30ab2d7cd9215 Mon Sep 17 00:00:00 2001 +From: Martin Balao <mbalao@openjdk.org> +Date: Fri, 29 Sep 2023 13:01:13 +0000 +Subject: [PATCH] 8314295: Enhance verification of verifier + +Reviewed-by: yan, andrew +Backport-of: 08980a0a60bc48c17eacd57fd2d7065ac2d986a8 + +Upstream-Status: Backport [https://github.com/openjdk/jdk8u/commit/77d38b78e4993381ddb113d012b30ab2d7cd9215] +CVE: CVE-2024-20919 +Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com> +--- + hotspot/src/share/vm/classfile/verifier.cpp | 5 +++-- + .../src/share/vm/interpreter/bytecodes.cpp | 22 ++++++++++++++----- + jdk/src/share/native/common/check_code.c | 11 ++++++---- + 3 files changed, 26 insertions(+), 12 deletions(-) + +diff --git a/hotspot/src/share/vm/classfile/verifier.cpp b/hotspot/src/share/vm/classfile/verifier.cpp +index 2a572058..3a3e04ba 100644 +--- a/hotspot/src/share/vm/classfile/verifier.cpp ++++ b/hotspot/src/share/vm/classfile/verifier.cpp +@@ -2078,11 +2078,12 @@ void ClassVerifier::verify_switch( + "low must be less than or equal to high in tableswitch"); + return; + } +- keys = high - low + 1; +- if (keys < 0) { ++ int64_t keys64 = ((int64_t)high - low) + 1; ++ if (keys64 > 65535) { // Max code length + verify_error(ErrorContext::bad_code(bci), "too many keys in tableswitch"); + return; + } ++ keys = (int)keys64; + delta = 1; + } else { + keys = (int)Bytes::get_Java_u4(aligned_bcp + jintSize); +diff --git a/hotspot/src/share/vm/interpreter/bytecodes.cpp b/hotspot/src/share/vm/interpreter/bytecodes.cpp +index 24adc4d9..f0753735 100644 +--- a/hotspot/src/share/vm/interpreter/bytecodes.cpp ++++ b/hotspot/src/share/vm/interpreter/bytecodes.cpp +@@ -111,12 +111,18 @@ int Bytecodes::special_length_at(Bytecodes::Code code, address bcp, address end) + if (end != NULL && aligned_bcp + 3*jintSize >= end) { + return -1; // don't read past end of code buffer + } ++ // Promote calculation to signed 64 bits to do range checks, used by the verifier. + jlong lo = (jint)Bytes::get_Java_u4(aligned_bcp + 1*jintSize); + jlong hi = (jint)Bytes::get_Java_u4(aligned_bcp + 2*jintSize); + jlong len = (aligned_bcp - bcp) + (3 + hi - lo + 1)*jintSize; +- // only return len if it can be represented as a positive int; +- // return -1 otherwise +- return (len > 0 && len == (int)len) ? len : -1; ++ // Only return len if it can be represented as a positive int and lo <= hi. ++ // The caller checks for bytecode stream overflow. ++ if (lo <= hi && len == (int)len) { ++ assert(len > 0, "must be"); ++ return (int)len; ++ } else { ++ return -1; ++ } + } + + case _lookupswitch: // fall through +@@ -128,9 +134,13 @@ int Bytecodes::special_length_at(Bytecodes::Code code, address bcp, address end) + } + jlong npairs = (jint)Bytes::get_Java_u4(aligned_bcp + jintSize); + jlong len = (aligned_bcp - bcp) + (2 + 2*npairs)*jintSize; +- // only return len if it can be represented as a positive int; +- // return -1 otherwise +- return (len > 0 && len == (int)len) ? len : -1; ++ // Only return len if it can be represented as a positive int and npairs >= 0. ++ if (npairs >= 0 && len == (int)len) { ++ assert(len > 0, "must be"); ++ return (int)len; ++ } else { ++ return -1; ++ } + } + } + // Note: Length functions must return <=0 for invalid bytecodes. +diff --git a/jdk/src/share/native/common/check_code.c b/jdk/src/share/native/common/check_code.c +index 96091720..491b95cd 100644 +--- a/jdk/src/share/native/common/check_code.c ++++ b/jdk/src/share/native/common/check_code.c +@@ -1,5 +1,5 @@ + /* +- * Copyright (c) 1994, 2014, Oracle and/or its affiliates. All rights reserved. ++ * Copyright (c) 1994, 2023, 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 +@@ -84,6 +84,7 @@ + #include <assert.h> + #include <limits.h> + #include <stdlib.h> ++#include <stdint.h> + + #include "jni.h" + #include "jvm.h" +@@ -1202,7 +1203,7 @@ verify_opcode_operands(context_type *context, unsigned int inumber, int offset) + } + } + if (opcode == JVM_OPC_tableswitch) { +- keys = _ck_ntohl(lpc[2]) - _ck_ntohl(lpc[1]) + 1; ++ keys = _ck_ntohl(lpc[2]) - _ck_ntohl(lpc[1]) + 1; + delta = 1; + } else { + keys = _ck_ntohl(lpc[1]); /* number of pairs */ +@@ -1682,11 +1683,13 @@ static int instruction_length(unsigned char *iptr, unsigned char *end) + switch (instruction) { + case JVM_OPC_tableswitch: { + int *lpc = (int *)UCALIGN(iptr + 1); +- int index; + if (lpc + 2 >= (int *)end) { + return -1; /* do not read pass the end */ + } +- index = _ck_ntohl(lpc[2]) - _ck_ntohl(lpc[1]); ++ int64_t low = _ck_ntohl(lpc[1]); ++ int64_t high = _ck_ntohl(lpc[2]); ++ int64_t index = high - low; ++ // The value of low must be less than or equal to high - i.e. index >= 0 + if ((index < 0) || (index > 65535)) { + return -1; /* illegal */ + } else { +-- +2.25.1 + diff --git a/recipes-core/openjdk/patches-openjdk-8/CVE-2024-20921.patch b/recipes-core/openjdk/patches-openjdk-8/CVE-2024-20921.patch new file mode 100644 index 0000000..edce6f0 --- /dev/null +++ b/recipes-core/openjdk/patches-openjdk-8/CVE-2024-20921.patch @@ -0,0 +1,657 @@ +From f7cb28de01117f598ecf48ad58e277c3a4590436 Mon Sep 17 00:00:00 2001 +From: Roland Westrelin <rwestrel@redhat.com> +Date: Tue, 7 Nov 2023 11:08:30 +0000 +Subject: [PATCH] 8314307: Improve loop handling + +Reviewed-by: mbalao, fferrari, andrew +Backport-of: 62ac93d145ca9fa1ab0c040533c62c42c202703a + +Upstream-Status: Backport [https://github.com/openjdk/jdk8u/commit/f7cb28de01117f598ecf48ad58e277c3a4590436] +CVE: CVE-2024-20921 +Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com> +--- + hotspot/src/share/vm/opto/ifnode.cpp | 59 +++- + hotspot/src/share/vm/opto/loopnode.cpp | 422 ++++++++++++++++++++----- + hotspot/src/share/vm/opto/loopnode.hpp | 4 + + hotspot/src/share/vm/opto/phaseX.hpp | 6 +- + 4 files changed, 404 insertions(+), 87 deletions(-) + +diff --git a/hotspot/src/share/vm/opto/ifnode.cpp b/hotspot/src/share/vm/opto/ifnode.cpp +index 68f068d0..51579032 100644 +--- a/hotspot/src/share/vm/opto/ifnode.cpp ++++ b/hotspot/src/share/vm/opto/ifnode.cpp +@@ -882,6 +882,46 @@ Node *IfNode::Ideal(PhaseGVN *phase, bool can_reshape) { + // then we are guaranteed to fail, so just start interpreting there. + // We 'expand' the top 3 range checks to include all post-dominating + // checks. ++ // ++ // Example: ++ // a[i+x] // (1) 1 < x < 6 ++ // a[i+3] // (2) ++ // a[i+4] // (3) ++ // a[i+6] // max = max of all constants ++ // a[i+2] ++ // a[i+1] // min = min of all constants ++ // ++ // If x < 3: ++ // (1) a[i+x]: Leave unchanged ++ // (2) a[i+3]: Replace with a[i+max] = a[i+6]: i+x < i+3 <= i+6 -> (2) is covered ++ // (3) a[i+4]: Replace with a[i+min] = a[i+1]: i+1 < i+4 <= i+6 -> (3) and all following checks are covered ++ // Remove all other a[i+c] checks ++ // ++ // If x >= 3: ++ // (1) a[i+x]: Leave unchanged ++ // (2) a[i+3]: Replace with a[i+min] = a[i+1]: i+1 < i+3 <= i+x -> (2) is covered ++ // (3) a[i+4]: Replace with a[i+max] = a[i+6]: i+1 < i+4 <= i+6 -> (3) and all following checks are covered ++ // Remove all other a[i+c] checks ++ // ++ // We only need the top 2 range checks if x is the min or max of all constants. ++ // ++ // This, however, only works if the interval [i+min,i+max] is not larger than max_int (i.e. abs(max - min) < max_int): ++ // The theoretical max size of an array is max_int with: ++ // - Valid index space: [0,max_int-1] ++ // - Invalid index space: [max_int,-1] // max_int, min_int, min_int - 1 ..., -1 ++ // ++ // The size of the consecutive valid index space is smaller than the size of the consecutive invalid index space. ++ // If we choose min and max in such a way that: ++ // - abs(max - min) < max_int ++ // - i+max and i+min are inside the valid index space ++ // then all indices [i+min,i+max] must be in the valid index space. Otherwise, the invalid index space must be ++ // smaller than the valid index space which is never the case for any array size. ++ // ++ // Choosing a smaller array size only makes the valid index space smaller and the invalid index space larger and ++ // the argument above still holds. ++ // ++ // Note that the same optimization with the same maximal accepted interval size can also be found in C1. ++ const jlong maximum_number_of_min_max_interval_indices = (jlong)max_jint; + + // The top 3 range checks seen + const int NRC =3; +@@ -915,13 +955,18 @@ Node *IfNode::Ideal(PhaseGVN *phase, bool can_reshape) { + found_immediate_dominator = true; + break; + } +- // Gather expanded bounds +- off_lo = MIN2(off_lo,offset2); +- off_hi = MAX2(off_hi,offset2); +- // Record top NRC range checks +- prev_checks[nb_checks%NRC].ctl = prev_dom; +- prev_checks[nb_checks%NRC].off = offset2; +- nb_checks++; ++ ++ // "x - y" -> must add one to the difference for number of elements in [x,y] ++ const jlong diff = (jlong)MIN2(offset2, off_lo) - (jlong)MAX2(offset2, off_hi); ++ if (ABS(diff) < maximum_number_of_min_max_interval_indices) { ++ // Gather expanded bounds ++ off_lo = MIN2(off_lo, offset2); ++ off_hi = MAX2(off_hi, offset2); ++ // Record top NRC range checks ++ prev_checks[nb_checks % NRC].ctl = prev_dom; ++ prev_checks[nb_checks % NRC].off = offset2; ++ nb_checks++; ++ } + } + } + prev_dom = dom; +diff --git a/hotspot/src/share/vm/opto/loopnode.cpp b/hotspot/src/share/vm/opto/loopnode.cpp +index b2d5dccd..8bc9dd29 100644 +--- a/hotspot/src/share/vm/opto/loopnode.cpp ++++ b/hotspot/src/share/vm/opto/loopnode.cpp +@@ -260,6 +260,49 @@ void PhaseIdealLoop::set_subtree_ctrl( Node *n ) { + set_early_ctrl( n ); + } + ++void PhaseIdealLoop::insert_loop_limit_check(ProjNode* limit_check_proj, Node* cmp_limit, Node* bol) { ++ Node* new_predicate_proj = create_new_if_for_predicate(limit_check_proj, NULL, ++ Deoptimization::Reason_loop_limit_check); ++ Node* iff = new_predicate_proj->in(0); ++ assert(iff->Opcode() == Op_If, "bad graph shape"); ++ Node* conv = iff->in(1); ++ assert(conv->Opcode() == Op_Conv2B, "bad graph shape"); ++ Node* opaq = conv->in(1); ++ assert(opaq->Opcode() == Op_Opaque1, "bad graph shape"); ++ cmp_limit = _igvn.register_new_node_with_optimizer(cmp_limit); ++ bol = _igvn.register_new_node_with_optimizer(bol); ++ set_subtree_ctrl(bol); ++ _igvn.replace_input_of(iff, 1, bol); ++ ++#ifndef PRODUCT ++ // report that the loop predication has been actually performed ++ // for this loop ++ if (TraceLoopLimitCheck) { ++ tty->print_cr("Counted Loop Limit Check generated:"); ++ debug_only( bol->dump(2); ) ++ } ++#endif ++} ++ ++static int check_stride_overflow(jlong final_correction, const TypeInt* limit_t) { ++ if (final_correction > 0) { ++ if (limit_t->_lo > (max_jint - final_correction)) { ++ return -1; ++ } ++ if (limit_t->_hi > (max_jint - final_correction)) { ++ return 1; ++ } ++ } else { ++ if (limit_t->_hi < (min_jint - final_correction)) { ++ return -1; ++ } ++ if (limit_t->_lo < (min_jint - final_correction)) { ++ return 1; ++ } ++ } ++ return 0; ++} ++ + //------------------------------is_counted_loop-------------------------------- + bool PhaseIdealLoop::is_counted_loop( Node *x, IdealLoopTree *loop ) { + PhaseGVN *gvn = &_igvn; +@@ -463,51 +506,256 @@ bool PhaseIdealLoop::is_counted_loop( Node *x, IdealLoopTree *loop ) { + assert(x->Opcode() == Op_Loop, "regular loops only"); + C->print_method(PHASE_BEFORE_CLOOPS, 3); + +- Node *hook = new (C) Node(6); ++ Node* adjusted_limit = limit; + + if (LoopLimitCheck) { + + // =================================================== +- // Generate loop limit check to avoid integer overflow +- // in cases like next (cyclic loops): ++ // We can only convert this loop to a counted loop if we can guarantee that the iv phi will never overflow at runtime. ++ // This is an implicit assumption taken by some loop optimizations. We therefore must ensure this property at all cost. ++ // At this point, we've already excluded some trivial cases where an overflow could have been proven statically. ++ // But even though we cannot prove that an overflow will *not* happen, we still want to speculatively convert this loop ++ // to a counted loop. This can be achieved by adding additional iv phi overflow checks before the loop. If they fail, ++ // we trap and resume execution before the loop without having executed any iteration of the loop, yet. + // +- // for (i=0; i <= max_jint; i++) {} +- // for (i=0; i < max_jint; i+=2) {} ++ // These additional iv phi overflow checks can be inserted as Loop Limit Check Predicates above the Loop Limit Check ++ // Parse Predicate which captures a JVM state just before the entry of the loop. If there is no such Parse Predicate, ++ // we cannot generate a Loop Limit Check Predicate and thus cannot speculatively convert the loop to a counted loop. + // ++ // In the following, we only focus on int loops with stride > 0 to keep things simple. The argumentation and proof ++ // for stride < 0 is analogously. For long loops, we would replace max_int with max_long. + // +- // Limit check predicate depends on the loop test: + // +- // for(;i != limit; i++) --> limit <= (max_jint) +- // for(;i < limit; i+=stride) --> limit <= (max_jint - stride + 1) +- // for(;i <= limit; i+=stride) --> limit <= (max_jint - stride ) ++ // The loop to be converted does not always need to have the often used shape: + // ++ // i = init ++ // i = init loop: ++ // do { ... ++ // // ... equivalent i+=stride ++ // i+=stride <==> if (i < limit) ++ // } while (i < limit); goto loop ++ // exit: ++ // ... ++ // ++ // where the loop exit check uses the post-incremented iv phi and a '<'-operator. ++ // ++ // We could also have '<='-operator (or '>='-operator for negative strides) or use the pre-incremented iv phi value ++ // in the loop exit check: ++ // ++ // i = init ++ // loop: ++ // ... ++ // if (i <= limit) ++ // i+=stride ++ // goto loop ++ // exit: ++ // ... ++ // ++ // Let's define the following terms: ++ // - iv_pre_i: The pre-incremented iv phi before the i-th iteration. ++ // - iv_post_i: The post-incremented iv phi after the i-th iteration. ++ // ++ // The iv_pre_i and iv_post_i have the following relation: ++ // iv_pre_i + stride = iv_post_i ++ // ++ // When converting a loop to a counted loop, we want to have a canonicalized loop exit check of the form: ++ // iv_post_i < adjusted_limit ++ // ++ // If that is not the case, we need to canonicalize the loop exit check by using different values for adjusted_limit: ++ // (LE1) iv_post_i < limit: Already canonicalized. We can directly use limit as adjusted_limit. ++ // -> adjusted_limit = limit. ++ // (LE2) iv_post_i <= limit: ++ // iv_post_i < limit + 1 ++ // -> adjusted limit = limit + 1 ++ // (LE3) iv_pre_i < limit: ++ // iv_pre_i + stride < limit + stride ++ // iv_post_i < limit + stride ++ // -> adjusted_limit = limit + stride ++ // (LE4) iv_pre_i <= limit: ++ // iv_pre_i < limit + 1 ++ // iv_pre_i + stride < limit + stride + 1 ++ // iv_post_i < limit + stride + 1 ++ // -> adjusted_limit = limit + stride + 1 ++ // ++ // Note that: ++ // (AL) limit <= adjusted_limit. ++ // ++ // The following loop invariant has to hold for counted loops with n iterations (i.e. loop exit check true after n-th ++ // loop iteration) and a canonicalized loop exit check to guarantee that no iv_post_i over- or underflows: ++ // (INV) For i = 1..n, min_int <= iv_post_i <= max_int ++ // ++ // To prove (INV), we require the following two conditions/assumptions: ++ // (i): adjusted_limit - 1 + stride <= max_int ++ // (ii): init < limit ++ // ++ // If we can prove (INV), we know that there can be no over- or underflow of any iv phi value. We prove (INV) by ++ // induction by assuming (i) and (ii). ++ // ++ // Proof by Induction ++ // ------------------ ++ // > Base case (i = 1): We show that (INV) holds after the first iteration: ++ // min_int <= iv_post_1 = init + stride <= max_int ++ // Proof: ++ // First, we note that (ii) implies ++ // (iii) init <= limit - 1 ++ // max_int >= adjusted_limit - 1 + stride [using (i)] ++ // >= limit - 1 + stride [using (AL)] ++ // >= init + stride [using (iii)] ++ // >= min_int [using stride > 0, no underflow] ++ // Thus, no overflow happens after the first iteration and (INV) holds for i = 1. ++ // ++ // Note that to prove the base case we need (i) and (ii). ++ // ++ // > Induction Hypothesis (i = j, j > 1): Assume that (INV) holds after the j-th iteration: ++ // min_int <= iv_post_j <= max_int ++ // > Step case (i = j + 1): We show that (INV) also holds after the j+1-th iteration: ++ // min_int <= iv_post_{j+1} = iv_post_j + stride <= max_int ++ // Proof: ++ // If iv_post_j >= adjusted_limit: ++ // We exit the loop after the j-th iteration, and we don't execute the j+1-th iteration anymore. Thus, there is ++ // also no iv_{j+1}. Since (INV) holds for iv_j, there is nothing left to prove. ++ // If iv_post_j < adjusted_limit: ++ // First, we note that: ++ // (iv) iv_post_j <= adjusted_limit - 1 ++ // max_int >= adjusted_limit - 1 + stride [using (i)] ++ // >= iv_post_j + stride [using (iv)] ++ // >= min_int [using stride > 0, no underflow] ++ // ++ // Note that to prove the step case we only need (i). ++ // ++ // Thus, by assuming (i) and (ii), we proved (INV). ++ // ++ // ++ // It is therefore enough to add the following two Loop Limit Check Predicates to check assumptions (i) and (ii): ++ // ++ // (1) Loop Limit Check Predicate for (i): ++ // Using (i): adjusted_limit - 1 + stride <= max_int ++ // ++ // This condition is now restated to use limit instead of adjusted_limit: ++ // ++ // To prevent an overflow of adjusted_limit -1 + stride itself, we rewrite this check to ++ // max_int - stride + 1 >= adjusted_limit ++ // We can merge the two constants into ++ // canonicalized_correction = stride - 1 ++ // which gives us ++ // max_int - canonicalized_correction >= adjusted_limit ++ // ++ // To directly use limit instead of adjusted_limit in the predicate condition, we split adjusted_limit into: ++ // adjusted_limit = limit + limit_correction ++ // Since stride > 0 and limit_correction <= stride + 1, we can restate this with no over- or underflow into: ++ // max_int - canonicalized_correction - limit_correction >= limit ++ // Since canonicalized_correction and limit_correction are both constants, we can replace them with a new constant: ++ // final_correction = canonicalized_correction + limit_correction ++ // which gives us: ++ // ++ // Final predicate condition: ++ // max_int - final_correction >= limit ++ // ++ // (2) Loop Limit Check Predicate for (ii): ++ // Using (ii): init < limit ++ // ++ // This Loop Limit Check Predicate is not required if we can prove at compile time that either: ++ // (2.1) type(init) < type(limit) ++ // In this case, we know: ++ // all possible values of init < all possible values of limit ++ // and we can skip the predicate. ++ // ++ // (2.2) init < limit is already checked before (i.e. found as a dominating check) ++ // In this case, we do not need to re-check the condition and can skip the predicate. ++ // This is often found for while- and for-loops which have the following shape: ++ // ++ // if (init < limit) { // Dominating test. Do not need the Loop Limit Check Predicate below. ++ // i = init; ++ // if (init >= limit) { trap(); } // Here we would insert the Loop Limit Check Predicate ++ // do { ++ // i += stride; ++ // } while (i < limit); ++ // } ++ // ++ // (2.3) init + stride <= max_int ++ // In this case, there is no overflow of the iv phi after the first loop iteration. ++ // In the proof of the base case above we showed that init + stride <= max_int by using assumption (ii): ++ // init < limit ++ // In the proof of the step case above, we did not need (ii) anymore. Therefore, if we already know at ++ // compile time that init + stride <= max_int then we have trivially proven the base case and that ++ // there is no overflow of the iv phi after the first iteration. In this case, we don't need to check (ii) ++ // again and can skip the predicate. ++ ++ ++ // Accounting for (LE3) and (LE4) where we use pre-incremented phis in the loop exit check. ++ const jlong limit_correction_for_pre_iv_exit_check = (phi_incr != NULL) ? stride_con : 0; ++ ++ // Accounting for (LE2) and (LE4) where we use <= or >= in the loop exit check. ++ const bool includes_limit = (bt == BoolTest::le || bt == BoolTest::ge); ++ const jlong limit_correction_for_le_ge_exit_check = (includes_limit ? (stride_con > 0 ? 1 : -1) : 0); ++ ++ const jlong limit_correction = limit_correction_for_pre_iv_exit_check + limit_correction_for_le_ge_exit_check; ++ const jlong canonicalized_correction = stride_con + (stride_con > 0 ? -1 : 1); ++ const jlong final_correction = canonicalized_correction + limit_correction; ++ ++ int sov = check_stride_overflow(final_correction, limit_t); ++ ++ // If sov==0, limit's type always satisfies the condition, for ++ // example, when it is an array length. ++ if (sov != 0) { ++ if (sov < 0) { ++ return false; // Bailout: integer overflow is certain. ++ } ++ // (1) Loop Limit Check Predicate is required because we could not statically prove that ++ // limit + final_correction = adjusted_limit - 1 + stride <= max_int ++ ProjNode *limit_check_proj = find_predicate_insertion_point(init_control, Deoptimization::Reason_loop_limit_check); ++ if (!limit_check_proj) { ++ // The Loop Limit Check Parse Predicate is not generated if this method trapped here before. ++#ifdef ASSERT ++ if (TraceLoopLimitCheck) { ++ tty->print("missing loop limit check:"); ++ loop->dump_head(); ++ x->dump(1); ++ } ++#endif ++ return false; ++ } + +- // Check if limit is excluded to do more precise int overflow check. +- bool incl_limit = (bt == BoolTest::le || bt == BoolTest::ge); +- int stride_m = stride_con - (incl_limit ? 0 : (stride_con > 0 ? 1 : -1)); +- +- // If compare points directly to the phi we need to adjust +- // the compare so that it points to the incr. Limit have +- // to be adjusted to keep trip count the same and the +- // adjusted limit should be checked for int overflow. +- if (phi_incr != NULL) { +- stride_m += stride_con; +- } ++ IfNode* check_iff = limit_check_proj->in(0)->as_If(); + +- if (limit->is_Con()) { +- int limit_con = limit->get_int(); +- if ((stride_con > 0 && limit_con > (max_jint - stride_m)) || +- (stride_con < 0 && limit_con < (min_jint - stride_m))) { +- // Bailout: it could be integer overflow. ++ if (!is_dominator(get_ctrl(limit), check_iff->in(0))) { + return false; + } +- } else if ((stride_con > 0 && limit_t->_hi <= (max_jint - stride_m)) || +- (stride_con < 0 && limit_t->_lo >= (min_jint - stride_m))) { +- // Limit's type may satisfy the condition, for example, +- // when it is an array length. +- } else { +- // Generate loop's limit check. +- // Loop limit check predicate should be near the loop. ++ ++ Node* cmp_limit; ++ Node* bol; ++ ++ if (stride_con > 0) { ++ cmp_limit = new (C) CmpINode(limit, _igvn.intcon(max_jint - final_correction)); ++ bol = new (C) BoolNode(cmp_limit, BoolTest::le); ++ } else { ++ cmp_limit = new (C) CmpINode(limit, _igvn.intcon(min_jint - final_correction)); ++ bol = new (C) BoolNode(cmp_limit, BoolTest::ge); ++ } ++ ++ insert_loop_limit_check(limit_check_proj, cmp_limit, bol); ++ } ++ ++ // (2.3) ++ const bool init_plus_stride_could_overflow = ++ (stride_con > 0 && init_t->_hi > max_jint - stride_con) || ++ (stride_con < 0 && init_t->_lo < min_jint - stride_con); ++ // (2.1) ++ const bool init_gte_limit = (stride_con > 0 && init_t->_hi >= limit_t->_lo) || ++ (stride_con < 0 && init_t->_lo <= limit_t->_hi); ++ ++ if (init_gte_limit && // (2.1) ++ ((bt == BoolTest::ne || init_plus_stride_could_overflow) && // (2.3) ++ !has_dominating_loop_limit_check(init_trip, limit, stride_con, init_control))) { // (2.2) ++ // (2) Iteration Loop Limit Check Predicate is required because neither (2.1), (2.2), nor (2.3) holds. ++ // We use the following condition: ++ // - stride > 0: init < limit ++ // - stride < 0: init > limit ++ // ++ // This predicate is always required if we have a non-equal-operator in the loop exit check (where stride = 1 is ++ // a requirement). We transform the loop exit check by using a less-than-operator. By doing so, we must always ++ // check that init < limit. Otherwise, we could have a different number of iterations at runtime. ++ + ProjNode *limit_check_proj = find_predicate_insertion_point(init_control, Deoptimization::Reason_loop_limit_check); + if (!limit_check_proj) { + // The limit check predicate is not generated if this method trapped here before. +@@ -520,41 +768,38 @@ bool PhaseIdealLoop::is_counted_loop( Node *x, IdealLoopTree *loop ) { + #endif + return false; + } +- + IfNode* check_iff = limit_check_proj->in(0)->as_If(); ++ ++ if (!is_dominator(get_ctrl(limit), check_iff->in(0)) || ++ !is_dominator(get_ctrl(init_trip), check_iff->in(0))) { ++ return false; ++ } ++ + Node* cmp_limit; + Node* bol; + + if (stride_con > 0) { +- cmp_limit = new (C) CmpINode(limit, _igvn.intcon(max_jint - stride_m)); +- bol = new (C) BoolNode(cmp_limit, BoolTest::le); ++ cmp_limit = new (C) CmpINode(init_trip, limit); ++ bol = new (C) BoolNode(cmp_limit, BoolTest::lt); + } else { +- cmp_limit = new (C) CmpINode(limit, _igvn.intcon(min_jint - stride_m)); +- bol = new (C) BoolNode(cmp_limit, BoolTest::ge); ++ cmp_limit = new (C) CmpINode(init_trip, limit); ++ bol = new (C) BoolNode(cmp_limit, BoolTest::gt); + } +- cmp_limit = _igvn.register_new_node_with_optimizer(cmp_limit); +- bol = _igvn.register_new_node_with_optimizer(bol); +- set_subtree_ctrl(bol); +- +- // Replace condition in original predicate but preserve Opaque node +- // so that previous predicates could be found. +- assert(check_iff->in(1)->Opcode() == Op_Conv2B && +- check_iff->in(1)->in(1)->Opcode() == Op_Opaque1, ""); +- Node* opq = check_iff->in(1)->in(1); +- _igvn.hash_delete(opq); +- opq->set_req(1, bol); +- // Update ctrl. +- set_ctrl(opq, check_iff->in(0)); +- set_ctrl(check_iff->in(1), check_iff->in(0)); + +-#ifndef PRODUCT +- // report that the loop predication has been actually performed +- // for this loop +- if (TraceLoopLimitCheck) { +- tty->print_cr("Counted Loop Limit Check generated:"); +- debug_only( bol->dump(2); ) ++ insert_loop_limit_check(limit_check_proj, cmp_limit, bol); ++ } ++ ++ if (bt == BoolTest::ne) { ++ // Now we need to canonicalize the loop condition if it is 'ne'. ++ assert(stride_con == 1 || stride_con == -1, "simple increment only - checked before"); ++ if (stride_con > 0) { ++ // 'ne' can be replaced with 'lt' only when init < limit. This is ensured by the inserted predicate above. ++ bt = BoolTest::lt; ++ } else { ++ assert(stride_con < 0, "must be"); ++ // 'ne' can be replaced with 'gt' only when init > limit. This is ensured by the inserted predicate above. ++ bt = BoolTest::gt; + } +-#endif + } + + if (phi_incr != NULL) { +@@ -567,26 +812,15 @@ bool PhaseIdealLoop::is_counted_loop( Node *x, IdealLoopTree *loop ) { + // is converted to + // i = init; do {} while(++i < limit+1); + // +- limit = gvn->transform(new (C) AddINode(limit, stride)); +- } +- +- // Now we need to canonicalize loop condition. +- if (bt == BoolTest::ne) { +- assert(stride_con == 1 || stride_con == -1, "simple increment only"); +- // 'ne' can be replaced with 'lt' only when init < limit. +- if (stride_con > 0 && init_t->_hi < limit_t->_lo) +- bt = BoolTest::lt; +- // 'ne' can be replaced with 'gt' only when init > limit. +- if (stride_con < 0 && init_t->_lo > limit_t->_hi) +- bt = BoolTest::gt; ++ adjusted_limit = gvn->transform(new (C) AddINode(limit, stride)); + } + +- if (incl_limit) { ++ if (includes_limit) { + // The limit check guaranties that 'limit <= (max_jint - stride)' so + // we can convert 'i <= limit' to 'i < limit+1' since stride != 0. + // + Node* one = (stride_con > 0) ? gvn->intcon( 1) : gvn->intcon(-1); +- limit = gvn->transform(new (C) AddINode(limit, one)); ++ adjusted_limit = gvn->transform(new (C) AddINode(adjusted_limit, one)); + if (bt == BoolTest::le) + bt = BoolTest::lt; + else if (bt == BoolTest::ge) +@@ -594,10 +828,11 @@ bool PhaseIdealLoop::is_counted_loop( Node *x, IdealLoopTree *loop ) { + else + ShouldNotReachHere(); + } +- set_subtree_ctrl( limit ); ++ set_subtree_ctrl(adjusted_limit); + + } else { // LoopLimitCheck + ++ Node *hook = new (C) Node(6); + // If compare points to incr, we are ok. Otherwise the compare + // can directly point to the phi; in this case adjust the compare so that + // it points to the incr by adjusting the limit. +@@ -691,6 +926,11 @@ bool PhaseIdealLoop::is_counted_loop( Node *x, IdealLoopTree *loop ) { + limit = gvn->transform(new (C) AddINode(span,init_trip)); + set_subtree_ctrl( limit ); + ++ adjusted_limit = limit; ++ ++ // Free up intermediate goo ++ _igvn.remove_dead_node(hook); ++ + } // LoopLimitCheck + + if (!UseCountedLoopSafepoints) { +@@ -728,7 +968,7 @@ bool PhaseIdealLoop::is_counted_loop( Node *x, IdealLoopTree *loop ) { + } + cmp = cmp->clone(); + cmp->set_req(1,incr); +- cmp->set_req(2,limit); ++ cmp->set_req(2, adjusted_limit); + cmp = _igvn.register_new_node_with_optimizer(cmp); + set_ctrl(cmp, iff->in(0)); + +@@ -802,9 +1042,6 @@ bool PhaseIdealLoop::is_counted_loop( Node *x, IdealLoopTree *loop ) { + } + } + +- // Free up intermediate goo +- _igvn.remove_dead_node(hook); +- + #ifdef ASSERT + assert(l->is_valid_counted_loop(), "counted loop shape is messed up"); + assert(l == loop->_head && l->phi() == phi && l->loopexit() == lex, "" ); +@@ -821,6 +1058,37 @@ bool PhaseIdealLoop::is_counted_loop( Node *x, IdealLoopTree *loop ) { + return true; + } + ++// Check if there is a dominating loop limit check of the form 'init < limit' starting at the loop entry. ++// If there is one, then we do not need to create an additional Loop Limit Check Predicate. ++bool PhaseIdealLoop::has_dominating_loop_limit_check(Node* init_trip, Node* limit, const int stride_con, ++ Node* loop_entry) { ++ // Eagerly call transform() on the Cmp and Bool node to common them up if possible. This is required in order to ++ // successfully find a dominated test with the If node below. ++ Node* cmp_limit; ++ Node* bol; ++ if (stride_con > 0) { ++ cmp_limit = _igvn.transform(new (C) CmpINode(init_trip, limit)); ++ bol = _igvn.transform(new (C) BoolNode(cmp_limit, BoolTest::lt)); ++ } else { ++ cmp_limit = _igvn.transform(new (C) CmpINode(init_trip, limit)); ++ bol = _igvn.transform(new (C) BoolNode(cmp_limit, BoolTest::gt)); ++ } ++ ++ // Check if there is already a dominating init < limit check. If so, we do not need a Loop Limit Check Predicate. ++ IfNode* iff = new (C) IfNode(loop_entry, bol, PROB_MIN, COUNT_UNKNOWN); ++ // Also add fake IfProj nodes in order to call transform() on the newly created IfNode. ++ IfFalseNode* if_false = new (C) IfFalseNode(iff); ++ IfTrueNode* if_true = new (C) IfTrueNode(iff); ++ Node* dominated_iff = _igvn.transform(iff); ++ // ConI node? Found dominating test (IfNode::dominated_by() returns a ConI node). ++ const bool found_dominating_test = dominated_iff != NULL && dominated_iff->Opcode() == Op_ConI; ++ ++ // Kill the If with its projections again in the next IGVN round by cutting it off from the graph. ++ _igvn.replace_input_of(iff, 0, C->top()); ++ _igvn.replace_input_of(iff, 1, C->top()); ++ return found_dominating_test; ++} ++ + //----------------------exact_limit------------------------------------------- + Node* PhaseIdealLoop::exact_limit( IdealLoopTree *loop ) { + assert(loop->_head->is_CountedLoop(), ""); +diff --git a/hotspot/src/share/vm/opto/loopnode.hpp b/hotspot/src/share/vm/opto/loopnode.hpp +index 55a7def3..3ae41f8d 100644 +--- a/hotspot/src/share/vm/opto/loopnode.hpp ++++ b/hotspot/src/share/vm/opto/loopnode.hpp +@@ -896,6 +896,10 @@ public: + // Create a new if above the uncommon_trap_if_pattern for the predicate to be promoted + ProjNode* create_new_if_for_predicate(ProjNode* cont_proj, Node* new_entry, + Deoptimization::DeoptReason reason); ++ void insert_loop_limit_check(ProjNode* limit_check_proj, Node* cmp_limit, Node* bol); ++ bool has_dominating_loop_limit_check(Node* init_trip, Node* limit, int stride_con, ++ Node* loop_entry); ++ + void register_control(Node* n, IdealLoopTree *loop, Node* pred); + + // Clone loop predicates to cloned loops (peeled, unswitched) +diff --git a/hotspot/src/share/vm/opto/phaseX.hpp b/hotspot/src/share/vm/opto/phaseX.hpp +index a2a2a538..bd7393ac 100644 +--- a/hotspot/src/share/vm/opto/phaseX.hpp ++++ b/hotspot/src/share/vm/opto/phaseX.hpp +@@ -431,9 +431,6 @@ private: + + protected: + +- // Idealize new Node 'n' with respect to its inputs and its value +- virtual Node *transform( Node *a_node ); +- + // Warm up hash table, type table and initial worklist + void init_worklist( Node *a_root ); + +@@ -447,6 +444,9 @@ public: + PhaseIterGVN( PhaseGVN *gvn ); // Used after Parser + PhaseIterGVN( PhaseIterGVN *igvn, const char *dummy ); // Used after +VerifyOpto + ++ // Idealize new Node 'n' with respect to its inputs and its value ++ virtual Node *transform( Node *a_node ); ++ + virtual PhaseIterGVN *is_IterGVN() { return this; } + + Unique_Node_List _worklist; // Iterative worklist +-- +2.25.1 +
Backport fixes for: * CVE-2022-40433 - Upstream-Status: Backport from https://github.com/openjdk/jdk8u/commit/961ab463974b7d05600b826303f9111c4f367a04 * CVE-2024-20919 - Upstream-Status: Backport from https://github.com/openjdk/jdk8u/commit/77d38b78e4993381ddb113d012b30ab2d7cd9215 * CVE-2024-20921 - Upstream-Status: Backport from https://github.com/openjdk/jdk8u/commit/f7cb28de01117f598ecf48ad58e277c3a4590436 Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com> --- .../openjdk/openjdk-8-release-common.inc | 3 + .../patches-openjdk-8/CVE-2022-40433.patch | 233 +++++++ .../patches-openjdk-8/CVE-2024-20919.patch | 126 ++++ .../patches-openjdk-8/CVE-2024-20921.patch | 657 ++++++++++++++++++ 4 files changed, 1019 insertions(+) create mode 100644 recipes-core/openjdk/patches-openjdk-8/CVE-2022-40433.patch create mode 100644 recipes-core/openjdk/patches-openjdk-8/CVE-2024-20919.patch create mode 100644 recipes-core/openjdk/patches-openjdk-8/CVE-2024-20921.patch