diff --git a/make/conf/version-numbers.conf b/make/conf/version-numbers.conf index e849eb560ef..d5ba88b6955 100644 --- a/make/conf/version-numbers.conf +++ b/make/conf/version-numbers.conf @@ -39,4 +39,4 @@ DEFAULT_VERSION_CLASSFILE_MINOR=0 DEFAULT_VERSION_DOCS_API_SINCE=11 DEFAULT_ACCEPTABLE_BOOT_VERSIONS="16 17" DEFAULT_JDK_SOURCE_TARGET_VERSION=17 -DEFAULT_PROMOTED_VERSION_PRE=ea +DEFAULT_PROMOTED_VERSION_PRE= diff --git a/src/hotspot/share/classfile/verifier.cpp b/src/hotspot/share/classfile/verifier.cpp index 590cce18ba2..5ad53e9eca1 100644 --- a/src/hotspot/share/classfile/verifier.cpp +++ b/src/hotspot/share/classfile/verifier.cpp @@ -32,6 +32,7 @@ #include "classfile/stackMapTableFormat.hpp" #include "classfile/symbolTable.hpp" #include "classfile/systemDictionary.hpp" +#include "classfile/systemDictionaryShared.hpp" #include "classfile/verifier.hpp" #include "classfile/vmClasses.hpp" #include "classfile/vmSymbols.hpp" @@ -211,6 +212,12 @@ bool Verifier::verify(InstanceKlass* klass, bool should_verify_class, TRAPS) { exception_name == vmSymbols::java_lang_ClassFormatError())) { log_info(verification)("Fail over class verification to old verifier for: %s", klass->external_name()); log_info(class, init)("Fail over class verification to old verifier for: %s", klass->external_name()); + // Exclude any classes that fail over during dynamic dumping + if (CDS_ONLY(DynamicDumpSharedSpaces) NOT_CDS(false)) { + ResourceMark rm; + log_warning(cds)("Skipping %s: Failed over class verification while dynamic dumping", klass->name()->as_C_string()); + SystemDictionaryShared::set_excluded(klass); + } message_buffer = NEW_RESOURCE_ARRAY(char, message_buffer_len); exception_message = message_buffer; exception_name = inference_verify( diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index ce6ecc4d012..a6a6cc0861f 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -2128,7 +2128,7 @@ Node *LoopLimitNode::Ideal(PhaseGVN *phase, bool can_reshape) { const TypeInt* init_t = phase->type(in(Init) )->is_int(); const TypeInt* limit_t = phase->type(in(Limit))->is_int(); - int stride_p; + jlong stride_p; jlong lim, ini; julong max; if (stride_con > 0) { @@ -2137,10 +2137,10 @@ Node *LoopLimitNode::Ideal(PhaseGVN *phase, bool can_reshape) { ini = init_t->_lo; max = (julong)max_jint; } else { - stride_p = -stride_con; + stride_p = -(jlong)stride_con; lim = init_t->_hi; ini = limit_t->_lo; - max = (julong)min_jint; + max = (julong)(juint)min_jint; // double cast to get 0x0000000080000000, not 0xffffffff80000000 } julong range = lim - ini + stride_p; if (range <= max) { diff --git a/src/hotspot/share/opto/superword.cpp b/src/hotspot/share/opto/superword.cpp index db214da125f..779e5b3cbe7 100644 --- a/src/hotspot/share/opto/superword.cpp +++ b/src/hotspot/share/opto/superword.cpp @@ -3742,6 +3742,10 @@ SWPointer::SWPointer(MemNode* mem, SuperWord* slp, Node_Stack *nstack, bool anal _mem(mem), _slp(slp), _base(nullptr), _adr(nullptr), _scale(0), _offset(0), _invar(nullptr), _negate_invar(false), _invar_scale(nullptr), + _has_int_index_after_convI2L(false), + _int_index_after_convI2L_offset(0), + _int_index_after_convI2L_invar(nullptr), + _int_index_after_convI2L_scale(0), _nstack(nstack), _analyze_only(analyze_only), _stack_idx(0) #ifndef PRODUCT @@ -3800,6 +3804,11 @@ SWPointer::SWPointer(MemNode* mem, SuperWord* slp, Node_Stack *nstack, bool anal NOT_PRODUCT(if(_slp->is_trace_alignment()) _tracer.restore_depth();) NOT_PRODUCT(_tracer.ctor_6(mem);) + if (!is_safe_to_use_as_simple_form(base, adr)) { + assert(!valid(), "does not have simple form"); + return; + } + _base = base; _adr = adr; assert(valid(), "Usable"); @@ -3811,6 +3820,10 @@ SWPointer::SWPointer(SWPointer* p) : _mem(p->_mem), _slp(p->_slp), _base(nullptr), _adr(nullptr), _scale(0), _offset(0), _invar(nullptr), _negate_invar(false), _invar_scale(nullptr), + _has_int_index_after_convI2L(false), + _int_index_after_convI2L_offset(0), + _int_index_after_convI2L_invar(nullptr), + _int_index_after_convI2L_scale(0), _nstack(p->_nstack), _analyze_only(p->_analyze_only), _stack_idx(p->_stack_idx) #ifndef PRODUCT @@ -3818,6 +3831,354 @@ SWPointer::SWPointer(SWPointer* p) : #endif {} +// We would like to make decisions about aliasing (i.e. removing memory edges) and adjacency +// (i.e. which loads/stores can be packed) based on the simple form: +// +// s_pointer = adr + offset + invar + scale * ConvI2L(iv) +// +// However, we parse the compound-long-int form: +// +// c_pointer = adr + long_offset + long_invar + long_scale * ConvI2L(int_index) +// int_index = int_offset + int_invar + int_scale * iv +// +// In general, the simple and the compound-long-int form do not always compute the same pointer +// at runtime. For example, the simple form would give a different result due to an overflow +// in the int_index. +// +// Example: +// For both forms, we have: +// iv = 0 +// scale = 1 +// +// We now account the offset and invar once to the long part and once to the int part: +// Pointer 1 (long offset and long invar): +// long_offset = min_int +// long_invar = min_int +// int_offset = 0 +// int_invar = 0 +// +// Pointer 2 (int offset and int invar): +// long_offset = 0 +// long_invar = 0 +// int_offset = min_int +// int_invar = min_int +// +// This gives us the following pointers: +// Compound-long-int form pointers: +// Form: +// c_pointer = adr + long_offset + long_invar + long_scale * ConvI2L(int_offset + int_invar + int_scale * iv) +// +// Pointers: +// c_pointer1 = adr + min_int + min_int + 1 * ConvI2L(0 + 0 + 1 * 0) +// = adr + min_int + min_int +// = adr - 2^32 +// +// c_pointer2 = adr + 0 + 0 + 1 * ConvI2L(min_int + min_int + 1 * 0) +// = adr + ConvI2L(min_int + min_int) +// = adr + 0 +// = adr +// +// Simple form pointers: +// Form: +// s_pointer = adr + offset + invar + scale * ConvI2L(iv) +// s_pointer = adr + (long_offset + int_offset) + (long_invar + int_invar) + (long_scale * int_scale) * ConvI2L(iv) +// +// Pointers: +// s_pointer1 = adr + (min_int + 0 ) + (min_int + 0 ) + 1 * 0 +// = adr + min_int + min_int +// = adr - 2^32 +// s_pointer2 = adr + (0 + min_int ) + (0 + min_int ) + 1 * 0 +// = adr + min_int + min_int +// = adr - 2^32 +// +// We see that the two addresses are actually 2^32 bytes apart (derived from the c_pointers), but their simple form look identical. +// +// Hence, we need to determine in which cases it is safe to make decisions based on the simple +// form, rather than the compound-long-int form. If we cannot prove that using the simple form +// is safe (i.e. equivalent to the compound-long-int form), then we do not get a valid SWPointer, +// and the associated memop cannot be vectorized. +bool SWPointer::is_safe_to_use_as_simple_form(Node* base, Node* adr) const { +#ifndef _LP64 + // On 32-bit platforms, there is never an explicit int_index with ConvI2L for the iv. Thus, the + // parsed pointer form is always the simple form, with int operations: + // + // pointer = adr + offset + invar + scale * iv + // + assert(!_has_int_index_after_convI2L, "32-bit never has an int_index with ConvI2L for the iv"); + return true; +#else + + // Array accesses that are not Unsafe always have a RangeCheck which ensures that there is no + // int_index overflow. This implies that the conversion to long can be done separately: + // + // ConvI2L(int_index) = ConvI2L(int_offset) + ConvI2L(int_invar) + ConvI2L(scale) * ConvI2L(iv) + // + // And hence, the simple form is guaranteed to be identical to the compound-long-int form at + // runtime and the SWPointer is safe/valid to be used. + const TypeAryPtr* ary_ptr_t = _mem->adr_type()->isa_aryptr(); + if (ary_ptr_t != nullptr) { + if (!_mem->is_unsafe_access()) { + return true; + } + } + + // We did not find the int_index. Just to be safe, reject this SWPointer. + if (!_has_int_index_after_convI2L) { + return false; + } + + int int_offset = _int_index_after_convI2L_offset; + Node* int_invar = _int_index_after_convI2L_invar; + int int_scale = _int_index_after_convI2L_scale; + int long_scale = _scale / int_scale; + + // If "int_index = iv", then the simple form is identical to the compound-long-int form. + // + // int_index = int_offset + int_invar + int_scale * iv + // = 0 0 1 * iv + // = iv + if (int_offset == 0 && int_invar == nullptr && int_scale == 1) { + return true; + } + + // Intuition: What happens if the int_index overflows? Let us look at two pointers on the "overflow edge": + // + // pointer1 = adr + ConvI2L(int_index1) + // pointer2 = adr + ConvI2L(int_index2) + // + // int_index1 = max_int + 0 = max_int -> very close to but before the overflow + // int_index2 = max_int + 1 = min_int -> just enough to get the overflow + // + // When looking at the difference of pointer1 and pointer2, we notice that it is very large + // (almost 2^32). Since arrays have at most 2^31 elements, chances are high that pointer2 is + // an actual out-of-bounds access at runtime. These would normally be prevented by range checks + // at runtime. However, if the access was done by using Unsafe, where range checks are omitted, + // then an out-of-bounds access constitutes undefined behavior. This means that we are allowed to + // do anything, including changing the behavior. + // + // If we can set the right conditions, we have a guarantee that an overflow is either impossible + // (no overflow or range checks preventing that) or undefined behavior. In both cases, we are + // safe to do a vectorization. + // + // Approach: We want to prove a lower bound for the distance between these two pointers, and an + // upper bound for the size of a memory object. We can derive such an upper bound for + // arrays. We know they have at most 2^31 elements. If we know the size of the elements + // in bytes, we have: + // + // array_element_size_in_bytes * 2^31 >= max_possible_array_size_in_bytes + // >= array_size_in_bytes (ARR) + // + // If some small difference "delta" leads to an int_index overflow, we know that the + // int_index1 before overflow must have been close to max_int, and the int_index2 after + // the overflow must be close to min_int: + // + // pointer1 = adr + long_offset + long_invar + long_scale * ConvI2L(int_index1) + // =approx adr + long_offset + long_invar + long_scale * max_int + // + // pointer2 = adr + long_offset + long_invar + long_scale * ConvI2L(int_index2) + // =approx adr + long_offset + long_invar + long_scale * min_int + // + // We realize that the pointer difference is very large: + // + // difference =approx long_scale * 2^32 + // + // Hence, if we set the right condition for long_scale and array_element_size_in_bytes, + // we can prove that an overflow is impossible (or would imply undefined behaviour). + // + // We must now take this intuition, and develop a rigorous proof. We start by stating the problem + // more precisely, with the help of some definitions and the Statement we are going to prove. + // + // Definition: + // Two SWPointers are "comparable" (i.e. SWPointer::comparable is true, set with SWPointer::cmp()), + // iff all of these conditions apply for the simple form: + // 1) Both SWPointers are valid. + // 2) The adr are identical, or both are array bases of different arrays. + // 3) They have identical scale. + // 4) They have identical invar. + // 5) The difference in offsets is limited: abs(offset1 - offset2) < 2^31. (DIFF) + // + // For the Vectorization Optimization, we pair-wise compare SWPointers and determine if they are: + // 1) "not comparable": + // We do not optimize them (assume they alias, not assume adjacency). + // + // Whenever we chose this option based on the simple form, it is also correct based on the + // compound-long-int form, since we make no optimizations based on it. + // + // 2) "comparable" with different array bases at runtime: + // We assume they do not alias (remove memory edges), but not assume adjacency. + // + // Whenever we have two different array bases for the simple form, we also have different + // array bases for the compound-long-form. Since SWPointers provably point to different + // memory objects, they can never alias. + // + // 3) "comparable" with the same base address: + // We compute the relative pointer difference, and based on the load/store size we can + // compute aliasing and adjacency. + // + // We must find a condition under which the pointer difference of the simple form is + // identical to the pointer difference of the compound-long-form. We do this with the + // Statement below, which we then proceed to prove. + // + // Statement: + // If two SWPointers satisfy these 3 conditions: + // 1) They are "comparable". + // 2) They have the same base address. + // 3) Their long_scale is a multiple of the array element size in bytes: + // + // abs(long_scale) % array_element_size_in_bytes = 0 (A) + // + // Then their pointer difference of the simple form is identical to the pointer difference + // of the compound-long-int form. + // + // More precisely: + // Such two SWPointers by definition have identical adr, invar, and scale. + // Their simple form is: + // + // s_pointer1 = adr + offset1 + invar + scale * ConvI2L(iv) (B1) + // s_pointer2 = adr + offset2 + invar + scale * ConvI2L(iv) (B2) + // + // Thus, the pointer difference of the simple forms collapses to the difference in offsets: + // + // s_difference = s_pointer1 - s_pointer2 = offset1 - offset2 (C) + // + // Their compound-long-int form for these SWPointer is: + // + // c_pointer1 = adr + long_offset1 + long_invar1 + long_scale1 * ConvI2L(int_index1) (D1) + // int_index1 = int_offset1 + int_invar1 + int_scale1 * iv (D2) + // + // c_pointer2 = adr + long_offset2 + long_invar2 + long_scale2 * ConvI2L(int_index2) (D3) + // int_index2 = int_offset2 + int_invar2 + int_scale2 * iv (D4) + // + // And these are the offset1, offset2, invar and scale from the simple form (B1) and (B2): + // + // offset1 = long_offset1 + long_scale1 * ConvI2L(int_offset1) (D5) + // offset2 = long_offset2 + long_scale2 * ConvI2L(int_offset2) (D6) + // + // invar = long_invar1 + long_scale1 * ConvI2L(int_invar1) + // = long_invar2 + long_scale2 * ConvI2L(int_invar2) (D7) + // + // scale = long_scale1 * ConvI2L(int_scale1) + // = long_scale2 * ConvI2L(int_scale2) (D8) + // + // The pointer difference of the compound-long-int form is defined as: + // + // c_difference = c_pointer1 - c_pointer2 + // + // Thus, the statement claims that for the two SWPointer we have: + // + // s_difference = c_difference (Statement) + // + // We prove the Statement with the help of a Lemma: + // + // Lemma: + // There is some integer x, such that: + // + // c_difference = s_difference + array_element_size_in_bytes * x * 2^32 (Lemma) + // + // From condition (DIFF), we can derive: + // + // abs(s_difference) < 2^31 (E) + // + // Assuming the Lemma, we prove the Statement: + // If "x = 0" (intuitively: the int_index does not overflow), then: + // c_difference = s_difference + // and hence the simple form computes the same pointer difference as the compound-long-int form. + // If "x != 0" (intuitively: the int_index overflows), then: + // abs(c_difference) >= abs(s_difference + array_element_size_in_bytes * x * 2^32) + // >= array_element_size_in_bytes * 2^32 - abs(s_difference) + // -- apply (E) -- + // > array_element_size_in_bytes * 2^32 - 2^31 + // >= array_element_size_in_bytes * 2^31 + // -- apply (ARR) -- + // >= max_possible_array_size_in_bytes + // >= array_size_in_bytes + // + // This shows that c_pointer1 and c_pointer2 have a distance that exceeds the maximum array size. + // Thus, at least one of the two pointers must be outside of the array bounds. But we can assume + // that out-of-bounds accesses do not happen. If they still do, it is undefined behavior. Hence, + // we are allowed to do anything. We can also "safely" use the simple form in this case even though + // it might not match the compound-long-int form at runtime. + // QED Statement. + // + // We must now prove the Lemma. + // + // ConvI2L always truncates by some power of 2^32, i.e. there is some integer y such that: + // + // ConvI2L(y1 + y2) = ConvI2L(y1) + ConvI2L(y2) + 2^32 * y (F) + // + // It follows, that there is an integer y1 such that: + // + // ConvI2L(int_index1) = ConvI2L(int_offset1 + int_invar1 + int_scale1 * iv) + // -- apply (F) -- + // = ConvI2L(int_offset1) + // + ConvI2L(int_invar1) + // + ConvI2L(int_scale1) * ConvI2L(iv) + // + y1 * 2^32 (G) + // + // Thus, we can write the compound-long-int form (D1) as: + // + // c_pointer1 = adr + long_offset1 + long_invar1 + long_scale1 * ConvI2L(int_index1) + // -- apply (G) -- + // = adr + // + long_offset1 + // + long_invar1 + // + long_scale1 * ConvI2L(int_offset1) + // + long_scale1 * ConvI2L(int_invar1) + // + long_scale1 * ConvI2L(int_scale1) * ConvI2L(iv) + // + long_scale1 * y1 * 2^32 (H) + // + // And we can write the simple form as: + // + // s_pointer1 = adr + offset1 + invar + scale * ConvI2L(iv) + // -- apply (D5, D7, D8) -- + // = adr + // + long_offset1 + // + long_scale1 * ConvI2L(int_offset1) + // + long_invar1 + // + long_scale1 * ConvI2L(int_invar1) + // + long_scale1 * ConvI2L(int_scale1) * ConvI2L(iv) (K) + // + // We now compute the pointer difference between the simple (K) and compound-long-int form (H). + // Most terms cancel out immediately: + // + // sc_difference1 = c_pointer1 - s_pointer1 = long_scale1 * y1 * 2^32 (L) + // + // Rearranging the equation (L), we get: + // + // c_pointer1 = s_pointer1 + long_scale1 * y1 * 2^32 (M) + // + // And since long_scale1 is a multiple of array_element_size_in_bytes, there is some integer + // x1, such that (M) implies: + // + // c_pointer1 = s_pointer1 + array_element_size_in_bytes * x1 * 2^32 (N) + // + // With an analogue equation for c_pointer2, we can now compute the pointer difference for + // the compound-long-int form: + // + // c_difference = c_pointer1 - c_pointer2 + // -- apply (N) -- + // = s_pointer1 + array_element_size_in_bytes * x1 * 2^32 + // -(s_pointer2 + array_element_size_in_bytes * x2 * 2^32) + // -- where "x = x1 - x2" -- + // = s_pointer1 - s_pointer2 + array_element_size_in_bytes * x * 2^32 + // -- apply (C) -- + // = s_difference + array_element_size_in_bytes * x * 2^32 + // QED Lemma. + if (ary_ptr_t != nullptr) { + BasicType array_element_bt = ary_ptr_t->elem()->array_element_basic_type(); + if (is_java_primitive(array_element_bt)) { + int array_element_size_in_bytes = type2aelembytes(array_element_bt); + if (abs(long_scale) % array_element_size_in_bytes == 0) { + return true; + } + } + } + + // General case: we do not know if it is safe to use the simple form. + return false; +#endif +} + bool SWPointer::is_main_loop_member(Node* n) const { Node* n_c = phase()->get_ctrl(n); return lpt()->is_member(phase()->get_loop(n_c)); @@ -3867,11 +4228,42 @@ bool SWPointer::scaled_iv_plus_offset(Node* n) { } } else if (opc == Op_SubI) { if (offset_plus_k(n->in(2), true) && scaled_iv_plus_offset(n->in(1))) { + // (offset1 + invar1 + scale * iv) - (offset2) or + // (offset1 + scale * iv) - (offset2 + invar1) + // Subtraction handled via "negate" flag of "offset_plus_k". NOT_PRODUCT(_tracer.scaled_iv_plus_offset_6(n);) return true; } - if (offset_plus_k(n->in(1)) && scaled_iv_plus_offset(n->in(2))) { - _scale *= -1; + SWPointer tmp(this); + if (offset_plus_k(n->in(1)) && tmp.scaled_iv_plus_offset(n->in(2))) { + // (offset1 + invar1) - (offset2 + scale * iv) or + // (offset1) - (offset2 + invar1 + scale * iv) + // Subtraction handled explicitly below. + assert(_scale == 0, "shouldn't be set yet"); + // _scale = -tmp._scale + if (!try_MulI_no_overflow(-1, tmp._scale, _scale)) { + return false; // mul overflow. + } + // _offset -= tmp._offset + if (!try_SubI_no_overflow(_offset, tmp._offset, _offset)) { + return false; // sub overflow. + } + // _invar -= tmp._invar + if (tmp._invar != nullptr) { + if (_invar != nullptr) { + return false; + } + _invar = tmp._invar; + _invar_scale = tmp._invar_scale; + _negate_invar = !tmp._negate_invar; + } + + // SWPointer tmp does not have an integer part to be forwarded + // (tmp._has_int_index_after_convI2L is false) because n is a SubI, all + // nodes above must also be of integer type (ConvL2I is not handled + // to allow a long) and ConvI2L (the only node that can add an integer + // part) won't be present. + NOT_PRODUCT(_tracer.scaled_iv_plus_offset_7(n);) return true; } @@ -3914,10 +4306,57 @@ bool SWPointer::scaled_iv(Node* n) { } } else if (opc == Op_LShiftI) { if (n->in(1) == iv() && n->in(2)->is_Con()) { - _scale = 1 << n->in(2)->get_int(); + if (!try_LShiftI_no_overflow(1, n->in(2)->get_int(), _scale)) { + return false; // shift overflow. + } NOT_PRODUCT(_tracer.scaled_iv_6(n, _scale);) return true; } + } else if (opc == Op_ConvI2L && !has_iv()) { + // So far we have not found the iv yet, and are about to enter a ConvI2L subgraph, + // which may be the int index (that might overflow) for the memory access, of the form: + // + // int_index = int_offset + int_invar + int_scale * iv + // + // If we simply continue parsing with the current SWPointer, then the int_offset and + // int_invar simply get added to the long offset and invar. But for the checks in + // SWPointer::is_safe_to_use_as_simple_form() we need to have explicit access to the + // int_index. Thus, we must parse it explicitly here. For this, we use a temporary + // SWPointer, to pattern match the int_index sub-expression of the address. + + NOT_PRODUCT(Tracer::Depth dddd;) + SWPointer tmp(this); + NOT_PRODUCT(_tracer.scaled_iv_8(n, &tmp);) + + if (tmp.scaled_iv_plus_offset(n->in(1)) && tmp.has_iv()) { + // We successfully matched an integer index, of the form: + // int_index = int_offset + int_invar + int_scale * iv + // Forward scale. + assert(_scale == 0 && tmp._scale != 0, "iv only found just now"); + _scale = tmp._scale; + // Accumulate offset. + if (!try_AddI_no_overflow(_offset, tmp._offset, _offset)) { + return false; // add overflow. + } + // Forward invariant if not already found. + if (tmp._invar != nullptr) { + if (_invar != nullptr) { + return false; + } + _invar = tmp._invar; + _invar_scale = tmp._invar_scale; + _negate_invar = tmp._negate_invar; + } + // Set info about the int_index: + assert(!_has_int_index_after_convI2L, "no previous int_index discovered"); + _has_int_index_after_convI2L = true; + _int_index_after_convI2L_offset = tmp._offset; + _int_index_after_convI2L_invar = tmp._invar; + _int_index_after_convI2L_scale = tmp._scale; + + NOT_PRODUCT(_tracer.scaled_iv_7(n);) + return true; + } } else if (opc == Op_ConvI2L || opc == Op_CastII) { if (scaled_iv_plus_offset(n->in(1))) { NOT_PRODUCT(_tracer.scaled_iv_7(n);) @@ -3933,14 +4372,33 @@ bool SWPointer::scaled_iv(Node* n) { NOT_PRODUCT(_tracer.scaled_iv_8(n, &tmp);) if (tmp.scaled_iv_plus_offset(n->in(1))) { - int scale = n->in(2)->get_int(); - _scale = tmp._scale << scale; - _offset += tmp._offset << scale; + int shift = n->in(2)->get_int(); + // Accumulate scale. + if (!try_LShiftI_no_overflow(tmp._scale, shift, _scale)) { + return false; // shift overflow. + } + // Accumulate offset. + int shifted_offset = 0; + if (!try_LShiftI_no_overflow(tmp._offset, shift, shifted_offset)) { + return false; // shift overflow. + } + if (!try_AddI_no_overflow(_offset, shifted_offset, _offset)) { + return false; // add overflow. + } + // Accumulate invar. _invar = tmp._invar; if (_invar != nullptr) { _negate_invar = tmp._negate_invar; _invar_scale = n->in(2); } + + // Forward info about the int_index: + assert(!_has_int_index_after_convI2L, "no previous int_index discovered"); + _has_int_index_after_convI2L = tmp._has_int_index_after_convI2L; + _int_index_after_convI2L_offset = tmp._int_index_after_convI2L_offset; + _int_index_after_convI2L_invar = tmp._int_index_after_convI2L_invar; + _int_index_after_convI2L_scale = tmp._int_index_after_convI2L_scale; + NOT_PRODUCT(_tracer.scaled_iv_9(n, _scale, _offset, _invar, _negate_invar);) return true; } @@ -3959,7 +4417,9 @@ bool SWPointer::offset_plus_k(Node* n, bool negate) { int opc = n->Opcode(); if (opc == Op_ConI) { - _offset += negate ? -(n->get_int()) : n->get_int(); + if (!try_AddSubI_no_overflow(_offset, n->get_int(), negate, _offset)) { + return false; // add/sub overflow. + } NOT_PRODUCT(_tracer.offset_plus_k_2(n, _offset);) return true; } else if (opc == Op_ConL) { @@ -3968,7 +4428,9 @@ bool SWPointer::offset_plus_k(Node* n, bool negate) { if (t->higher_equal(TypeLong::INT)) { jlong loff = n->get_long(); jint off = (jint)loff; - _offset += negate ? -off : loff; + if (!try_AddSubI_no_overflow(_offset, off, negate, _offset)) { + return false; // add/sub overflow. + } NOT_PRODUCT(_tracer.offset_plus_k_3(n, _offset);) return true; } @@ -3987,11 +4449,15 @@ bool SWPointer::offset_plus_k(Node* n, bool negate) { if (n->in(2)->is_Con() && invariant(n->in(1))) { _negate_invar = negate; _invar = n->in(1); - _offset += negate ? -(n->in(2)->get_int()) : n->in(2)->get_int(); + if (!try_AddSubI_no_overflow(_offset, n->in(2)->get_int(), negate, _offset)) { + return false; // add/sub overflow. + } NOT_PRODUCT(_tracer.offset_plus_k_6(n, _invar, _negate_invar, _offset);) return true; } else if (n->in(1)->is_Con() && invariant(n->in(2))) { - _offset += negate ? -(n->in(1)->get_int()) : n->in(1)->get_int(); + if (!try_AddSubI_no_overflow(_offset, n->in(1)->get_int(), negate, _offset)) { + return false; // add/sub overflow. + } _negate_invar = negate; _invar = n->in(2); NOT_PRODUCT(_tracer.offset_plus_k_7(n, _invar, _negate_invar, _offset);) @@ -4002,11 +4468,15 @@ bool SWPointer::offset_plus_k(Node* n, bool negate) { if (n->in(2)->is_Con() && invariant(n->in(1))) { _negate_invar = negate; _invar = n->in(1); - _offset += !negate ? -(n->in(2)->get_int()) : n->in(2)->get_int(); + if (!try_AddSubI_no_overflow(_offset, n->in(2)->get_int(), !negate, _offset)) { + return false; // add/sub overflow. + } NOT_PRODUCT(_tracer.offset_plus_k_8(n, _invar, _negate_invar, _offset);) return true; } else if (n->in(1)->is_Con() && invariant(n->in(2))) { - _offset += negate ? -(n->in(1)->get_int()) : n->in(1)->get_int(); + if (!try_AddSubI_no_overflow(_offset, n->in(1)->get_int(), negate, _offset)) { + return false; // add/sub overflow. + } _negate_invar = !negate; _invar = n->in(2); NOT_PRODUCT(_tracer.offset_plus_k_9(n, _invar, _negate_invar, _offset);) @@ -4037,6 +4507,57 @@ bool SWPointer::offset_plus_k(Node* n, bool negate) { return false; } +bool SWPointer::try_AddI_no_overflow(int offset1, int offset2, int& result) { + jlong long_offset = java_add((jlong)(offset1), (jlong)(offset2)); + jint int_offset = java_add((jint)(offset1), (jint)(offset2)); + if (long_offset != int_offset) { + return false; + } + result = int_offset; + return true; +} + +bool SWPointer::try_SubI_no_overflow(int offset1, int offset2, int& result) { + jlong long_offset = java_subtract((jlong)(offset1), (jlong)(offset2)); + jint int_offset = java_subtract((jint)(offset1), (jint)(offset2)); + if (long_offset != int_offset) { + return false; + } + result = int_offset; + return true; +} + +bool SWPointer::try_AddSubI_no_overflow(int offset1, int offset2, bool is_sub, int& result) { + if (is_sub) { + return try_SubI_no_overflow(offset1, offset2, result); + } else { + return try_AddI_no_overflow(offset1, offset2, result); + } +} + +bool SWPointer::try_LShiftI_no_overflow(int offset, int shift, int& result) { + if (shift < 0 || shift > 31) { + return false; + } + jlong long_offset = java_shift_left((jlong)(offset), (julong)((jlong)(shift))); + jint int_offset = java_shift_left((jint)(offset), (juint)((jint)(shift))); + if (long_offset != int_offset) { + return false; + } + result = int_offset; + return true; +} + +bool SWPointer::try_MulI_no_overflow(int offset1, int offset2, int& result) { + jlong long_offset = java_multiply((jlong)(offset1), (jlong)(offset2)); + jint int_offset = java_multiply((jint)(offset1), (jint)(offset2)); + if (long_offset != int_offset) { + return false; + } + result = int_offset; + return true; +} + //----------------------------print------------------------ void SWPointer::print() { #ifndef PRODUCT diff --git a/src/hotspot/share/opto/superword.hpp b/src/hotspot/share/opto/superword.hpp index f53a25c5bfb..5d95a14cd06 100644 --- a/src/hotspot/share/opto/superword.hpp +++ b/src/hotspot/share/opto/superword.hpp @@ -572,13 +572,51 @@ class SuperWord : public ResourceObj { //------------------------------SWPointer--------------------------- // Information about an address for dependence checking and vector alignment +// +// We parse and represent pointers of the simple form: +// +// pointer = adr + offset + invar + scale * ConvI2L(iv) +// +// Where: +// +// adr: the base address of an array (base = adr) +// OR +// some address to off-heap memory (base = TOP) +// +// offset: a constant offset +// invar: a runtime variable, which is invariant during the loop +// scale: scaling factor +// iv: loop induction variable +// +// But more precisely, we parse the composite-long-int form: +// +// pointer = adr + long_offset + long_invar + long_scale * ConvI2L(int_offset + inv_invar + int_scale * iv) +// +// pointer = adr + long_offset + long_invar + long_scale * ConvI2L(int_index) +// int_index = int_offset + int_invar + int_scale * iv +// +// However, for aliasing and adjacency checks (e.g. SWPointer::cmp()) we always use the simple form to make +// decisions. Hence, we must make sure to only create a "valid" SWPointer if the optimisations based on the +// simple form produce the same result as the compound-long-int form would. Intuitively, this depends on +// if the int_index overflows, but the precise conditions are given in SWPointer::is_safe_to_use_as_simple_form(). +// +// ConvI2L(int_index) = ConvI2L(int_offset + int_invar + int_scale * iv) +// = Convi2L(int_offset) + ConvI2L(int_invar) + ConvI2L(int_scale) * ConvI2L(iv) +// +// scale = long_scale * ConvI2L(int_scale) +// offset = long_offset + long_scale * ConvI2L(int_offset) +// invar = long_invar + long_scale * ConvI2L(int_invar) +// +// pointer = adr + offset + invar + scale * ConvI2L(iv) +// class SWPointer { protected: MemNode* _mem; // My memory reference node SuperWord* _slp; // SuperWord class - Node* _base; // null if unsafe nonheap reference - Node* _adr; // address pointer + // Components of the simple form: + Node* _base; // Base address of an array OR null if some off-heap memory. + Node* _adr; // Same as _base if an array pointer OR some off-heap memory pointer. int _scale; // multiplier for iv (in bytes), 0 if no loop iv int _offset; // constant offset (in bytes) @@ -586,6 +624,13 @@ class SWPointer { bool _negate_invar; // if true then use: (0 - _invar) Node* _invar_scale; // multiplier for invariant + // The int_index components of the compound-long-int form. Used to decide if it is safe to use the + // simple form rather than the compound-long-int form that was parsed. + bool _has_int_index_after_convI2L; + int _int_index_after_convI2L_offset; + Node* _int_index_after_convI2L_invar; + int _int_index_after_convI2L_scale; + Node_Stack* _nstack; // stack used to record a swpointer trace of variants bool _analyze_only; // Used in loop unrolling only for swpointer trace uint _stack_idx; // Used in loop unrolling only for swpointer trace @@ -604,6 +649,8 @@ class SWPointer { // Match: offset is (k [+/- invariant]) bool offset_plus_k(Node* n, bool negate = false); + bool is_safe_to_use_as_simple_form(Node* base, Node* adr) const; + public: enum CMP { Less = 1, @@ -639,10 +686,43 @@ class SWPointer { _negate_invar == q._negate_invar); } + // We compute if and how two SWPointers can alias at runtime, i.e. if the two addressed regions of memory can + // ever overlap. There are essentially 3 relevant return states: + // - NotComparable: Synonymous to "unknown aliasing". + // We have no information about how the two SWPointers can alias. They could overlap, refer + // to another location in the same memory object, or point to a completely different object. + // -> Memory edge required. Aliasing unlikely but possible. + // + // - Less / Greater: Synonymous to "never aliasing". + // The two SWPointers may point into the same memory object, but be non-aliasing (i.e. we + // know both address regions inside the same memory object, but these regions are non- + // overlapping), or the SWPointers point to entirely different objects. + // -> No memory edge required. Aliasing impossible. + // + // - Equal: Synonymous to "overlap, or point to different memory objects". + // The two SWPointers either overlap on the same memory object, or point to two different + // memory objects. + // -> Memory edge required. Aliasing likely. + // + // In a future refactoring, we can simplify to two states: + // - NeverAlias: instead of Less / Greater + // - MayAlias: instead of Equal / NotComparable + // + // Two SWPointer are "comparable" (Less / Greater / Equal), iff all of these conditions apply: + // 1) Both are valid, i.e. expressible in the compound-long-int or simple form. + // 2) The adr are identical, or both are array bases of different arrays. + // 3) They have identical scale. + // 4) They have identical invar. + // 5) The difference in offsets is limited: abs(offset0 - offset1) < 2^31. int cmp(SWPointer& q) { if (valid() && q.valid() && (_adr == q._adr || (_base == _adr && q._base == q._adr)) && _scale == q._scale && invar_equals(q)) { + jlong difference = abs(java_subtract((jlong)_offset, (jlong)q._offset)); + jlong max_diff = (jlong)1 << 31; + if (difference >= max_diff) { + return NotComparable; + } bool overlap = q._offset < _offset + memory_size() && _offset < q._offset + q.memory_size(); return overlap ? Equal : (_offset < q._offset ? Less : Greater); @@ -729,6 +809,13 @@ class SWPointer { } _tracer;//TRacer; #endif + + static bool try_AddI_no_overflow(int offset1, int offset2, int& result); + static bool try_SubI_no_overflow(int offset1, int offset2, int& result); + static bool try_AddSubI_no_overflow(int offset1, int offset2, bool is_sub, int& result); + static bool try_LShiftI_no_overflow(int offset1, int offset2, int& result); + static bool try_MulI_no_overflow(int offset1, int offset2, int& result); + }; #endif // SHARE_OPTO_SUPERWORD_HPP diff --git a/src/java.base/share/classes/java/net/doc-files/net-properties.html b/src/java.base/share/classes/java/net/doc-files/net-properties.html index b7ed2f9924f..2c10d18a46e 100644 --- a/src/java.base/share/classes/java/net/doc-files/net-properties.html +++ b/src/java.base/share/classes/java/net/doc-files/net-properties.html @@ -240,6 +240,15 @@
The channel binding tokens generated are of the type "tls-server-end-point" as defined in RFC 5929.
+ +{@systemProperty jdk.http.maxHeaderSize} (default: 393216 or 384kB)
+ This is the maximum header field section size that a client is prepared to accept.
+ This is computed as the sum of the size of the uncompressed header name, plus
+ the size of the uncompressed header value, plus an overhead of 32 bytes for
+ each field section line. If a peer sends a field section that exceeds this
+ size a {@link java.net.ProtocolException ProtocolException} will be raised.
+ This applies to all versions of the HTTP protocol. A value of zero or a negative
+ value means no limit. If left unspecified, the default value is 393216 bytes.
All these properties are checked only once at startup.
diff --git a/src/java.base/share/classes/java/text/MessageFormat.java b/src/java.base/share/classes/java/text/MessageFormat.java index e304d072411..5ac88f0cfaa 100644 --- a/src/java.base/share/classes/java/text/MessageFormat.java +++ b/src/java.base/share/classes/java/text/MessageFormat.java @@ -41,6 +41,7 @@ import java.io.InvalidObjectException; import java.io.IOException; import java.io.ObjectInputStream; +import java.io.ObjectStreamException; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Arrays; @@ -983,6 +984,8 @@ public Object[] parse(String source, ParsePosition pos) { maximumArgumentNumber = argumentNumbers[i]; } } + + // Constructors/applyPattern ensure that resultArray.length < MAX_ARGUMENT_INDEX Object[] resultArray = new Object[maximumArgumentNumber + 1]; int patternOffset = 0; @@ -1235,6 +1238,9 @@ protected Object readResolve() throws InvalidObjectException { * @serial */ private int[] argumentNumbers = new int[INITIAL_FORMATS]; + // Implementation limit for ArgumentIndex pattern element. Valid indices must + // be less than this value + private static final int MAX_ARGUMENT_INDEX = 10000; /** * One less than the number of entries in {@code offsets}. Can also be thought of @@ -1459,6 +1465,11 @@ private void makeFormat(int position, int offsetNumber, + argumentNumber); } + if (argumentNumber >= MAX_ARGUMENT_INDEX) { + throw new IllegalArgumentException( + argumentNumber + " exceeds the ArgumentIndex implementation limit"); + } + // resize format information arrays if necessary if (offsetNumber >= formats.length) { int newLength = formats.length * 2; @@ -1606,24 +1617,53 @@ private static final void copyAndFixQuotes(String source, int start, int end, */ @java.io.Serial private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { - in.defaultReadObject(); - boolean isValid = maxOffset >= -1 - && formats.length > maxOffset - && offsets.length > maxOffset - && argumentNumbers.length > maxOffset; + ObjectInputStream.GetField fields = in.readFields(); + if (fields.defaulted("argumentNumbers") || fields.defaulted("offsets") + || fields.defaulted("formats") || fields.defaulted("locale") + || fields.defaulted("pattern") || fields.defaulted("maxOffset")){ + throw new InvalidObjectException("Stream has missing data"); + } + + locale = (Locale) fields.get("locale", null); + String patt = (String) fields.get("pattern", null); + int maxOff = fields.get("maxOffset", -2); + int[] argNums = ((int[]) fields.get("argumentNumbers", null)).clone(); + int[] offs = ((int[]) fields.get("offsets", null)).clone(); + Format[] fmts = ((Format[]) fields.get("formats", null)).clone(); + + // Check arrays/maxOffset have correct value/length + boolean isValid = maxOff >= -1 && argNums.length > maxOff + && offs.length > maxOff && fmts.length > maxOff; + + // Check the correctness of arguments and offsets if (isValid) { - int lastOffset = pattern.length() + 1; - for (int i = maxOffset; i >= 0; --i) { - if ((offsets[i] < 0) || (offsets[i] > lastOffset)) { + int lastOffset = patt.length() + 1; + for (int i = maxOff; i >= 0; --i) { + if (argNums[i] < 0 || argNums[i] >= MAX_ARGUMENT_INDEX + || offs[i] < 0 || offs[i] > lastOffset) { isValid = false; break; } else { - lastOffset = offsets[i]; + lastOffset = offs[i]; } } } + if (!isValid) { - throw new InvalidObjectException("Could not reconstruct MessageFormat from corrupt stream."); + throw new InvalidObjectException("Stream has invalid data"); } + maxOffset = maxOff; + pattern = patt; + offsets = offs; + formats = fmts; + argumentNumbers = argNums; + } + + /** + * Serialization without data not supported for this class. + */ + @java.io.Serial + private void readObjectNoData() throws ObjectStreamException { + throw new InvalidObjectException("Deserialized MessageFormat objects need data"); } } diff --git a/src/java.base/share/classes/sun/net/www/MessageHeader.java b/src/java.base/share/classes/sun/net/www/MessageHeader.java index 22b16407dd2..4a60ab482c7 100644 --- a/src/java.base/share/classes/sun/net/www/MessageHeader.java +++ b/src/java.base/share/classes/sun/net/www/MessageHeader.java @@ -30,6 +30,8 @@ package sun.net.www; import java.io.*; +import java.lang.reflect.Array; +import java.net.ProtocolException; import java.util.Collections; import java.util.*; @@ -46,11 +48,32 @@ class MessageHeader { private String values[]; private int nkeys; + // max number of bytes for headers, <=0 means unlimited; + // this corresponds to the length of the names, plus the length + // of the values, plus an overhead of 32 bytes per name: value + // pair. + // Note: we use the same definition as HTTP/2 SETTINGS_MAX_HEADER_LIST_SIZE + // see RFC 9113, section 6.5.2. + // https://www.rfc-editor.org/rfc/rfc9113.html#SETTINGS_MAX_HEADER_LIST_SIZE + private final int maxHeaderSize; + + // Aggregate size of the field lines (name + value + 32) x N + // that have been parsed and accepted so far. + // This is defined as a long to force promotion to long + // and avoid overflows; see checkNewSize; + private long size; + public MessageHeader () { + this(0); + } + + public MessageHeader (int maxHeaderSize) { + this.maxHeaderSize = maxHeaderSize; grow(); } public MessageHeader (InputStream is) throws java.io.IOException { + maxHeaderSize = 0; parseHeader(is); } @@ -477,10 +500,28 @@ public static String canonicalID(String id) { public void parseHeader(InputStream is) throws java.io.IOException { synchronized (this) { nkeys = 0; + size = 0; } mergeHeader(is); } + private void checkMaxHeaderSize(int sz) throws ProtocolException { + if (maxHeaderSize > 0) checkNewSize(size, sz, 0); + } + + private long checkNewSize(long size, int name, int value) throws ProtocolException { + // See SETTINGS_MAX_HEADER_LIST_SIZE, RFC 9113, section 6.5.2. + long newSize = size + name + value + 32; + if (maxHeaderSize > 0 && newSize > maxHeaderSize) { + Arrays.fill(keys, 0, nkeys, null); + Arrays.fill(values,0, nkeys, null); + nkeys = 0; + throw new ProtocolException(String.format("Header size too big: %s > %s", + newSize, maxHeaderSize)); + } + return newSize; + } + /** Parse and merge a MIME header from an input stream. */ @SuppressWarnings("fallthrough") public void mergeHeader(InputStream is) throws java.io.IOException { @@ -494,7 +535,15 @@ public void mergeHeader(InputStream is) throws java.io.IOException { int c; boolean inKey = firstc > ' '; s[len++] = (char) firstc; + checkMaxHeaderSize(len); parseloop:{ + // We start parsing for a new name value pair here. + // The max header size includes an overhead of 32 bytes per + // name value pair. + // See SETTINGS_MAX_HEADER_LIST_SIZE, RFC 9113, section 6.5.2. + long maxRemaining = maxHeaderSize > 0 + ? maxHeaderSize - size - 32 + : Long.MAX_VALUE; while ((c = is.read()) >= 0) { switch (c) { case ':': @@ -528,6 +577,9 @@ public void mergeHeader(InputStream is) throws java.io.IOException { s = ns; } s[len++] = (char) c; + if (maxHeaderSize > 0 && len > maxRemaining) { + checkMaxHeaderSize(len); + } } firstc = -1; } @@ -549,6 +601,9 @@ public void mergeHeader(InputStream is) throws java.io.IOException { v = new String(); else v = String.copyValueOf(s, keyend, len - keyend); + int klen = k == null ? 0 : k.length(); + + size = checkNewSize(size, klen, v.length()); add(k, v); } } diff --git a/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java b/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java index 7dc9f99eb18..288ebfd8504 100644 --- a/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java +++ b/src/java.base/share/classes/sun/net/www/protocol/http/HttpURLConnection.java @@ -171,6 +171,8 @@ public class HttpURLConnection extends java.net.HttpURLConnection { */ private static int bufSize4ES = 0; + private static final int maxHeaderSize; + /* * Restrict setting of request headers through the public api * consistent with JavaScript XMLHttpRequest2 with a few @@ -285,6 +287,19 @@ private static Set The value of the capacity has to be agreed between decoder and encoder out-of-band,
+ * e.g. by a protocol that uses HPACK
+ * (see 4.2. Maximum Table Size).
+ *
+ * @param capacity
+ * a non-negative integer
+ * @param maxHeaderListSize
+ * a maximum value for the header list size. This is the uncompressed
+ * names size + uncompressed values size + 32 bytes per field line
+ * @param maxIndexed
+ * the maximum number of literal with indexing we're prepared to handle
+ * for a header field section
+ *
+ * @throws IllegalArgumentException
+ * if capacity is negative
+ */
+ public Decoder(int capacity, int maxHeaderListSize, int maxIndexed) {
id = DECODERS_IDS.incrementAndGet();
logger = HPACK.getLogger().subLogger("Decoder#" + id);
if (logger.isLoggable(NORMAL)) {
@@ -145,6 +175,8 @@ public Decoder(int capacity) {
toString(), hashCode);
});
}
+ this.maxHeaderListSize = maxHeaderListSize;
+ this.maxIndexed = maxIndexed;
setMaxCapacity0(capacity);
table = new SimpleHeaderTable(capacity, logger.subLogger("HeaderTable"));
integerReader = new IntegerReader();
@@ -242,22 +274,25 @@ public void decode(ByteBuffer headerBlock,
requireNonNull(consumer, "consumer");
if (logger.isLoggable(NORMAL)) {
logger.log(NORMAL, () -> format("reading %s, end of header block? %s",
- headerBlock, endOfHeaderBlock));
+ headerBlock, endOfHeaderBlock));
}
while (headerBlock.hasRemaining()) {
proceed(headerBlock, consumer);
}
if (endOfHeaderBlock && state != State.READY) {
logger.log(NORMAL, () -> format("unexpected end of %s representation",
- state));
+ state));
throw new IOException("Unexpected end of header block");
}
+ if (endOfHeaderBlock) {
+ size = indexed = 0;
+ }
}
private void proceed(ByteBuffer input, DecodingCallback action)
throws IOException {
switch (state) {
- case READY -> resumeReady(input);
+ case READY -> resumeReady(input, action);
case INDEXED -> resumeIndexed(input, action);
case LITERAL -> resumeLiteral(input, action);
case LITERAL_WITH_INDEXING -> resumeLiteralWithIndexing(input, action);
@@ -268,7 +303,7 @@ private void proceed(ByteBuffer input, DecodingCallback action)
}
}
- private void resumeReady(ByteBuffer input) {
+ private void resumeReady(ByteBuffer input, DecodingCallback action) throws IOException {
int b = input.get(input.position()) & 0xff; // absolute read
State s = states.get(b);
if (logger.isLoggable(EXTRA)) {
@@ -289,6 +324,9 @@ private void resumeReady(ByteBuffer input) {
}
break;
case LITERAL_WITH_INDEXING:
+ if (maxIndexed > 0 && ++indexed > maxIndexed) {
+ action.onMaxLiteralWithIndexingReached(indexed, maxIndexed);
+ }
state = State.LITERAL_WITH_INDEXING;
firstValueIndex = (b & 0b0011_1111) != 0;
if (firstValueIndex) {
@@ -315,6 +353,12 @@ private void resumeReady(ByteBuffer input) {
}
}
+ private void checkMaxHeaderListSize(long sz, DecodingCallback consumer) throws ProtocolException {
+ if (maxHeaderListSize > 0 && sz > maxHeaderListSize) {
+ consumer.onMaxHeaderListSizeReached(sz, maxHeaderListSize);
+ }
+ }
+
// 0 1 2 3 4 5 6 7
// +---+---+---+---+---+---+---+---+
// | 1 | Index (7+) |
@@ -332,6 +376,8 @@ private void resumeIndexed(ByteBuffer input, DecodingCallback action)
}
try {
SimpleHeaderTable.HeaderField f = getHeaderFieldAt(intValue);
+ size = size + 32 + f.name.length() + f.value.length();
+ checkMaxHeaderListSize(size, action);
action.onIndexed(intValue, f.name, f.value);
} finally {
state = State.READY;
@@ -374,7 +420,7 @@ private SimpleHeaderTable.HeaderField getHeaderFieldAt(int index)
//
private void resumeLiteral(ByteBuffer input, DecodingCallback action)
throws IOException {
- if (!completeReading(input)) {
+ if (!completeReading(input, action)) {
return;
}
try {
@@ -385,6 +431,8 @@ private void resumeLiteral(ByteBuffer input, DecodingCallback action)
intValue, value, valueHuffmanEncoded));
}
SimpleHeaderTable.HeaderField f = getHeaderFieldAt(intValue);
+ size = size + 32 + f.name.length() + value.length();
+ checkMaxHeaderListSize(size, action);
action.onLiteral(intValue, f.name, value, valueHuffmanEncoded);
} else {
if (logger.isLoggable(NORMAL)) {
@@ -392,6 +440,8 @@ private void resumeLiteral(ByteBuffer input, DecodingCallback action)
"literal without indexing ('%s', huffman=%b, '%s', huffman=%b)",
name, nameHuffmanEncoded, value, valueHuffmanEncoded));
}
+ size = size + 32 + name.length() + value.length();
+ checkMaxHeaderListSize(size, action);
action.onLiteral(name, nameHuffmanEncoded, value, valueHuffmanEncoded);
}
} finally {
@@ -425,7 +475,7 @@ private void resumeLiteral(ByteBuffer input, DecodingCallback action)
private void resumeLiteralWithIndexing(ByteBuffer input,
DecodingCallback action)
throws IOException {
- if (!completeReading(input)) {
+ if (!completeReading(input, action)) {
return;
}
try {
@@ -445,6 +495,8 @@ private void resumeLiteralWithIndexing(ByteBuffer input,
}
SimpleHeaderTable.HeaderField f = getHeaderFieldAt(intValue);
n = f.name;
+ size = size + 32 + n.length() + v.length();
+ checkMaxHeaderListSize(size, action);
action.onLiteralWithIndexing(intValue, n, v, valueHuffmanEncoded);
} else {
n = name.toString();
@@ -453,6 +505,8 @@ private void resumeLiteralWithIndexing(ByteBuffer input,
"literal with incremental indexing ('%s', huffman=%b, '%s', huffman=%b)",
n, nameHuffmanEncoded, value, valueHuffmanEncoded));
}
+ size = size + 32 + n.length() + v.length();
+ checkMaxHeaderListSize(size, action);
action.onLiteralWithIndexing(n, nameHuffmanEncoded, v, valueHuffmanEncoded);
}
table.put(n, v);
@@ -486,7 +540,7 @@ private void resumeLiteralWithIndexing(ByteBuffer input,
private void resumeLiteralNeverIndexed(ByteBuffer input,
DecodingCallback action)
throws IOException {
- if (!completeReading(input)) {
+ if (!completeReading(input, action)) {
return;
}
try {
@@ -497,6 +551,8 @@ private void resumeLiteralNeverIndexed(ByteBuffer input,
intValue, value, valueHuffmanEncoded));
}
SimpleHeaderTable.HeaderField f = getHeaderFieldAt(intValue);
+ size = size + 32 + f.name.length() + value.length();
+ checkMaxHeaderListSize(size, action);
action.onLiteralNeverIndexed(intValue, f.name, value, valueHuffmanEncoded);
} else {
if (logger.isLoggable(NORMAL)) {
@@ -504,6 +560,8 @@ private void resumeLiteralNeverIndexed(ByteBuffer input,
"literal never indexed ('%s', huffman=%b, '%s', huffman=%b)",
name, nameHuffmanEncoded, value, valueHuffmanEncoded));
}
+ size = size + 32 + name.length() + value.length();
+ checkMaxHeaderListSize(size, action);
action.onLiteralNeverIndexed(name, nameHuffmanEncoded, value, valueHuffmanEncoded);
}
} finally {
@@ -541,7 +599,7 @@ private void resumeSizeUpdate(ByteBuffer input,
}
}
- private boolean completeReading(ByteBuffer input) throws IOException {
+ private boolean completeReading(ByteBuffer input, DecodingCallback action) throws IOException {
if (!firstValueRead) {
if (firstValueIndex) {
if (!integerReader.read(input)) {
@@ -551,6 +609,8 @@ private boolean completeReading(ByteBuffer input) throws IOException {
integerReader.reset();
} else {
if (!stringReader.read(input, name)) {
+ long sz = size + 32 + name.length();
+ checkMaxHeaderListSize(sz, action);
return false;
}
nameHuffmanEncoded = stringReader.isHuffmanEncoded();
@@ -560,6 +620,8 @@ private boolean completeReading(ByteBuffer input) throws IOException {
return false;
} else {
if (!stringReader.read(input, value)) {
+ long sz = size + 32 + name.length() + value.length();
+ checkMaxHeaderListSize(sz, action);
return false;
}
}
diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/hpack/DecodingCallback.java b/src/java.net.http/share/classes/jdk/internal/net/http/hpack/DecodingCallback.java
index 5e9df860feb..228f9bf0206 100644
--- a/src/java.net.http/share/classes/jdk/internal/net/http/hpack/DecodingCallback.java
+++ b/src/java.net.http/share/classes/jdk/internal/net/http/hpack/DecodingCallback.java
@@ -24,6 +24,7 @@
*/
package jdk.internal.net.http.hpack;
+import java.net.ProtocolException;
import java.nio.ByteBuffer;
/**
@@ -292,4 +293,17 @@ default void onLiteralWithIndexing(CharSequence name,
* new capacity of the header table
*/
default void onSizeUpdate(int capacity) { }
+
+ default void onMaxHeaderListSizeReached(long size, int maxHeaderListSize)
+ throws ProtocolException {
+ throw new ProtocolException(String
+ .format("Size exceeds MAX_HEADERS_LIST_SIZE: %s > %s",
+ size, maxHeaderListSize));
+ }
+
+ default void onMaxLiteralWithIndexingReached(long indexed, int maxIndexed)
+ throws ProtocolException {
+ throw new ProtocolException(String.format("Too many literal with indexing: %s > %s",
+ indexed, maxIndexed));
+ }
}
diff --git a/src/java.net.http/share/classes/jdk/internal/net/http/hpack/Encoder.java b/src/java.net.http/share/classes/jdk/internal/net/http/hpack/Encoder.java
index 4188937b1ad..c603e917ca4 100644
--- a/src/java.net.http/share/classes/jdk/internal/net/http/hpack/Encoder.java
+++ b/src/java.net.http/share/classes/jdk/internal/net/http/hpack/Encoder.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2024, 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
@@ -258,9 +258,10 @@ public void header(CharSequence name,
}
}
}
+ assert encoding : "encoding is false";
}
- private boolean isHuffmanBetterFor(CharSequence value) {
+ protected final boolean isHuffmanBetterFor(CharSequence value) {
// prefer Huffman encoding only if it is strictly smaller than Latin-1
return huffmanWriter.lengthOf(value) < value.length();
}
@@ -340,6 +341,10 @@ protected int calculateCapacity(int maxCapacity) {
return 0;
}
+ protected final int tableIndexOf(CharSequence name, CharSequence value) {
+ return getHeaderTable().indexOf(name, value);
+ }
+
/**
* Encodes the {@linkplain #header(CharSequence, CharSequence) set up}
* header into the given buffer.
@@ -380,6 +385,7 @@ public final boolean encode(ByteBuffer headerBlock) {
writer.reset(); // FIXME: WHY?
encoding = false;
}
+ assert done || encoding : "done: " + done + ", encoding: " + encoding;
return done;
}
@@ -542,4 +548,8 @@ protected final void checkEncoding() { // TODO: better name e.g. checkIfEncoding
"Previous encoding operation hasn't finished yet");
}
}
+
+ protected final Logger logger() {
+ return logger;
+ }
}
diff --git a/src/java.security.jgss/share/classes/javax/security/auth/kerberos/EncryptionKey.java b/src/java.security.jgss/share/classes/javax/security/auth/kerberos/EncryptionKey.java
index 492915f638e..9ca1272dfb0 100644
--- a/src/java.security.jgss/share/classes/javax/security/auth/kerberos/EncryptionKey.java
+++ b/src/java.security.jgss/share/classes/javax/security/auth/kerberos/EncryptionKey.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2024, 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
@@ -168,7 +168,7 @@ public String toString() {
if (destroyed) {
return "Destroyed EncryptionKey";
}
- return "key " + key.toString();
+ return "EncryptionKey: " + key.toString();
}
/**
diff --git a/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KerberosCredMessage.java b/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KerberosCredMessage.java
index c39ae01d913..08b67e0abaf 100644
--- a/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KerberosCredMessage.java
+++ b/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KerberosCredMessage.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, 2024, 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
@@ -27,7 +27,6 @@
import javax.security.auth.Destroyable;
import java.util.Arrays;
-import java.util.Base64;
import java.util.Objects;
/**
@@ -140,8 +139,7 @@ public String toString() {
if (destroyed) {
return "Destroyed KerberosCredMessage";
} else {
- return "KRB_CRED from " + sender + " to " + recipient + ":\n"
- + Base64.getUrlEncoder().encodeToString(message);
+ return "KRB_CRED from " + sender + " to " + recipient;
}
}
diff --git a/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KerberosKey.java b/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KerberosKey.java
index b5874f5637e..43d74c37a6b 100644
--- a/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KerberosKey.java
+++ b/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KerberosKey.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2024, 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
@@ -270,9 +270,9 @@ public String toString() {
if (destroyed) {
return "Destroyed KerberosKey";
}
- return "Kerberos Principal " + principal +
- "Key Version " + versionNum +
- "key " + key.toString();
+ return "KerberosKey: principal " + principal +
+ ", version " + versionNum +
+ ", key " + key.toString();
}
/**
diff --git a/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KeyImpl.java b/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KeyImpl.java
index 59c1a4458f8..caa702c2ed4 100644
--- a/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KeyImpl.java
+++ b/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KeyImpl.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2024, 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
@@ -30,7 +30,8 @@
import javax.crypto.SecretKey;
import javax.security.auth.Destroyable;
import javax.security.auth.DestroyFailedException;
-import sun.security.util.HexDumpEncoder;
+
+import sun.security.jgss.krb5.Krb5Util;
import sun.security.krb5.Asn1Exception;
import sun.security.krb5.PrincipalName;
import sun.security.krb5.EncryptionKey;
@@ -222,15 +223,8 @@ private void readObject(ObjectInputStream ois)
}
public String toString() {
- HexDumpEncoder hd = new HexDumpEncoder();
- return "EncryptionKey: keyType=" + keyType
- + " keyBytes (hex dump)="
- + (keyBytes == null || keyBytes.length == 0 ?
- " Empty Key" :
- '\n' + hd.encodeBuffer(keyBytes)
- + '\n');
-
-
+ return "keyType=" + keyType
+ + ", " + Krb5Util.keyInfo(keyBytes);
}
public int hashCode() {
diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Context.java b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Context.java
index 3cb0bf46cb8..67cd1315886 100644
--- a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Context.java
+++ b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Context.java
@@ -914,15 +914,11 @@ public final int getWrapSizeLimit(int qop, boolean confReq,
public final byte[] wrap(byte[] inBuf, int offset, int len,
MessageProp msgProp) throws GSSException {
- if (DEBUG) {
- System.out.println("Krb5Context.wrap: data=["
- + getHexBytes(inBuf, offset, len)
- + "]");
- }
- if (state != STATE_DONE)
- throw new GSSException(GSSException.NO_CONTEXT, -1,
- "Wrap called in invalid state!");
+ if (state != STATE_DONE) {
+ throw new GSSException(GSSException.NO_CONTEXT, -1,
+ "Wrap called in invalid state!");
+ }
byte[] encToken = null;
try {
@@ -1067,12 +1063,6 @@ public final byte[] unwrap(byte[] inBuf, int offset, int len,
setSequencingAndReplayProps(token, msgProp);
}
- if (DEBUG) {
- System.out.println("Krb5Context.unwrap: data=["
- + getHexBytes(data, 0, data.length)
- + "]");
- }
-
return data;
}
@@ -1423,8 +1413,8 @@ public byte[] getEncoded() {
@Override
public String toString() {
- return "Kerberos session key: etype: " + key.getEType() + "\n" +
- new HexDumpEncoder().encodeBuffer(key.getBytes());
+ return "Kerberos session key: etype=" + key.getEType()
+ + ", " + Krb5Util.keyInfo(key.getBytes());
}
}
diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Util.java b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Util.java
index 24353cceeca..54102205026 100644
--- a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Util.java
+++ b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Util.java
@@ -201,4 +201,19 @@ public static EncryptionKey[] keysFromJavaxKeyTab(
KeyTab ktab, PrincipalName cname) {
return snapshotFromJavaxKeyTab(ktab).readServiceKeys(cname);
}
+
+ public static String keyInfo(byte[] data) {
+ if (data == null) {
+ return "null key";
+ } else if (data.length == 0) {
+ return "empty key";
+ } else {
+ for (byte b : data) {
+ if (b != 0) {
+ return data.length + "-byte key";
+ }
+ }
+ return data.length + "-byte zero key";
+ }
+ }
}
diff --git a/src/java.security.jgss/share/classes/sun/security/krb5/EncryptionKey.java b/src/java.security.jgss/share/classes/sun/security/krb5/EncryptionKey.java
index c7dc72fdebe..d812fd63d2c 100644
--- a/src/java.security.jgss/share/classes/sun/security/krb5/EncryptionKey.java
+++ b/src/java.security.jgss/share/classes/sun/security/krb5/EncryptionKey.java
@@ -31,6 +31,7 @@
package sun.security.krb5;
+import sun.security.jgss.krb5.Krb5Util;
import sun.security.util.*;
import sun.security.krb5.internal.*;
import sun.security.krb5.internal.crypto.*;
@@ -498,12 +499,7 @@ public synchronized void writeKey(CCacheOutputStream cos)
public String toString() {
return new String("EncryptionKey: keyType=" + keyType
- + " kvno=" + kvno
- + " keyValue (hex dump)="
- + (keyValue == null || keyValue.length == 0 ?
- " Empty Key" : '\n'
- + Krb5.hexDumper.encodeBuffer(keyValue)
- + '\n'));
+ + ", kvno=" + kvno + ", " + Krb5Util.keyInfo(keyValue));
}
/**
diff --git a/src/java.security.jgss/share/classes/sun/security/krb5/internal/Krb5.java b/src/java.security.jgss/share/classes/sun/security/krb5/internal/Krb5.java
index fabff57ae64..93831f5a3e6 100644
--- a/src/java.security.jgss/share/classes/sun/security/krb5/internal/Krb5.java
+++ b/src/java.security.jgss/share/classes/sun/security/krb5/internal/Krb5.java
@@ -315,9 +315,6 @@ public static String getErrorMessage(int i) {
public static final boolean DEBUG = GetBooleanAction
.privilegedGetProperty("sun.security.krb5.debug");
- public static final sun.security.util.HexDumpEncoder hexDumper =
- new sun.security.util.HexDumpEncoder();
-
static {
errMsgList = new Hashtable