Reviewed-by: alanb, dfuchs
This commit is contained in:
Jaikiran Pai 2024-10-16 11:36:01 +00:00
commit cf5bb12731
79 changed files with 3346 additions and 911 deletions

View File

@ -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"
@ -212,6 +213,11 @@ 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 (CDSConfig::is_dumping_dynamic_archive()) {
SystemDictionaryShared::warn_excluded(klass, "Failed over class verification while dynamic dumping");
SystemDictionaryShared::set_excluded(klass);
}
message_buffer = NEW_RESOURCE_ARRAY(char, message_buffer_len);
exception_message = message_buffer;
exception_name = inference_verify(

View File

@ -2601,7 +2601,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) {
@ -2610,10 +2610,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) {

View File

@ -416,6 +416,10 @@ VPointer::VPointer(MemNode* const mem, const VLoop& vloop,
#ifdef ASSERT
_debug_invar(nullptr), _debug_negate_invar(false), _debug_invar_scale(nullptr),
#endif
_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
, _tracer(vloop.is_trace_pointer_analysis())
@ -495,6 +499,11 @@ VPointer::VPointer(MemNode* const mem, const VLoop& vloop,
return;
}
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");
@ -508,6 +517,10 @@ VPointer::VPointer(VPointer* p) :
#ifdef ASSERT
_debug_invar(nullptr), _debug_negate_invar(false), _debug_invar_scale(nullptr),
#endif
_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
, _tracer(p->_tracer._is_trace_alignment)
@ -530,6 +543,354 @@ int VPointer::invar_factor() const {
return 1;
}
// 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 VPointer,
// and the associated memop cannot be vectorized.
bool VPointer::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 VPointer 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 VPointer.
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 VPointers are "comparable" (i.e. VPointer::comparable is true, set with VPointer::cmp()),
// iff all of these conditions apply for the simple form:
// 1) Both VPointers 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 VPointers 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 VPointers 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 VPointers 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 VPointers 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 VPointer 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 VPointer 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 VPointer::is_loop_member(Node* n) const {
Node* n_c = phase()->get_ctrl(n);
return lpt()->is_member(phase()->get_loop(n_c));
@ -582,11 +943,40 @@ bool VPointer::scaled_iv_plus_offset(Node* n) {
}
} else if (opc == Op_SubI || opc == Op_SubL) {
if (offset_plus_k(n->in(2), true) && scaled_iv_plus_offset(n->in(1))) {
// (offset1 + invar1 + scale * iv) - (offset2 + invar2)
// 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;
VPointer tmp(this);
if (offset_plus_k(n->in(1)) && tmp.scaled_iv_plus_offset(n->in(2))) {
// (offset1 + invar1) - (offset2 + invar2 + 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) {
maybe_add_to_invar(tmp._invar, true);
#ifdef ASSERT
_debug_invar_scale = tmp._debug_invar_scale;
_debug_negate_invar = !tmp._debug_negate_invar;
#endif
}
// 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_plus_offset_7(n);)
return true;
}
@ -628,10 +1018,52 @@ bool VPointer::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 VPointer, then the int_offset and
// int_invar simply get added to the long offset and invar. But for the checks in
// VPointer::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
// VPointer, to pattern match the int_index sub-expression of the address.
NOT_PRODUCT(Tracer::Depth dddd;)
VPointer 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.
}
// Accumulate invar.
if (tmp._invar != nullptr) {
maybe_add_to_invar(tmp._invar, false);
}
// 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);)
@ -647,9 +1079,20 @@ bool VPointer::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.
if (tmp._invar != nullptr) {
BasicType bt = tmp._invar->bottom_type()->basic_type();
assert(bt == T_INT || bt == T_LONG, "");
@ -658,6 +1101,14 @@ bool VPointer::scaled_iv(Node* n) {
_debug_invar_scale = n->in(2);
#endif
}
// 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);)
return true;
}
@ -675,7 +1126,9 @@ bool VPointer::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) {
@ -684,7 +1137,9 @@ bool VPointer::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;
}
@ -699,11 +1154,15 @@ bool VPointer::offset_plus_k(Node* n, bool negate) {
if (opc == Op_AddI) {
if (n->in(2)->is_Con() && invariant(n->in(1))) {
maybe_add_to_invar(n->in(1), negate);
_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, _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.
}
maybe_add_to_invar(n->in(2), negate);
NOT_PRODUCT(_tracer.offset_plus_k_7(n, _invar, negate, _offset);)
return true;
@ -712,11 +1171,15 @@ bool VPointer::offset_plus_k(Node* n, bool negate) {
if (opc == Op_SubI) {
if (n->in(2)->is_Con() && invariant(n->in(1))) {
maybe_add_to_invar(n->in(1), negate);
_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, _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.
}
maybe_add_to_invar(n->in(2), !negate);
NOT_PRODUCT(_tracer.offset_plus_k_9(n, _invar, !negate, _offset);)
return true;
@ -806,6 +1269,57 @@ void VPointer::maybe_add_to_invar(Node* new_invar, bool negate) {
_invar = register_if_new(add);
}
bool VPointer::try_AddI_no_overflow(int offset1, int offset2, int& result) {
jlong long_offset = java_add((jlong)(offset1), (jlong)(offset2));
jint int_offset = java_add( offset1, offset2);
if (long_offset != int_offset) {
return false;
}
result = int_offset;
return true;
}
bool VPointer::try_SubI_no_overflow(int offset1, int offset2, int& result) {
jlong long_offset = java_subtract((jlong)(offset1), (jlong)(offset2));
jint int_offset = java_subtract( offset1, offset2);
if (long_offset != int_offset) {
return false;
}
result = int_offset;
return true;
}
bool VPointer::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 VPointer::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), shift);
jint int_offset = java_shift_left( offset, shift);
if (long_offset != int_offset) {
return false;
}
result = int_offset;
return true;
}
bool VPointer::try_MulI_no_overflow(int offset1, int offset2, int& result) {
jlong long_offset = java_multiply((jlong)(offset1), (jlong)(offset2));
jint int_offset = java_multiply( offset1, offset2);
if (long_offset != int_offset) {
return false;
}
result = int_offset;
return true;
}
// We use two comparisons, because a subtraction could underflow.
#define RETURN_CMP_VALUE_IF_NOT_EQUAL(a, b) \
if (a < b) { return -1; } \

View File

@ -670,13 +670,51 @@ private:
// A vectorization pointer (VPointer) has information about an address for
// dependence checking and vector alignment. It's usually bound to a memory
// operation in a counted loop for vectorizable analysis.
//
// 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. VPointer::cmp()) we always use the simple form to make
// decisions. Hence, we must make sure to only create a "valid" VPointer 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 VPointer::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 VPointer : public ArenaObj {
protected:
MemNode* const _mem; // My memory reference node
const VLoop& _vloop;
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)
@ -687,6 +725,13 @@ class VPointer : public ArenaObj {
Node* _debug_invar_scale; // multiplier for invariant
#endif
// 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 vpointer trace of variants
bool _analyze_only; // Used in loop unrolling only for vpointer trace
uint _stack_idx; // Used in loop unrolling only for vpointer trace
@ -726,6 +771,8 @@ class VPointer : public ArenaObj {
VPointer(VPointer* p);
NONCOPYABLE(VPointer);
bool is_safe_to_use_as_simple_form(Node* base, Node* adr) const;
public:
bool valid() const { return _adr != nullptr; }
bool has_iv() const { return _scale != 0; }
@ -751,10 +798,43 @@ class VPointer : public ArenaObj {
return _invar == q._invar;
}
// We compute if and how two VPointers 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 VPointers 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 VPointers 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 VPointers point to entirely different objects.
// -> No memory edge required. Aliasing impossible.
//
// - Equal: Synonymous to "overlap, or point to different memory objects".
// The two VPointers 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 VPointer 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(const VPointer& q) const {
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);
@ -859,6 +939,12 @@ class VPointer : public ArenaObj {
void maybe_add_to_invar(Node* new_invar, bool negate);
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);
Node* register_if_new(Node* n) const;
};

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 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
@ -56,6 +56,7 @@ final class DHPrivateKey implements PrivateKey,
private final BigInteger x;
// the key bytes, without the algorithm information
// cannot be final as it's re-assigned for deserialization
private byte[] key;
// the encoded key
@ -70,6 +71,131 @@ final class DHPrivateKey implements PrivateKey,
// the private-value length (optional)
private final int l;
private static class DHComponents {
final BigInteger x;
final BigInteger p;
final BigInteger g;
final int l;
final byte[] key;
DHComponents(BigInteger x, BigInteger p, BigInteger g, int l,
byte[] key) {
this.x = x;
this.p = p;
this.g = g;
this.l = l;
this.key = key;
}
}
// parses the specified encoding into a DHComponents object
private static DHComponents decode(byte[] encodedKey)
throws IOException {
DerValue val = null;
try {
val = new DerValue(encodedKey);
if (val.tag != DerValue.tag_Sequence) {
throw new IOException("Key not a SEQUENCE");
}
// version
BigInteger parsedVersion = val.data.getBigInteger();
if (!parsedVersion.equals(PKCS8_VERSION)) {
throw new IOException("version mismatch: (supported: " +
PKCS8_VERSION + ", parsed: " + parsedVersion);
}
// privateKeyAlgorithm
DerValue algid = val.data.getDerValue();
if (algid.tag != DerValue.tag_Sequence) {
throw new IOException("AlgId is not a SEQUENCE");
}
DerInputStream derInStream = algid.toDerInputStream();
ObjectIdentifier oid = derInStream.getOID();
if (oid == null) {
throw new IOException("Null OID");
}
if (derInStream.available() == 0) {
throw new IOException("Parameters missing");
}
// parse the parameters
DerValue params = derInStream.getDerValue();
if (params.tag == DerValue.tag_Null) {
throw new IOException("Null parameters");
}
if (params.tag != DerValue.tag_Sequence) {
throw new IOException("Parameters not a SEQUENCE");
}
params.data.reset();
BigInteger p = params.data.getBigInteger();
BigInteger g = params.data.getBigInteger();
// Private-value length is OPTIONAL
int l = (params.data.available() != 0 ?
params.data.getInteger() : 0);
// should have no trailing data
if (params.data.available() != 0) {
throw new IOException("Extra parameter data");
}
// privateKey
byte[] key = val.data.getOctetString();
DerInputStream in = new DerInputStream(key);
BigInteger x = in.getBigInteger();
// should have no trailing data
if (val.data.available() != 0) {
throw new IOException("Excess trailing data");
}
return new DHComponents(x, p, g, l, key);
} catch (NumberFormatException e) {
throw new IOException("Error parsing key encoding", e);
} finally {
if (val != null) {
val.clear();
}
}
}
// Generates the ASN.1 encoding
private static byte[] encode(BigInteger p, BigInteger g, int l,
byte[] key) {
DerOutputStream tmp = new DerOutputStream();
// version
tmp.putInteger(PKCS8_VERSION);
// privateKeyAlgorithm
DerOutputStream algid = new DerOutputStream();
// store OID
algid.putOID(DHPublicKey.DH_OID);
// encode parameters
DerOutputStream params = new DerOutputStream();
params.putInteger(p);
params.putInteger(g);
if (l != 0) {
params.putInteger(l);
}
// wrap parameters into SEQUENCE
DerValue paramSequence = new DerValue(DerValue.tag_Sequence,
params.toByteArray());
// store parameter SEQUENCE in algid
algid.putDerValue(paramSequence);
// wrap algid into SEQUENCE
tmp.write(DerValue.tag_Sequence, algid);
// privateKey
tmp.putOctetString(key);
// make it a SEQUENCE
DerValue val = DerValue.wrap(DerValue.tag_Sequence, tmp);
byte[] encoded = val.toByteArray();
val.clear();
return encoded;
}
/**
* Make a DH private key out of a private value <code>x</code>, a prime
* modulus <code>p</code>, and a base generator <code>g</code>.
@ -79,7 +205,7 @@ final class DHPrivateKey implements PrivateKey,
* @param g the base generator
*/
DHPrivateKey(BigInteger x, BigInteger p, BigInteger g)
throws InvalidKeyException {
throws InvalidKeyException {
this(x, p, g, 0);
}
@ -98,12 +224,16 @@ final class DHPrivateKey implements PrivateKey,
this.p = p;
this.g = g;
this.l = l;
byte[] xbytes = x.toByteArray();
DerValue val = new DerValue(DerValue.tag_Integer, xbytes);
this.key = val.toByteArray();
val.clear();
Arrays.fill(xbytes, (byte) 0);
encode();
try {
this.key = val.toByteArray();
} finally {
val.clear();
Arrays.fill(xbytes, (byte) 0);
}
this.encodedKey = encode(p, g, l, key);
}
/**
@ -115,75 +245,18 @@ final class DHPrivateKey implements PrivateKey,
* a Diffie-Hellman private key
*/
DHPrivateKey(byte[] encodedKey) throws InvalidKeyException {
DerValue val = null;
this.encodedKey = encodedKey.clone();
DHComponents dc;
try {
val = new DerValue(encodedKey);
if (val.tag != DerValue.tag_Sequence) {
throw new InvalidKeyException ("Key not a SEQUENCE");
}
//
// version
//
BigInteger parsedVersion = val.data.getBigInteger();
if (!parsedVersion.equals(PKCS8_VERSION)) {
throw new IOException("version mismatch: (supported: " +
PKCS8_VERSION + ", parsed: " +
parsedVersion);
}
//
// privateKeyAlgorithm
//
DerValue algid = val.data.getDerValue();
if (algid.tag != DerValue.tag_Sequence) {
throw new InvalidKeyException("AlgId is not a SEQUENCE");
}
DerInputStream derInStream = algid.toDerInputStream();
ObjectIdentifier oid = derInStream.getOID();
if (oid == null) {
throw new InvalidKeyException("Null OID");
}
if (derInStream.available() == 0) {
throw new InvalidKeyException("Parameters missing");
}
// parse the parameters
DerValue params = derInStream.getDerValue();
if (params.tag == DerValue.tag_Null) {
throw new InvalidKeyException("Null parameters");
}
if (params.tag != DerValue.tag_Sequence) {
throw new InvalidKeyException("Parameters not a SEQUENCE");
}
params.data.reset();
this.p = params.data.getBigInteger();
this.g = params.data.getBigInteger();
// Private-value length is OPTIONAL
if (params.data.available() != 0) {
this.l = params.data.getInteger();
} else {
this.l = 0;
}
if (params.data.available() != 0) {
throw new InvalidKeyException("Extra parameter data");
}
//
// privateKey
//
this.key = val.data.getOctetString();
DerInputStream in = new DerInputStream(this.key);
this.x = in.getBigInteger();
this.encodedKey = encodedKey.clone();
} catch (IOException | NumberFormatException e) {
throw new InvalidKeyException("Error parsing key encoding", e);
} finally {
if (val != null) {
val.clear();
}
dc = decode(this.encodedKey);
} catch (IOException e) {
throw new InvalidKeyException("Invalid encoding", e);
}
this.x = dc.x;
this.p = dc.p;
this.g = dc.g;
this.l = dc.l;
this.key = dc.key;
}
/**
@ -204,55 +277,9 @@ final class DHPrivateKey implements PrivateKey,
* Get the encoding of the key.
*/
public synchronized byte[] getEncoded() {
encode();
return encodedKey.clone();
}
/**
* Generate the encodedKey field if it has not been calculated.
* Could generate null.
*/
private void encode() {
if (this.encodedKey == null) {
DerOutputStream tmp = new DerOutputStream();
//
// version
//
tmp.putInteger(PKCS8_VERSION);
//
// privateKeyAlgorithm
//
DerOutputStream algid = new DerOutputStream();
// store OID
algid.putOID(DHPublicKey.DH_OID);
// encode parameters
DerOutputStream params = new DerOutputStream();
params.putInteger(this.p);
params.putInteger(this.g);
if (this.l != 0) {
params.putInteger(this.l);
}
// wrap parameters into SEQUENCE
DerValue paramSequence = new DerValue(DerValue.tag_Sequence,
params.toByteArray());
// store parameter SEQUENCE in algid
algid.putDerValue(paramSequence);
// wrap algid into SEQUENCE
tmp.write(DerValue.tag_Sequence, algid);
// privateKey
tmp.putOctetString(this.key);
// make it a SEQUENCE
DerValue val = DerValue.wrap(DerValue.tag_Sequence, tmp);
this.encodedKey = val.toByteArray();
val.clear();
}
}
/**
* Returns the private value, <code>x</code>.
*
@ -307,10 +334,7 @@ final class DHPrivateKey implements PrivateKey,
*/
@java.io.Serial
private Object writeReplace() throws java.io.ObjectStreamException {
encode();
return new KeyRep(KeyRep.Type.PRIVATE,
getAlgorithm(),
getFormat(),
return new KeyRep(KeyRep.Type.PRIVATE, getAlgorithm(), getFormat(),
encodedKey);
}
@ -330,11 +354,28 @@ final class DHPrivateKey implements PrivateKey,
if ((key == null) || (key.length == 0)) {
throw new InvalidObjectException("key not deserializable");
}
this.key = key.clone();
if ((encodedKey == null) || (encodedKey.length == 0)) {
throw new InvalidObjectException(
"encoded key not deserializable");
}
this.encodedKey = encodedKey.clone();
// check if the "encodedKey" value matches the deserialized fields
DHComponents c;
byte[] encodedKeyIntern = encodedKey.clone();
try {
c = decode(encodedKeyIntern);
} catch (IOException e) {
throw new InvalidObjectException("Invalid encoding", e);
}
if (!Arrays.equals(c.key, key) || !c.x.equals(x) || !c.p.equals(p)
|| !c.g.equals(g) || c.l != l) {
throw new InvalidObjectException(
"encoded key not matching internal fields");
}
// zero out external arrays
Arrays.fill(key, (byte)0x00);
Arrays.fill(encodedKey, (byte)0x00);
// use self-created internal copies
this.key = c.key;
this.encodedKey = encodedKeyIntern;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 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
@ -26,6 +26,7 @@
package com.sun.crypto.provider;
import java.io.*;
import java.util.Arrays;
import java.util.Objects;
import java.math.BigInteger;
import java.security.KeyRep;
@ -70,6 +71,116 @@ javax.crypto.interfaces.DHPublicKey, Serializable {
static final ObjectIdentifier DH_OID =
ObjectIdentifier.of(KnownOIDs.DiffieHellman);
private static class DHComponents {
final BigInteger y;
final BigInteger p;
final BigInteger g;
final int l;
final byte[] key;
DHComponents(BigInteger y, BigInteger p, BigInteger g, int l,
byte[] key) {
this.y = y;
this.p = p;
this.g = g;
this.l = l;
this.key = key;
}
}
// parses the specified encoding into a DHComponents object
private static DHComponents decode(byte[] encodedKey)
throws IOException {
DerValue val = null;
try {
val = new DerValue(encodedKey);
if (val.tag != DerValue.tag_Sequence) {
throw new IOException("Invalid key format");
}
// algorithm identifier
DerValue algid = val.data.getDerValue();
if (algid.tag != DerValue.tag_Sequence) {
throw new IOException("AlgId is not a SEQUENCE");
}
DerInputStream derInStream = algid.toDerInputStream();
ObjectIdentifier oid = derInStream.getOID();
if (oid == null) {
throw new IOException("Null OID");
}
if (derInStream.available() == 0) {
throw new IOException("Parameters missing");
}
// parse the parameters
DerValue params = derInStream.getDerValue();
if (params.tag == DerValue.tag_Null) {
throw new IOException("Null parameters");
}
if (params.tag != DerValue.tag_Sequence) {
throw new IOException("Parameters not a SEQUENCE");
}
params.data.reset();
BigInteger p = params.data.getBigInteger();
BigInteger g = params.data.getBigInteger();
// Private-value length is OPTIONAL
int l = (params.data.available() != 0 ? params.data.getInteger() :
0);
if (params.data.available() != 0) {
throw new IOException("Extra parameter data");
}
// publickey
byte[] key = val.data.getBitString();
DerInputStream in = new DerInputStream(key);
BigInteger y = in.getBigInteger();
if (val.data.available() != 0) {
throw new IOException("Excess key data");
}
return new DHComponents(y, p, g, l, key);
} catch (NumberFormatException e) {
throw new IOException("Error parsing key encoding", e);
}
}
// generates the ASN.1 encoding
private static byte[] encode(BigInteger p, BigInteger g, int l,
byte[] key) {
DerOutputStream algid = new DerOutputStream();
// store oid in algid
algid.putOID(DH_OID);
// encode parameters
DerOutputStream params = new DerOutputStream();
params.putInteger(p);
params.putInteger(g);
if (l != 0) {
params.putInteger(l);
}
// wrap parameters into SEQUENCE
DerValue paramSequence = new DerValue(DerValue.tag_Sequence,
params.toByteArray());
// store parameter SEQUENCE in algid
algid.putDerValue(paramSequence);
// wrap algid into SEQUENCE, and store it in key encoding
DerOutputStream tmpDerKey = new DerOutputStream();
tmpDerKey.write(DerValue.tag_Sequence, algid);
// store key data
tmpDerKey.putBitString(key);
// wrap algid and key into SEQUENCE
DerOutputStream derKey = new DerOutputStream();
derKey.write(DerValue.tag_Sequence, tmpDerKey);
return derKey.toByteArray();
}
/**
* Make a DH public key out of a public value <code>y</code>, a prime
* modulus <code>p</code>, and a base generator <code>g</code>.
@ -102,7 +213,7 @@ javax.crypto.interfaces.DHPublicKey, Serializable {
this.l = l;
this.key = new DerValue(DerValue.tag_Integer,
this.y.toByteArray()).toByteArray();
this.encodedKey = getEncoded();
this.encodedKey = encode(p, g, l, key);
}
/**
@ -114,68 +225,19 @@ javax.crypto.interfaces.DHPublicKey, Serializable {
* a Diffie-Hellman public key
*/
DHPublicKey(byte[] encodedKey) throws InvalidKeyException {
InputStream inStream = new ByteArrayInputStream(encodedKey);
this.encodedKey = encodedKey.clone();
DHComponents dc;
try {
DerValue derKeyVal = new DerValue(inStream);
if (derKeyVal.tag != DerValue.tag_Sequence) {
throw new InvalidKeyException ("Invalid key format");
}
/*
* Parse the algorithm identifier
*/
DerValue algid = derKeyVal.data.getDerValue();
if (algid.tag != DerValue.tag_Sequence) {
throw new InvalidKeyException("AlgId is not a SEQUENCE");
}
DerInputStream derInStream = algid.toDerInputStream();
ObjectIdentifier oid = derInStream.getOID();
if (oid == null) {
throw new InvalidKeyException("Null OID");
}
if (derInStream.available() == 0) {
throw new InvalidKeyException("Parameters missing");
}
/*
* Parse the parameters
*/
DerValue params = derInStream.getDerValue();
if (params.tag == DerValue.tag_Null) {
throw new InvalidKeyException("Null parameters");
}
if (params.tag != DerValue.tag_Sequence) {
throw new InvalidKeyException("Parameters not a SEQUENCE");
}
params.data.reset();
this.p = params.data.getBigInteger();
this.g = params.data.getBigInteger();
// Private-value length is OPTIONAL
if (params.data.available() != 0) {
this.l = params.data.getInteger();
} else {
this.l = 0;
}
if (params.data.available() != 0) {
throw new InvalidKeyException("Extra parameter data");
}
/*
* Parse the key
*/
this.key = derKeyVal.data.getBitString();
DerInputStream in = new DerInputStream(this.key);
this.y = in.getBigInteger();
if (derKeyVal.data.available() != 0) {
throw new InvalidKeyException("Excess key data");
}
this.encodedKey = encodedKey.clone();
} catch (IOException | NumberFormatException e) {
throw new InvalidKeyException("Error parsing key encoding", e);
dc = decode(this.encodedKey);
} catch (IOException e) {
throw new InvalidKeyException("Invalid encoding", e);
}
this.y = dc.y;
this.p = dc.p;
this.g = dc.g;
this.l = dc.l;
this.key = dc.key;
}
/**
@ -196,37 +258,6 @@ javax.crypto.interfaces.DHPublicKey, Serializable {
* Get the encoding of the key.
*/
public synchronized byte[] getEncoded() {
if (this.encodedKey == null) {
DerOutputStream algid = new DerOutputStream();
// store oid in algid
algid.putOID(DH_OID);
// encode parameters
DerOutputStream params = new DerOutputStream();
params.putInteger(this.p);
params.putInteger(this.g);
if (this.l != 0) {
params.putInteger(this.l);
}
// wrap parameters into SEQUENCE
DerValue paramSequence = new DerValue(DerValue.tag_Sequence,
params.toByteArray());
// store parameter SEQUENCE in algid
algid.putDerValue(paramSequence);
// wrap algid into SEQUENCE, and store it in key encoding
DerOutputStream tmpDerKey = new DerOutputStream();
tmpDerKey.write(DerValue.tag_Sequence, algid);
// store key data
tmpDerKey.putBitString(this.key);
// wrap algid and key into SEQUENCE
DerOutputStream derKey = new DerOutputStream();
derKey.write(DerValue.tag_Sequence, tmpDerKey);
this.encodedKey = derKey.toByteArray();
}
return this.encodedKey.clone();
}
@ -263,8 +294,9 @@ javax.crypto.interfaces.DHPublicKey, Serializable {
+ Debug.toHexString(this.p)
+ LINE_SEP + "g:" + LINE_SEP
+ Debug.toHexString(this.g));
if (this.l != 0)
if (this.l != 0) {
sb.append(LINE_SEP + "l:" + LINE_SEP + " " + this.l);
}
return sb.toString();
}
@ -304,7 +336,7 @@ javax.crypto.interfaces.DHPublicKey, Serializable {
return new KeyRep(KeyRep.Type.PUBLIC,
getAlgorithm(),
getFormat(),
getEncoded());
encodedKey);
}
/**
@ -323,11 +355,28 @@ javax.crypto.interfaces.DHPublicKey, Serializable {
if ((key == null) || (key.length == 0)) {
throw new InvalidObjectException("key not deserializable");
}
this.key = key.clone();
if ((encodedKey == null) || (encodedKey.length == 0)) {
throw new InvalidObjectException(
"encoded key not deserializable");
}
this.encodedKey = encodedKey.clone();
// check if the "encodedKey" value matches the deserialized fields
DHComponents c;
byte[] encodedKeyIntern = encodedKey.clone();
try {
c = decode(encodedKeyIntern);
} catch (IOException e) {
throw new InvalidObjectException("Invalid encoding", e);
}
if (!Arrays.equals(c.key, key) || !c.y.equals(y) || !c.p.equals(p)
|| !c.g.equals(g) || c.l != l) {
throw new InvalidObjectException(
"encoded key not matching internal fields");
}
// zero out external arrays
Arrays.fill(key, (byte)0x00);
Arrays.fill(encodedKey, (byte)0x00);
// use self-created internal copies
this.key = c.key;
this.encodedKey = encodedKeyIntern;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 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
@ -194,22 +194,24 @@ public final class TlsMasterSecretGenerator extends KeyGeneratorSpi {
return key.clone();
}
/**
* Restores the state of this object from the stream.
*
* @param stream the {@code ObjectInputStream} from which data is read
* @throws IOException if an I/O error occurs
* @throws ClassNotFoundException if a serialized class cannot be loaded
*/
@java.io.Serial
private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException {
stream.defaultReadObject();
if ((key == null) || (key.length == 0)) {
throw new InvalidObjectException("TlsMasterSecretKey is null");
}
key = key.clone();
}
}
/**
* Restores the state of this object from the stream.
*
* @param stream the {@code ObjectInputStream} from which data is read
* @throws IOException if an I/O error occurs
* @throws ClassNotFoundException if a serialized class cannot be loaded
*/
@java.io.Serial
private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException {
stream.defaultReadObject();
if (key == null || key.length == 0) {
throw new InvalidObjectException("TlsMasterSecretKey is null");
}
byte[] temp = key;
this.key = temp.clone();
Arrays.fill(temp, (byte)0);
}
}
}

View File

@ -253,6 +253,15 @@ to determine the proxy that should be used for connecting to a given URI.</P>
</OL>
<P>The channel binding tokens generated are of the type "tls-server-end-point" as defined in
RFC 5929.</P>
<LI><P><B>{@systemProperty jdk.http.maxHeaderSize}</B> (default: 393216 or 384kB)<BR>
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.
</UL>
<P>All these properties are checked only once at startup.</P>
<a id="AddressCache"></a>

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 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
@ -407,6 +407,11 @@ implements Serializable
@SuppressWarnings("unchecked")
Hashtable<Class<?>, PermissionCollection> perms =
(Hashtable<Class<?>, PermissionCollection>)gfields.get("perms", null);
if (perms == null) {
throw new InvalidObjectException("perms can't be null");
}
permsMap = new ConcurrentHashMap<>(perms.size()*2);
permsMap.putAll(perms);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 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
@ -152,20 +152,20 @@ public final class SignedObject implements Serializable {
*/
public SignedObject(Serializable object, PrivateKey signingKey,
Signature signingEngine)
throws IOException, InvalidKeyException, SignatureException {
// creating a stream pipe-line, from a to b
ByteArrayOutputStream b = new ByteArrayOutputStream();
ObjectOutput a = new ObjectOutputStream(b);
throws IOException, InvalidKeyException, SignatureException {
// creating a stream pipe-line, from a to b
ByteArrayOutputStream b = new ByteArrayOutputStream();
ObjectOutput a = new ObjectOutputStream(b);
// write and flush the object content to byte array
a.writeObject(object);
a.flush();
a.close();
this.content = b.toByteArray();
b.close();
// write and flush the object content to byte array
a.writeObject(object);
a.flush();
a.close();
this.content = b.toByteArray();
b.close();
// now sign the encapsulated object
this.sign(signingKey, signingEngine);
// now sign the encapsulated object
this.sign(signingKey, signingEngine);
}
/**
@ -245,12 +245,12 @@ public final class SignedObject implements Serializable {
* @throws SignatureException if signing fails.
*/
private void sign(PrivateKey signingKey, Signature signingEngine)
throws InvalidKeyException, SignatureException {
// initialize the signing engine
signingEngine.initSign(signingKey);
signingEngine.update(this.content.clone());
this.signature = signingEngine.sign().clone();
this.thealgorithm = signingEngine.getAlgorithm();
throws InvalidKeyException, SignatureException {
// initialize the signing engine
signingEngine.initSign(signingKey);
signingEngine.update(this.content.clone());
this.signature = signingEngine.sign();
this.thealgorithm = signingEngine.getAlgorithm();
}
/**
@ -263,10 +263,16 @@ public final class SignedObject implements Serializable {
*/
@Serial
private void readObject(ObjectInputStream s)
throws IOException, ClassNotFoundException {
ObjectInputStream.GetField fields = s.readFields();
content = ((byte[])fields.get("content", null)).clone();
signature = ((byte[])fields.get("signature", null)).clone();
thealgorithm = (String)fields.get("thealgorithm", null);
throws IOException, ClassNotFoundException {
ObjectInputStream.GetField fields = s.readFields();
byte[] c = (byte[]) fields.get("content", null);
byte[] sig = (byte[]) fields.get("signature", null);
String a = (String) fields.get("thealgorithm", null);
if (c == null || sig == null || a == null) {
throw new InvalidObjectException("One or more null fields");
}
content = c.clone();
signature = sig.clone();
thealgorithm = a;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 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,6 +27,7 @@ package java.security;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.InvalidObjectException;
import java.io.Serializable;
import java.security.cert.CertPath;
import java.security.cert.Certificate;
@ -78,7 +79,7 @@ public final class Timestamp implements Serializable {
* {@code null}.
*/
public Timestamp(Date timestamp, CertPath signerCertPath) {
if (timestamp == null || signerCertPath == null) {
if (isNull(timestamp, signerCertPath)) {
throw new NullPointerException();
}
this.timestamp = new Date(timestamp.getTime()); // clone
@ -166,9 +167,16 @@ public final class Timestamp implements Serializable {
*/
@java.io.Serial
private void readObject(ObjectInputStream ois)
throws IOException, ClassNotFoundException {
throws IOException, ClassNotFoundException {
ois.defaultReadObject();
if (isNull(timestamp, signerCertPath)) {
throw new InvalidObjectException("Invalid null field(s)");
}
myhash = -1;
timestamp = new Date(timestamp.getTime());
}
private static boolean isNull(Date d, CertPath c) {
return (d == null || c == null);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 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
@ -29,6 +29,7 @@ import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamField;
import java.io.InvalidObjectException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
@ -196,23 +197,32 @@ implements java.io.Serializable
ObjectInputStream.GetField gfields = in.readFields();
// Get permissions
@SuppressWarnings("unchecked")
// writeObject writes a Hashtable<String, Vector<UnresolvedPermission>>
// for the permissions key, so this cast is safe, unless the data is corrupt.
Hashtable<String, Vector<UnresolvedPermission>> permissions =
(Hashtable<String, Vector<UnresolvedPermission>>)
gfields.get("permissions", null);
perms = new ConcurrentHashMap<>(permissions.size()*2);
try {
@SuppressWarnings("unchecked")
Hashtable<String, Vector<UnresolvedPermission>> permissions =
(Hashtable<String, Vector<UnresolvedPermission>>)
gfields.get("permissions", null);
// Convert each entry (Vector) into a List
Set<Map.Entry<String, Vector<UnresolvedPermission>>> set = permissions.entrySet();
for (Map.Entry<String, Vector<UnresolvedPermission>> e : set) {
// Convert Vector into ArrayList
Vector<UnresolvedPermission> vec = e.getValue();
List<UnresolvedPermission> list = new CopyOnWriteArrayList<>(vec);
if (permissions == null) {
throw new InvalidObjectException("Invalid null permissions");
}
// Add to Hashtable being serialized
perms.put(e.getKey(), list);
perms = new ConcurrentHashMap<>(permissions.size()*2);
// Convert each entry (Vector) into a List
Set<Map.Entry<String, Vector<UnresolvedPermission>>> set = permissions.entrySet();
for (Map.Entry<String, Vector<UnresolvedPermission>> e : set) {
// Convert Vector into ArrayList
Vector<UnresolvedPermission> vec = e.getValue();
List<UnresolvedPermission> list = new CopyOnWriteArrayList<>(vec);
// Add to Hashtable being serialized
perms.put(e.getKey(), list);
}
} catch (ClassCastException cce) {
throw new InvalidObjectException("Invalid type for permissions");
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2007, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2007, 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
@ -28,6 +28,7 @@ package java.security.cert;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
@ -70,6 +71,13 @@ public class CertificateRevokedException extends CertificateException {
private transient Map<String, Extension> extensions;
private static boolean isNull(Date revocationDate,
CRLReason reason, X500Principal authority,
Map<String, Extension> extensions) {
return (revocationDate == null || reason == null || authority == null
|| extensions == null);
}
/**
* Constructs a {@code CertificateRevokedException} with
* the specified revocation date, reason code, authority name, and map
@ -92,8 +100,7 @@ public class CertificateRevokedException extends CertificateException {
*/
public CertificateRevokedException(Date revocationDate, CRLReason reason,
X500Principal authority, Map<String, Extension> extensions) {
if (revocationDate == null || reason == null || authority == null ||
extensions == null) {
if (isNull(revocationDate, reason, authority, extensions)) {
throw new NullPointerException();
}
this.revocationDate = new Date(revocationDate.getTime());
@ -234,9 +241,6 @@ public class CertificateRevokedException extends CertificateException {
// (revocationDate, reason, authority)
ois.defaultReadObject();
// Defensively copy the revocation date
revocationDate = new Date(revocationDate.getTime());
// Read in the size (number of mappings) of the extensions map
// and create the extensions map
int size = ois.readInt();
@ -247,6 +251,13 @@ public class CertificateRevokedException extends CertificateException {
} else {
extensions = HashMap.newHashMap(Math.min(size, 20));
}
// make sure all fields are set before checking
if (isNull(revocationDate, reason, authority, extensions)) {
throw new InvalidObjectException("Invalid null field(s)");
}
// Defensively copy the revocation date
revocationDate = new Date(revocationDate.getTime());
// Read in the extensions and put the mappings in the extensions map
for (int i = 0; i < size; i++) {

View File

@ -41,6 +41,7 @@ package java.text;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectStreamException;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
@ -1181,6 +1182,8 @@ public class MessageFormat extends Format {
maximumArgumentNumber = argumentNumbers[i];
}
}
// Constructors/applyPattern ensure that resultArray.length < MAX_ARGUMENT_INDEX
Object[] resultArray = new Object[maximumArgumentNumber + 1];
int patternOffset = 0;
@ -1459,6 +1462,9 @@ public class MessageFormat extends Format {
* @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
@ -1639,6 +1645,11 @@ public class MessageFormat extends Format {
+ 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;
@ -2006,24 +2017,53 @@ public class MessageFormat extends Format {
*/
@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");
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1998, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1998, 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
@ -100,11 +100,9 @@ public class SecretKeySpec implements KeySpec, SecretKey {
* is null or <code>key</code> is null or empty.
*/
public SecretKeySpec(byte[] key, String algorithm) {
if (key == null || algorithm == null) {
throw new IllegalArgumentException("Missing argument");
}
if (key.length == 0) {
throw new IllegalArgumentException("Empty key");
String errMsg = doSanityCheck(key, algorithm);
if (errMsg != null) {
throw new IllegalArgumentException(errMsg);
}
this.key = key.clone();
this.algorithm = algorithm;
@ -266,14 +264,22 @@ public class SecretKeySpec implements KeySpec, SecretKey {
private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException {
stream.defaultReadObject();
String errMsg = doSanityCheck(key, algorithm);
if (errMsg != null) {
throw new InvalidObjectException(errMsg);
}
byte[] temp = key;
this.key = temp.clone();
Arrays.fill(temp, (byte) 0);
}
private static String doSanityCheck(byte[] key, String algorithm) {
String errMsg = null;
if (key == null || algorithm == null) {
throw new InvalidObjectException("Missing argument");
}
this.key = key.clone();
if (key.length == 0) {
throw new InvalidObjectException("Invalid key length");
errMsg = "Missing argument";
} else if (key.length == 0) {
errMsg = "Empty key";
}
return errMsg;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 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
@ -102,20 +102,18 @@ public class ChoiceCallback implements Callback, java.io.Serializable {
public ChoiceCallback(String prompt, String[] choices,
int defaultChoice, boolean multipleSelectionsAllowed) {
if (prompt == null || prompt.isEmpty() ||
choices == null || choices.length == 0 ||
defaultChoice < 0 || defaultChoice >= choices.length)
throw new IllegalArgumentException();
choices = (choices == null || choices.length == 0 ? choices :
choices.clone());
String errMsg = doSanityCheck(prompt, choices, defaultChoice,
multipleSelectionsAllowed);
if (errMsg != null) {
throw new IllegalArgumentException(errMsg);
}
this.prompt = prompt;
this.defaultChoice = defaultChoice;
this.multipleSelectionsAllowed = multipleSelectionsAllowed;
this.choices = choices.clone();
for (int i = 0; i < choices.length; i++) {
if (choices[i] == null || choices[i].isEmpty())
throw new IllegalArgumentException();
}
this.choices = choices;
}
/**
@ -183,9 +181,11 @@ public class ChoiceCallback implements Callback, java.io.Serializable {
* @see #getSelectedIndexes
*/
public void setSelectedIndexes(int[] selections) {
if (!multipleSelectionsAllowed)
if (!multipleSelectionsAllowed) {
throw new UnsupportedOperationException();
this.selections = selections == null ? null : selections.clone();
}
this.selections = ((selections == null || selections.length == 0) ?
selections : selections.clone());
}
/**
@ -211,26 +211,35 @@ public class ChoiceCallback implements Callback, java.io.Serializable {
private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException {
stream.defaultReadObject();
choices = (choices == null || choices.length == 0 ?
choices : choices.clone());
String errMsg = doSanityCheck(prompt, choices, defaultChoice,
multipleSelectionsAllowed);
if (errMsg != null) {
throw new InvalidObjectException(errMsg);
}
selections = (selections == null || selections.length == 0 ?
selections : selections.clone());
if (selections != null && selections.length > 1 &&
!multipleSelectionsAllowed) {
throw new InvalidObjectException("Multiple selections not allowed");
}
}
private static String doSanityCheck(String prompt, String[] choices,
int defaultChoice, boolean allowMultiple) {
if ((prompt == null) || prompt.isEmpty() ||
(choices == null) || (choices.length == 0) ||
(defaultChoice < 0) || (defaultChoice >= choices.length)) {
throw new InvalidObjectException(
"Missing/invalid prompt/choices");
return "Missing/invalid prompt/choices";
}
choices = choices.clone();
for (int i = 0; i < choices.length; i++) {
if ((choices[i] == null) || choices[i].isEmpty())
throw new InvalidObjectException("Null/empty choices");
}
if (selections != null) {
selections = selections.clone();
if (!multipleSelectionsAllowed && (selections.length != 1)) {
throw new InvalidObjectException(
"Multiple selections not allowed");
if ((choices[i] == null) || choices[i].isEmpty()) {
return "Null/empty choices value";
}
}
return null;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 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
@ -26,6 +26,7 @@
package javax.security.auth.callback;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
/**
@ -189,25 +190,10 @@ public class ConfirmationCallback implements Callback, java.io.Serializable {
*/
public ConfirmationCallback(int messageType,
int optionType, int defaultOption) {
if (messageType < INFORMATION || messageType > ERROR ||
optionType < YES_NO_OPTION || optionType > OK_CANCEL_OPTION)
throw new IllegalArgumentException();
switch (optionType) {
case YES_NO_OPTION:
if (defaultOption != YES && defaultOption != NO)
throw new IllegalArgumentException();
break;
case YES_NO_CANCEL_OPTION:
if (defaultOption != YES && defaultOption != NO &&
defaultOption != CANCEL)
throw new IllegalArgumentException();
break;
case OK_CANCEL_OPTION:
if (defaultOption != OK && defaultOption != CANCEL)
throw new IllegalArgumentException();
break;
String errMsg = doSanityCheck(messageType, optionType, false, null,
defaultOption, null, false);
if (errMsg != null) {
throw new IllegalArgumentException(errMsg);
}
this.prompt = null;
@ -250,21 +236,20 @@ public class ConfirmationCallback implements Callback, java.io.Serializable {
public ConfirmationCallback(int messageType,
String[] options, int defaultOption) {
if (messageType < INFORMATION || messageType > ERROR ||
options == null || options.length == 0 ||
defaultOption < 0 || defaultOption >= options.length)
throw new IllegalArgumentException();
if (options != null) {
options = options.clone();
}
String errMsg = doSanityCheck(messageType, UNSPECIFIED_OPTION, true,
options, defaultOption, null, false);
if (errMsg != null) {
throw new IllegalArgumentException(errMsg);
}
this.prompt = null;
this.messageType = messageType;
this.optionType = UNSPECIFIED_OPTION;
this.defaultOption = defaultOption;
this.options = options.clone();
for (int i = 0; i < options.length; i++) {
if (options[i] == null || options[i].isEmpty())
throw new IllegalArgumentException();
}
this.options = options;
}
/**
@ -304,27 +289,11 @@ public class ConfirmationCallback implements Callback, java.io.Serializable {
public ConfirmationCallback(String prompt, int messageType,
int optionType, int defaultOption) {
if (prompt == null || prompt.isEmpty() ||
messageType < INFORMATION || messageType > ERROR ||
optionType < YES_NO_OPTION || optionType > OK_CANCEL_OPTION)
throw new IllegalArgumentException();
switch (optionType) {
case YES_NO_OPTION:
if (defaultOption != YES && defaultOption != NO)
throw new IllegalArgumentException();
break;
case YES_NO_CANCEL_OPTION:
if (defaultOption != YES && defaultOption != NO &&
defaultOption != CANCEL)
throw new IllegalArgumentException();
break;
case OK_CANCEL_OPTION:
if (defaultOption != OK && defaultOption != CANCEL)
throw new IllegalArgumentException();
break;
String errMsg = doSanityCheck(messageType, optionType, false, null,
defaultOption, prompt, true);
if (errMsg != null) {
throw new IllegalArgumentException(errMsg);
}
this.prompt = prompt;
this.messageType = messageType;
this.optionType = optionType;
@ -369,22 +338,20 @@ public class ConfirmationCallback implements Callback, java.io.Serializable {
public ConfirmationCallback(String prompt, int messageType,
String[] options, int defaultOption) {
if (prompt == null || prompt.isEmpty() ||
messageType < INFORMATION || messageType > ERROR ||
options == null || options.length == 0 ||
defaultOption < 0 || defaultOption >= options.length)
throw new IllegalArgumentException();
if (options != null) {
options = options.clone();
}
String errMsg = doSanityCheck(messageType, UNSPECIFIED_OPTION, true,
options, defaultOption, prompt, true);
if (errMsg != null) {
throw new IllegalArgumentException(errMsg);
}
this.prompt = prompt;
this.messageType = messageType;
this.optionType = UNSPECIFIED_OPTION;
this.defaultOption = defaultOption;
this.options = options.clone();
for (int i = 0; i < options.length; i++) {
if (options[i] == null || options[i].isEmpty())
throw new IllegalArgumentException();
}
this.options = options;
}
/**
@ -491,6 +458,49 @@ public class ConfirmationCallback implements Callback, java.io.Serializable {
return selection;
}
private static String doSanityCheck(int msgType, int optionType,
boolean isUnspecifiedOption, String[] options, int defOption,
String prompt, boolean checkPrompt) {
// validate msgType
if (msgType < INFORMATION || msgType > ERROR) {
return "Invalid msgType";
}
// validate prompt if checkPrompt == true
if (checkPrompt && (prompt == null || prompt.isEmpty())) {
return "Invalid prompt";
}
// validate optionType
if (isUnspecifiedOption) {
if (optionType != UNSPECIFIED_OPTION) {
return "Invalid optionType";
}
// check options
if (options == null || options.length == 0 ||
defOption < 0 || defOption >= options.length) {
return "Invalid options and/or default option";
}
for (String ov : options) {
if (ov == null || ov.isEmpty()) {
return "Invalid option value";
}
}
} else {
if (optionType < YES_NO_OPTION || optionType > OK_CANCEL_OPTION) {
return "Invalid optionType";
}
// validate defOption based on optionType
if ((optionType == YES_NO_OPTION && (defOption != YES &&
defOption != NO)) ||
(optionType == YES_NO_CANCEL_OPTION && (defOption != YES &&
defOption != NO && defOption != CANCEL)) ||
(optionType == OK_CANCEL_OPTION && (defOption != OK &&
defOption != CANCEL))) {
return "Invalid default option";
}
}
return null;
}
/**
* Restores the state of this object from the stream.
*
@ -502,8 +512,15 @@ public class ConfirmationCallback implements Callback, java.io.Serializable {
private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException {
stream.defaultReadObject();
if (options != null) {
options = options.clone();
}
String errMsg = doSanityCheck(messageType, optionType,
(optionType == UNSPECIFIED_OPTION), options, defaultOption,
prompt, false);
if (errMsg != null) {
throw new InvalidObjectException(errMsg);
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 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
@ -178,7 +178,9 @@ public class PasswordCallback implements Callback, java.io.Serializable {
}
if (inputPassword != null) {
inputPassword = inputPassword.clone();
char[] temp = inputPassword;
inputPassword = temp.clone();
Arrays.fill(temp, '0');
cleanable = CleanerFactory.cleaner().register(
this, cleanerFor(inputPassword));
}

View File

@ -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.*;
@ -45,11 +47,32 @@ public final 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);
}
@ -476,10 +499,28 @@ public final class MessageHeader {
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 {
@ -493,7 +534,15 @@ public final class MessageHeader {
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 ':':
@ -527,6 +576,9 @@ public final class MessageHeader {
s = ns;
}
s[len++] = (char) c;
if (maxHeaderSize > 0 && len > maxRemaining) {
checkMaxHeaderSize(len);
}
}
firstc = -1;
}
@ -548,6 +600,9 @@ public final class MessageHeader {
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);
}
}

View File

@ -172,6 +172,8 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
*/
private static final int bufSize4ES;
private static final int maxHeaderSize;
/*
* Restrict setting of request headers through the public api
* consistent with JavaScript XMLHttpRequest2 with a few
@ -288,6 +290,19 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
} else {
restrictedHeaderSet = null;
}
int defMaxHeaderSize = 384 * 1024;
String maxHeaderSizeStr = getNetProperty("jdk.http.maxHeaderSize");
int maxHeaderSizeVal = defMaxHeaderSize;
if (maxHeaderSizeStr != null) {
try {
maxHeaderSizeVal = Integer.parseInt(maxHeaderSizeStr);
} catch (NumberFormatException n) {
maxHeaderSizeVal = defMaxHeaderSize;
}
}
if (maxHeaderSizeVal < 0) maxHeaderSizeVal = 0;
maxHeaderSize = maxHeaderSizeVal;
}
static final String httpVersion = "HTTP/1.1";
@ -754,7 +769,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
}
ps = (PrintStream) http.getOutputStream();
connected=true;
responses = new MessageHeader();
responses = new MessageHeader(maxHeaderSize);
setRequests=false;
writeRequests();
}
@ -912,7 +927,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
throws IOException {
super(checkURL(u));
requests = new MessageHeader();
responses = new MessageHeader();
responses = new MessageHeader(maxHeaderSize);
userHeaders = new MessageHeader();
this.handler = handler;
instProxy = p;
@ -2810,7 +2825,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
}
// clear out old response headers!!!!
responses = new MessageHeader();
responses = new MessageHeader(maxHeaderSize);
if (stat == HTTP_USE_PROXY) {
/* This means we must re-request the resource through the
* proxy denoted in the "Location:" field of the response.
@ -3000,7 +3015,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
} catch (IOException e) { }
}
responseCode = -1;
responses = new MessageHeader();
responses = new MessageHeader(maxHeaderSize);
connected = false;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 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
@ -26,6 +26,7 @@
package sun.security.provider;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.security.AccessController;
import java.security.DrbgParameters;
import java.security.PrivilegedAction;
@ -272,11 +273,18 @@ public final class DRBG extends SecureRandomSpi {
}
}
/**
* Restores the state of this object from the stream.
*
* @param s the {@code ObjectInputStream} from which data is read
* @throws IOException if an I/O error occurs
* @throws ClassNotFoundException if a serialized class cannot be loaded
*/
@java.io.Serial
private void readObject(java.io.ObjectInputStream s)
throws IOException, ClassNotFoundException {
s.defaultReadObject();
if (mdp.mech == null) {
if (mdp == null || mdp.mech == null) {
throw new IllegalArgumentException("Input data is corrupted");
}
createImpl();

View File

@ -213,8 +213,6 @@ final class ClientHello {
// ignore cookie
hos.putBytes16(getEncodedCipherSuites());
hos.putBytes8(compressionMethod);
extensions.send(hos); // In TLS 1.3, use of certain
// extensions is mandatory.
} catch (IOException ioe) {
// unlikely
}
@ -1426,6 +1424,9 @@ final class ClientHello {
shc.handshakeProducers.put(SSLHandshake.SERVER_HELLO.id,
SSLHandshake.SERVER_HELLO);
// Reset the ClientHello non-zero offset fragment allowance
shc.acceptCliHelloFragments = false;
//
// produce
//

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 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
@ -40,12 +40,23 @@ import sun.security.ssl.SSLCipher.SSLReadCipher;
final class DTLSInputRecord extends InputRecord implements DTLSRecord {
private DTLSReassembler reassembler = null;
private int readEpoch;
private SSLContextImpl sslContext;
DTLSInputRecord(HandshakeHash handshakeHash) {
super(handshakeHash, SSLReadCipher.nullDTlsReadCipher());
this.readEpoch = 0;
}
// Method to set TransportContext
public void setTransportContext(TransportContext tc) {
this.tc = tc;
}
// Method to set SSLContext
public void setSSLContext(SSLContextImpl sslContext) {
this.sslContext = sslContext;
}
@Override
void changeReadCiphers(SSLReadCipher readCipher) {
this.readCipher = readCipher;
@ -537,6 +548,27 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
}
}
/**
* Turn a sufficiently-large initial ClientHello fragment into one that
* stops immediately after the compression methods. This is only used
* for the initial CH message fragment at offset 0.
*
* @param srcFrag the fragment actually received by the DTLSReassembler
* @param limit the size of the new, cloned/truncated handshake fragment
*
* @return a truncated handshake fragment that is sized to look like a
* complete message, but actually contains only up to the compression
* methods (no extensions)
*/
private static HandshakeFragment truncateChFragment(HandshakeFragment srcFrag,
int limit) {
return new HandshakeFragment(Arrays.copyOf(srcFrag.fragment, limit),
srcFrag.contentType, srcFrag.majorVersion,
srcFrag.minorVersion, srcFrag.recordEnS, srcFrag.recordEpoch,
srcFrag.recordSeq, srcFrag.handshakeType, limit,
srcFrag.messageSeq, srcFrag.fragmentOffset, limit);
}
private static final class HoleDescriptor {
int offset; // fragment_offset
int limit; // fragment_offset + fragment_length
@ -640,10 +672,17 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
// Queue up a handshake message.
void queueUpHandshake(HandshakeFragment hsf) throws SSLProtocolException {
if (!isDesirable(hsf)) {
// Not a dedired record, discard it.
// Not a desired record, discard it.
return;
}
if (hsf.handshakeType == SSLHandshake.CLIENT_HELLO.id) {
// validate the first or subsequent ClientHello message
if ((hsf = valHello(hsf, hsf.messageSeq == 0)) == null) {
return;
}
}
// Clean up the retransmission messages if necessary.
cleanUpRetransmit(hsf);
@ -769,6 +808,100 @@ final class DTLSInputRecord extends InputRecord implements DTLSRecord {
}
}
private HandshakeFragment valHello(HandshakeFragment hsf,
boolean firstHello) {
ServerHandshakeContext shc =
(ServerHandshakeContext) tc.handshakeContext;
// Drop any fragment that is not a zero offset until we've received
// a second (or possibly later) CH message that passes the cookie
// check.
if (shc == null || !shc.acceptCliHelloFragments) {
if (hsf.fragmentOffset != 0) {
return null;
}
} else {
// Let this fragment through to the DTLSReassembler as-is
return hsf;
}
try {
ByteBuffer fragmentData = ByteBuffer.wrap(hsf.fragment);
ProtocolVersion pv = ProtocolVersion.valueOf(
Record.getInt16(fragmentData));
if (!pv.isDTLS) {
return null;
}
// Read the random (32 bytes)
if (fragmentData.remaining() < 32) {
if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
SSLLogger.fine("Rejected client hello fragment (bad random len) " +
"fo=" + hsf.fragmentOffset + " fl=" + hsf.fragmentLength);
}
return null;
}
fragmentData.position(fragmentData.position() + 32);
// SessionID
byte[] sessId = Record.getBytes8(fragmentData);
if (sessId.length > 0 &&
!SSLConfiguration.enableDtlsResumeCookie) {
// If we are in a resumption it is possible that the cookie
// exchange will be skipped. This is a server-side setting
// and it is NOT the default. If enableDtlsResumeCookie is
// false though, then we will buffer fragments since there
// is no cookie exchange to execute prior to performing
// reassembly.
return hsf;
}
// Cookie
byte[] cookie = Record.getBytes8(fragmentData);
if (firstHello && cookie.length != 0) {
if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
SSLLogger.fine("Rejected initial client hello fragment (bad cookie len) " +
"fo=" + hsf.fragmentOffset + " fl=" + hsf.fragmentLength);
}
return null;
}
// CipherSuites
Record.getBytes16(fragmentData);
// Compression methods
Record.getBytes8(fragmentData);
// If it's the first fragment, we'll truncate it and push it
// through the reassembler.
if (firstHello) {
return truncateChFragment(hsf, fragmentData.position());
} else {
HelloCookieManager hcMgr = sslContext.
getHelloCookieManager(ProtocolVersion.DTLS10);
ByteBuffer msgFragBuf = ByteBuffer.wrap(hsf.fragment, 0,
fragmentData.position());
ClientHello.ClientHelloMessage chMsg =
new ClientHello.ClientHelloMessage(shc, msgFragBuf, null);
if (!hcMgr.isCookieValid(shc, chMsg, cookie)) {
// Bad cookie check, truncate it and let the ClientHello
// consumer recheck, fail and take the appropriate action.
return truncateChFragment(hsf, fragmentData.position());
} else {
// It's a good cookie, return the original handshake
// fragment and let it go into the DTLSReassembler like
// any other fragment so we can wait for the rest of
// the CH message.
shc.acceptCliHelloFragments = true;
return hsf;
}
}
} catch (IOException ioe) {
if (SSLLogger.isOn && SSLLogger.isOn("verbose")) {
SSLLogger.fine("Rejected client hello fragment " +
"fo=" + hsf.fragmentOffset + " fl=" + hsf.fragmentLength);
}
return null;
}
}
// Queue up a ChangeCipherSpec message
void queueUpChangeCipherSpec(RecordFragment rf)
throws SSLProtocolException {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 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
@ -55,6 +55,7 @@ class ServerHandshakeContext extends HandshakeContext {
CertificateMessage.CertificateEntry currentCertEntry;
private static final long DEFAULT_STATUS_RESP_DELAY = 5000L;
final long statusRespTimeout;
boolean acceptCliHelloFragments = false;
ServerHandshakeContext(SSLContextImpl sslContext,

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 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
@ -156,6 +156,11 @@ final class TransportContext implements ConnectionContext {
this.acc = AccessController.getContext();
this.consumers = new HashMap<>();
if (inputRecord instanceof DTLSInputRecord dtlsInputRecord) {
dtlsInputRecord.setTransportContext(this);
dtlsInputRecord.setSSLContext(this.sslContext);
}
}
// Dispatch plaintext to a specific consumer.

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1996, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1996, 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
@ -127,13 +127,24 @@ public final class ObjectIdentifier implements Serializable {
// Is the component's field calculated?
private transient boolean componentsCalculated = false;
/**
* Restores the state of this object from the stream.
*
* @param is the {@code ObjectInputStream} from which data is read
* @throws IOException if an I/O error occurs
* @throws ClassNotFoundException if a serialized class cannot be loaded
*/
@java.io.Serial
private void readObject(ObjectInputStream is)
throws IOException, ClassNotFoundException {
is.defaultReadObject();
if (encoding == null) { // from an old version
int[] comp = (int[])components;
if (components == null) {
throw new InvalidObjectException("OID components is null");
}
int[] comp = ((int[]) components).clone();
if (componentLen > comp.length) {
componentLen = comp.length;
}
@ -142,7 +153,9 @@ public final class ObjectIdentifier implements Serializable {
// will be performed again in init().
checkOidSize(componentLen);
init(comp, componentLen);
components = comp;
} else {
encoding = encoding.clone(); // defensive copying
checkOidSize(encoding.length);
check(encoding);
}
@ -261,6 +274,7 @@ public final class ObjectIdentifier implements Serializable {
encoding = in.getDerValue().getOID().encoding;
}
// set 'encoding' field based on the specified 'components' and 'length'
private void init(int[] components, int length) throws IOException {
int pos = 0;
byte[] tmp = new byte[length * 5 + 1]; // +1 for empty input

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1996, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1996, 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
@ -25,11 +25,13 @@
package sun.security.x509;
import java.io.ObjectInputStream;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.math.BigInteger;
import java.security.*;
import java.security.interfaces.DSAParams;
import java.util.Arrays;
import sun.security.util.*;
@ -72,33 +74,42 @@ import sun.security.util.*;
*
* @author David Brownell
*/
public final
class AlgIdDSA extends AlgorithmId implements DSAParams
{
public final class AlgIdDSA extends AlgorithmId implements DSAParams {
@java.io.Serial
private static final long serialVersionUID = 3437177836797504046L;
private static class DSAComponents {
private final BigInteger p;
private final BigInteger q;
private final BigInteger g;
DSAComponents(BigInteger p, BigInteger q, BigInteger g) {
this.p = p;
this.q = q;
this.g = g;
}
}
/*
* The three unsigned integer parameters.
*/
private BigInteger p , q, g;
private BigInteger p, q, g;
/** Returns the DSS/DSA parameter "P" */
public BigInteger getP () { return p; }
public BigInteger getP() { return p; }
/** Returns the DSS/DSA parameter "Q" */
public BigInteger getQ () { return q; }
public BigInteger getQ() { return q; }
/** Returns the DSS/DSA parameter "G" */
public BigInteger getG () { return g; }
public BigInteger getG() { return g; }
/**
* Default constructor. The OID and parameters must be
* deserialized before this algorithm ID is used.
*/
@Deprecated
public AlgIdDSA () {}
public AlgIdDSA() {}
/**
* Constructs a DSS/DSA Algorithm ID from numeric parameters.
@ -109,7 +120,7 @@ class AlgIdDSA extends AlgorithmId implements DSAParams
* @param q the DSS/DSA parameter "Q"
* @param g the DSS/DSA parameter "G"
*/
public AlgIdDSA (BigInteger p, BigInteger q, BigInteger g) {
public AlgIdDSA(BigInteger p, BigInteger q, BigInteger g) {
super (DSA_oid);
if (p != null || q != null || g != null) {
@ -120,8 +131,10 @@ class AlgIdDSA extends AlgorithmId implements DSAParams
this.p = p;
this.q = q;
this.g = g;
initializeParams ();
// For algorithm IDs which haven't been created from a DER
// encoded value, need to create DER encoding and store it
// into "encodedParams"
encodedParams = encode(p, q, g);
} catch (IOException e) {
/* this should not happen */
throw new ProviderException ("Construct DSS/DSA Algorithm ID");
@ -133,50 +146,10 @@ class AlgIdDSA extends AlgorithmId implements DSAParams
* Returns "DSA", indicating the Digital Signature Algorithm (DSA) as
* defined by the Digital Signature Standard (DSS), FIPS 186.
*/
public String getName ()
{ return "DSA"; }
/*
* For algorithm IDs which haven't been created from a DER encoded
* value, "params" must be created.
*/
private void initializeParams () throws IOException {
DerOutputStream out = new DerOutputStream();
out.putInteger(p);
out.putInteger(q);
out.putInteger(g);
DerOutputStream result = new DerOutputStream();
result.write(DerValue.tag_Sequence, out);
encodedParams = result.toByteArray();
public String getName() {
return "DSA";
}
/**
* Parses algorithm parameters P, Q, and G. They're found
* in the "params" member, which never needs to be changed.
*/
protected void decodeParams () throws IOException {
if (encodedParams == null) {
throw new IOException("DSA alg params are null");
}
DerValue params = new DerValue(encodedParams);
if (params.tag != DerValue.tag_Sequence) {
throw new IOException("DSA alg parsing error");
}
params.data.reset ();
this.p = params.data.getBigInteger();
this.q = params.data.getBigInteger();
this.g = params.data.getBigInteger();
if (params.data.available () != 0)
throw new IOException ("AlgIdDSA params, extra="+
params.data.available ());
}
/*
* Returns a formatted string describing the parameters.
*/
@ -197,4 +170,44 @@ class AlgIdDSA extends AlgorithmId implements DSAParams
"\n";
}
}
/**
* Restores the state of this object from the stream. Override to check
* on the 'p', 'q', 'g', and 'encodedParams'.
*
* @param stream the {@code ObjectInputStream} from which data is read
* @throws IOException if an I/O error occurs
* @throws ClassNotFoundException if a serialized class cannot be loaded
*/
@java.io.Serial
private void readObject(ObjectInputStream stream) throws IOException {
try {
stream.defaultReadObject();
// if any of the 'p', 'q', 'g', 'encodedParams' is non-null,
// then they must be all non-null w/ matching encoding
if ((p != null || q != null || g != null || encodedParams != null)
&& !Arrays.equals(encodedParams, encode(p, q, g))) {
throw new InvalidObjectException("Invalid DSA alg params");
}
} catch (ClassNotFoundException e) {
throw new IOException(e);
}
}
/*
* Create the DER encoding w/ the specified 'p', 'q', 'g'
*/
private static byte[] encode(BigInteger p, BigInteger q,
BigInteger g) throws IOException {
if (p == null || q == null || g == null) {
throw new InvalidObjectException("invalid null value");
}
DerOutputStream out = new DerOutputStream();
out.putInteger(p);
out.putInteger(q);
out.putInteger(g);
DerOutputStream result = new DerOutputStream();
result.write(DerValue.tag_Sequence, out);
return result.toByteArray();
}
}

View File

@ -130,3 +130,20 @@ jdk.http.auth.tunneling.disabledSchemes=Basic
#jdk.http.ntlm.transparentAuth=trustedHosts
#
jdk.http.ntlm.transparentAuth=disabled
#
# Maximum HTTP field section size that a client is prepared to accept
#
# jdk.http.maxHeaderSize=393216
#
# 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
# or 384kB.
#
# Note: This property is currently used by the JDK Reference implementation. It
# is not guaranteed to be examined and used by other implementations.

View File

@ -41,6 +41,7 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.net.http.HttpClient;
import java.net.http.HttpHeaders;
@ -69,6 +70,8 @@ import static jdk.internal.net.http.common.Utils.permissionForProxy;
*/
final class Exchange<T> {
static final int MAX_NON_FINAL_RESPONSES =
Utils.getIntegerNetProperty("jdk.httpclient.maxNonFinalResponses", 8);
final Logger debug = Utils.getDebugLogger(this::dbgString, Utils.DEBUG);
final HttpRequestImpl request;
@ -93,6 +96,8 @@ final class Exchange<T> {
// exchange so that it can be aborted/timed out mid setup.
final ConnectionAborter connectionAborter = new ConnectionAborter();
final AtomicInteger nonFinalResponses = new AtomicInteger();
Exchange(HttpRequestImpl request, MultiExchange<T> multi) {
this.request = request;
this.upgrading = false;
@ -359,7 +364,7 @@ final class Exchange<T> {
public void h2Upgrade() {
upgrading = true;
request.setH2Upgrade(client.client2());
request.setH2Upgrade(this);
}
synchronized IOException getCancelCause() {
@ -482,9 +487,9 @@ final class Exchange<T> {
Log.logResponse(r1::toString);
int rcode = r1.statusCode();
if (rcode == 100) {
nonFinalResponses.incrementAndGet();
Log.logTrace("Received 100-Continue: sending body");
if (debug.on())
debug.log("Received 100-Continue for %s", r1);
if (debug.on()) debug.log("Received 100-Continue for %s", r1);
CompletableFuture<Response> cf =
exchImpl.sendBodyAsync()
.thenCompose(exIm -> exIm.getResponseAsync(parentExecutor));
@ -492,9 +497,9 @@ final class Exchange<T> {
cf = wrapForLog(cf);
return cf;
} else {
Log.logTrace("Expectation failed: Received {0}", rcode);
if (debug.on())
debug.log("Expect-Continue failed (%d) for: %s", rcode, r1);
Log.logTrace("Expectation failed: Received {0}",
rcode);
if (debug.on()) debug.log("Expect-Continue failed (%d) for: %s", rcode, r1);
if (upgrading && rcode == 101) {
IOException failed = new IOException(
"Unable to handle 101 while waiting for 100");
@ -559,12 +564,20 @@ final class Exchange<T> {
+ rsp.statusCode());
}
assert exchImpl != null : "Illegal state - current exchange isn't set";
// ignore this Response and wait again for the subsequent response headers
final CompletableFuture<Response> cf = exchImpl.getResponseAsync(parentExecutor);
// we recompose the CF again into the ignore1xxResponse check/function because
// the 1xx response is allowed to be sent multiple times for a request, before
// a final response arrives
return cf.thenCompose(this::ignore1xxResponse);
int count = nonFinalResponses.incrementAndGet();
if (MAX_NON_FINAL_RESPONSES > 0 && (count < 0 || count > MAX_NON_FINAL_RESPONSES)) {
return MinimalFuture.failedFuture(
new ProtocolException(String.format(
"Too many interim responses received: %s > %s",
count, MAX_NON_FINAL_RESPONSES)));
} else {
// ignore this Response and wait again for the subsequent response headers
final CompletableFuture<Response> cf = exchImpl.getResponseAsync(parentExecutor);
// we recompose the CF again into the ignore1xxResponse check/function because
// the 1xx response is allowed to be sent multiple times for a request, before
// a final response arrives
return cf.thenCompose(this::ignore1xxResponse);
}
} else {
// return the already completed future
return MinimalFuture.completedFuture(rsp);
@ -829,6 +842,14 @@ final class Exchange<T> {
return multi.version();
}
boolean pushEnabled() {
return pushGroup != null;
}
String h2cSettingsStrings() {
return client.client2().getSettingsString(pushEnabled());
}
String dbgString() {
return dbgTag;
}

View File

@ -25,6 +25,7 @@
package jdk.internal.net.http;
import java.io.IOException;
import java.net.ProtocolException;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
@ -53,6 +54,12 @@ class Http1HeaderParser {
private int responseCode;
private HttpHeaders headers;
private Map<String,List<String>> privateMap = new HashMap<>();
private long size;
private static final int K = 1024;
private static final int MAX_HTTP_HEADER_SIZE = Utils.getIntegerNetProperty(
"jdk.http.maxHeaderSize",
Integer.MIN_VALUE, Integer.MAX_VALUE, 384 * K, true);
enum State { INITIAL,
STATUS_LINE,
@ -164,11 +171,16 @@ class Http1HeaderParser {
return (char)(input.get() & 0xFF);
}
private void readResumeStatusLine(ByteBuffer input) {
private void readResumeStatusLine(ByteBuffer input) throws ProtocolException {
final long max = MAX_HTTP_HEADER_SIZE - size - 32 - sb.length();
int count = 0;
char c = 0;
while (input.hasRemaining() && (c = get(input)) != CR) {
if (c == LF) break;
sb.append(c);
if (++count > max) {
checkMaxHeaderSize(sb.length());
}
}
if (c == CR) {
state = State.STATUS_LINE_FOUND_CR;
@ -185,6 +197,7 @@ class Http1HeaderParser {
}
statusLine = sb.toString();
size = size + 32 + statusLine.length();
sb = new StringBuilder();
if (!statusLine.startsWith("HTTP/1.")) {
throw protocolException("Invalid status line: \"%s\"", statusLine);
@ -205,7 +218,23 @@ class Http1HeaderParser {
state = State.STATUS_LINE_END;
}
private void maybeStartHeaders(ByteBuffer input) {
private void checkMaxHeaderSize(int sz) throws ProtocolException {
long s = size + sz + 32;
if (MAX_HTTP_HEADER_SIZE > 0 && s > MAX_HTTP_HEADER_SIZE) {
throw new ProtocolException(String.format("Header size too big: %s > %s",
s, MAX_HTTP_HEADER_SIZE));
}
}
static private long newSize(long size, int name, int value) throws ProtocolException {
long newSize = size + name + value + 32;
if (MAX_HTTP_HEADER_SIZE > 0 && newSize > MAX_HTTP_HEADER_SIZE) {
throw new ProtocolException(String.format("Header size too big: %s > %s",
newSize, MAX_HTTP_HEADER_SIZE));
}
return newSize;
}
private void maybeStartHeaders(ByteBuffer input) throws ProtocolException {
assert state == State.STATUS_LINE_END;
assert sb.length() == 0;
char c = get(input);
@ -215,6 +244,7 @@ class Http1HeaderParser {
state = State.STATUS_LINE_END_LF;
} else {
sb.append(c);
checkMaxHeaderSize(sb.length());
state = State.HEADER;
}
}
@ -232,9 +262,11 @@ class Http1HeaderParser {
}
}
private void readResumeHeader(ByteBuffer input) {
private void readResumeHeader(ByteBuffer input) throws ProtocolException {
assert state == State.HEADER;
assert input.hasRemaining();
final long max = MAX_HTTP_HEADER_SIZE - size - 32 - sb.length();
int count = 0;
while (input.hasRemaining()) {
char c = get(input);
if (c == CR) {
@ -248,6 +280,9 @@ class Http1HeaderParser {
if (c == HT)
c = SP;
sb.append(c);
if (++count > max) {
checkMaxHeaderSize(sb.length());
}
}
}
@ -268,12 +303,12 @@ class Http1HeaderParser {
if (!Utils.isValidValue(value)) {
throw protocolException("Invalid header value \"%s: %s\"", name, value);
}
size = newSize(size, name.length(), value.length());
privateMap.computeIfAbsent(name.toLowerCase(Locale.US),
k -> new ArrayList<>()).add(value);
}
private void resumeOrLF(ByteBuffer input) {
private void resumeOrLF(ByteBuffer input) throws ProtocolException {
assert state == State.HEADER_FOUND_CR || state == State.HEADER_FOUND_LF;
char c = state == State.HEADER_FOUND_LF ? LF : get(input);
if (c == LF) {
@ -283,10 +318,12 @@ class Http1HeaderParser {
state = State.HEADER_FOUND_CR_LF;
} else if (c == SP || c == HT) {
sb.append(SP); // parity with MessageHeaders
checkMaxHeaderSize(sb.length());
state = State.HEADER;
} else {
sb = new StringBuilder();
sb.append(c);
checkMaxHeaderSize(1);
state = State.HEADER;
}
}
@ -312,6 +349,7 @@ class Http1HeaderParser {
} else if (c == SP || c == HT) {
assert sb.length() != 0;
sb.append(SP); // continuation line
checkMaxHeaderSize(sb.length());
state = State.HEADER;
} else {
if (sb.length() > 0) {
@ -322,6 +360,7 @@ class Http1HeaderParser {
addHeaderFromString(headerString);
}
sb.append(c);
checkMaxHeaderSize(sb.length());
state = State.HEADER;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 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
@ -36,7 +36,6 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.locks.ReentrantLock;
import jdk.internal.net.http.common.Log;
import jdk.internal.net.http.common.Logger;
import jdk.internal.net.http.common.MinimalFuture;
import jdk.internal.net.http.common.Utils;
@ -46,6 +45,7 @@ import static jdk.internal.net.http.frame.SettingsFrame.ENABLE_PUSH;
import static jdk.internal.net.http.frame.SettingsFrame.HEADER_TABLE_SIZE;
import static jdk.internal.net.http.frame.SettingsFrame.MAX_CONCURRENT_STREAMS;
import static jdk.internal.net.http.frame.SettingsFrame.MAX_FRAME_SIZE;
import static jdk.internal.net.http.frame.SettingsFrame.MAX_HEADER_LIST_SIZE;
/**
* Http2 specific aspects of HttpClientImpl
@ -98,16 +98,20 @@ class Http2ClientImpl {
CompletableFuture<Http2Connection> getConnectionFor(HttpRequestImpl req,
Exchange<?> exchange) {
String key = Http2Connection.keyFor(req);
boolean pushEnabled = exchange.pushEnabled();
connectionPoolLock.lock();
try {
Http2Connection connection = connections.get(key);
if (connection != null) {
try {
if (!connection.tryReserveForPoolCheckout() || !connection.reserveStream(true)) {
if (!connection.tryReserveForPoolCheckout()
|| !connection.reserveStream(true, pushEnabled)) {
if (debug.on())
debug.log("removing connection from pool since it couldn't be" +
" reserved for use: %s", connection);
" reserved for use%s: %s",
pushEnabled ? " with server push enabled" : "",
connection);
removeFromPool(connection);
} else {
// fast path if connection already exists
@ -137,7 +141,7 @@ class Http2ClientImpl {
try {
if (conn != null) {
try {
conn.reserveStream(true);
conn.reserveStream(true, exchange.pushEnabled());
} catch (IOException e) {
throw new UncheckedIOException(e); // shouldn't happen
}
@ -183,10 +187,21 @@ class Http2ClientImpl {
}
Http2Connection c1 = connections.putIfAbsent(key, c);
if (c1 != null) {
c.setFinalStream();
if (debug.on())
debug.log("existing entry in connection pool for %s", key);
return false;
if (c.serverPushEnabled() && !c1.serverPushEnabled()) {
c1.setFinalStream();
connections.remove(key, c1);
connections.put(key, c);
if (debug.on()) {
debug.log("Replacing %s with %s in connection pool", c1, c);
}
if (c1.shouldClose()) c1.close();
return true;
} else {
c.setFinalStream();
if (debug.on())
debug.log("existing entry in connection pool for %s", key);
return false;
}
}
if (debug.on())
debug.log("put in the connection pool: %s", c);
@ -250,8 +265,8 @@ class Http2ClientImpl {
}
/** Returns the client settings as a base64 (url) encoded string */
String getSettingsString() {
SettingsFrame sf = getClientSettings();
String getSettingsString(boolean defaultServerPush) {
SettingsFrame sf = getClientSettings(defaultServerPush);
byte[] settings = sf.toByteArray(); // without the header
Base64.Encoder encoder = Base64.getUrlEncoder()
.withoutPadding();
@ -261,14 +276,7 @@ class Http2ClientImpl {
private static final int K = 1024;
private static int getParameter(String property, int min, int max, int defaultValue) {
int value = Utils.getIntegerNetProperty(property, defaultValue);
// use default value if misconfigured
if (value < min || value > max) {
Log.logError("Property value for {0}={1} not in [{2}..{3}]: " +
"using default={4}", property, value, min, max, defaultValue);
value = defaultValue;
}
return value;
return Utils.getIntegerNetProperty(property, min, max, defaultValue, true);
}
// used for the connection window, to have a connection window size
@ -288,7 +296,18 @@ class Http2ClientImpl {
streamWindow, Integer.MAX_VALUE, defaultValue);
}
SettingsFrame getClientSettings() {
/**
* This method is used to test whether pushes are globally
* disabled on all connections.
* @return true if pushes are globally disabled on all connections
*/
boolean serverPushDisabled() {
return getParameter(
"jdk.httpclient.enablepush",
0, 1, 1) == 0;
}
SettingsFrame getClientSettings(boolean defaultServerPush) {
SettingsFrame frame = new SettingsFrame();
// default defined for HTTP/2 is 4 K, we use 16 K.
frame.setParameter(HEADER_TABLE_SIZE, getParameter(
@ -297,14 +316,15 @@ class Http2ClientImpl {
// O: does not accept push streams. 1: accepts push streams.
frame.setParameter(ENABLE_PUSH, getParameter(
"jdk.httpclient.enablepush",
0, 1, 1));
0, 1, defaultServerPush ? 1 : 0));
// HTTP/2 recommends to set the number of concurrent streams
// no lower than 100. We use 100. 0 means no stream would be
// accepted. That would render the client to be non functional,
// so we won't let 0 be configured for our Http2ClientImpl.
// no lower than 100. We use 100, unless push promises are
// disabled.
int initialServerStreams = frame.getParameter(ENABLE_PUSH) == 0
? 0 : 100;
frame.setParameter(MAX_CONCURRENT_STREAMS, getParameter(
"jdk.httpclient.maxstreams",
1, Integer.MAX_VALUE, 100));
0, Integer.MAX_VALUE, initialServerStreams));
// Maximum size is 2^31-1. Don't allow window size to be less
// than the minimum frame size as this is likely to be a
// configuration error. HTTP/2 specify a default of 64 * K -1,
@ -317,6 +337,14 @@ class Http2ClientImpl {
frame.setParameter(MAX_FRAME_SIZE, getParameter(
"jdk.httpclient.maxframesize",
16 * K, 16 * K * K -1, 16 * K));
// Maximum field section size we're prepared to accept
// This is the uncompressed name + value size + 32 per field line
int maxHeaderSize = getParameter(
"jdk.http.maxHeaderSize",
Integer.MIN_VALUE, Integer.MAX_VALUE, 384 * K);
// If the property is <= 0 the value is unlimited
if (maxHeaderSize <= 0) maxHeaderSize = -1;
frame.setParameter(MAX_HEADER_LIST_SIZE, maxHeaderSize);
return frame;
}

View File

@ -31,6 +31,7 @@ import java.io.UncheckedIOException;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.net.InetSocketAddress;
import java.net.ProtocolException;
import java.net.http.HttpClient;
import java.net.http.HttpHeaders;
import java.nio.ByteBuffer;
@ -49,6 +50,7 @@ import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Flow;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
@ -88,10 +90,12 @@ import jdk.internal.net.http.hpack.DecodingCallback;
import jdk.internal.net.http.hpack.Encoder;
import static java.nio.charset.StandardCharsets.UTF_8;
import static jdk.internal.net.http.frame.SettingsFrame.DEFAULT_INITIAL_WINDOW_SIZE;
import static jdk.internal.net.http.frame.SettingsFrame.ENABLE_PUSH;
import static jdk.internal.net.http.frame.SettingsFrame.HEADER_TABLE_SIZE;
import static jdk.internal.net.http.frame.SettingsFrame.INITIAL_WINDOW_SIZE;
import static jdk.internal.net.http.frame.SettingsFrame.MAX_CONCURRENT_STREAMS;
import static jdk.internal.net.http.frame.SettingsFrame.MAX_FRAME_SIZE;
import static jdk.internal.net.http.frame.SettingsFrame.MAX_HEADER_LIST_SIZE;
/**
* An Http2Connection. Encapsulates the socket(channel) and any SSLEngine used
@ -327,6 +331,45 @@ class Http2Connection {
}
}
private final class PushPromiseDecoder extends HeaderDecoder implements DecodingCallback {
final int parentStreamId;
final int pushPromiseStreamId;
final Stream<?> parent;
final AtomicReference<Throwable> errorRef = new AtomicReference<>();
PushPromiseDecoder(int parentStreamId, int pushPromiseStreamId, Stream<?> parent) {
this.parentStreamId = parentStreamId;
this.pushPromiseStreamId = pushPromiseStreamId;
this.parent = parent;
}
@Override
protected void addHeader(String name, String value) {
if (errorRef.get() == null) {
super.addHeader(name, value);
}
}
@Override
public void onMaxHeaderListSizeReached(long size, int maxHeaderListSize) throws ProtocolException {
try {
DecodingCallback.super.onMaxHeaderListSizeReached(size, maxHeaderListSize);
} catch (ProtocolException pe) {
if (parent != null) {
if (errorRef.compareAndSet(null, pe)) {
// cancel the parent stream
resetStream(pushPromiseStreamId, ResetFrame.REFUSED_STREAM);
parent.onProtocolError(pe);
}
} else {
// interrupt decoding and closes the connection
throw pe;
}
}
}
}
private static final int HALF_CLOSED_LOCAL = 1;
private static final int HALF_CLOSED_REMOTE = 2;
@ -355,7 +398,7 @@ class Http2Connection {
private final Decoder hpackIn;
final SettingsFrame clientSettings;
private volatile SettingsFrame serverSettings;
private record PushContinuationState(HeaderDecoder pushContDecoder, PushPromiseFrame pushContFrame) {}
private record PushContinuationState(PushPromiseDecoder pushContDecoder, PushPromiseFrame pushContFrame) {}
private volatile PushContinuationState pushContinuationState;
private final String key; // for HttpClientImpl.connections map
private final FramesDecoder framesDecoder;
@ -370,12 +413,24 @@ class Http2Connection {
private final FramesController framesController = new FramesController();
private final Http2TubeSubscriber subscriber;
final ConnectionWindowUpdateSender windowUpdater;
private volatile Throwable cause;
private final AtomicReference<Throwable> cause = new AtomicReference<>();
private volatile Supplier<ByteBuffer> initial;
private volatile Stream<?> initialStream;
static final int DEFAULT_FRAME_SIZE = 16 * 1024;
private ValidatingHeadersConsumer orphanedConsumer;
private final AtomicInteger orphanedHeaders = new AtomicInteger();
static final int DEFAULT_FRAME_SIZE = 16 * 1024;
static final int MAX_LITERAL_WITH_INDEXING =
Utils.getIntegerNetProperty("jdk.httpclient.maxLiteralWithIndexing",512);
// The maximum number of HEADER frames, CONTINUATION frames, or PUSH_PROMISE frames
// referring to an already closed or non-existent stream that a client will accept to
// process. Receiving frames referring to non-existent or closed streams doesn't necessarily
// constitute an HTTP/2 protocol error, but receiving too many may indicate a problem
// with the connection. If this limit is reached, a {@link java.net.ProtocolException
// ProtocolException} will be raised and the connection will be closed.
static final int MAX_ORPHANED_HEADERS = 1024;
// TODO: need list of control frames from other threads
// that need to be sent
@ -383,19 +438,21 @@ class Http2Connection {
private Http2Connection(HttpConnection connection,
Http2ClientImpl client2,
int nextstreamid,
String key) {
String key,
boolean defaultServerPush) {
this.connection = connection;
this.client2 = client2;
this.subscriber = new Http2TubeSubscriber(client2.client());
this.nextstreamid = nextstreamid;
this.key = key;
this.clientSettings = this.client2.getClientSettings();
this.clientSettings = this.client2.getClientSettings(defaultServerPush);
this.framesDecoder = new FramesDecoder(this::processFrame,
clientSettings.getParameter(SettingsFrame.MAX_FRAME_SIZE));
// serverSettings will be updated by server
this.serverSettings = SettingsFrame.defaultRFCSettings();
this.hpackOut = new Encoder(serverSettings.getParameter(HEADER_TABLE_SIZE));
this.hpackIn = new Decoder(clientSettings.getParameter(HEADER_TABLE_SIZE));
this.hpackIn = new Decoder(clientSettings.getParameter(HEADER_TABLE_SIZE),
clientSettings.getParameter(MAX_HEADER_LIST_SIZE), MAX_LITERAL_WITH_INDEXING);
if (debugHpack.on()) {
debugHpack.log("For the record:" + super.toString());
debugHpack.log("Decoder created: %s", hpackIn);
@ -414,14 +471,16 @@ class Http2Connection {
private Http2Connection(HttpConnection connection,
Http2ClientImpl client2,
Exchange<?> exchange,
Supplier<ByteBuffer> initial)
Supplier<ByteBuffer> initial,
boolean defaultServerPush)
throws IOException, InterruptedException
{
this(connection,
client2,
3, // stream 1 is registered during the upgrade
keyFor(connection));
reserveStream(true);
keyFor(connection),
defaultServerPush);
reserveStream(true, clientSettings.getFlag(ENABLE_PUSH));
Log.logTrace("Connection send window size {0} ", windowController.connectionWindowSize());
Stream<?> initialStream = createStream(exchange);
@ -454,7 +513,8 @@ class Http2Connection {
Exchange<?> exchange,
Supplier<ByteBuffer> initial)
{
return MinimalFuture.supply(() -> new Http2Connection(connection, client2, exchange, initial));
return MinimalFuture.supply(() -> new Http2Connection(connection, client2, exchange, initial,
exchange.pushEnabled()));
}
// Requires TLS handshake. So, is really async
@ -478,7 +538,8 @@ class Http2Connection {
.thenCompose(notused-> {
CompletableFuture<Http2Connection> cf = new MinimalFuture<>();
try {
Http2Connection hc = new Http2Connection(request, h2client, connection);
Http2Connection hc = new Http2Connection(request, h2client,
connection, exchange.pushEnabled());
cf.complete(hc);
} catch (IOException e) {
cf.completeExceptionally(e);
@ -493,13 +554,15 @@ class Http2Connection {
*/
private Http2Connection(HttpRequestImpl request,
Http2ClientImpl h2client,
HttpConnection connection)
HttpConnection connection,
boolean defaultServerPush)
throws IOException
{
this(connection,
h2client,
1,
keyFor(request));
keyFor(request),
defaultServerPush);
Log.logTrace("Connection send window size {0} ", windowController.connectionWindowSize());
@ -522,24 +585,30 @@ class Http2Connection {
// if false returned then a new Http2Connection is required
// if true, the stream may be assigned to this connection
// for server push, if false returned, then the stream should be cancelled
boolean reserveStream(boolean clientInitiated) throws IOException {
boolean reserveStream(boolean clientInitiated, boolean pushEnabled) throws IOException {
stateLock.lock();
try {
return reserveStream0(clientInitiated);
return reserveStream0(clientInitiated, pushEnabled);
} finally {
stateLock.unlock();
}
}
private boolean reserveStream0(boolean clientInitiated) throws IOException {
private boolean reserveStream0(boolean clientInitiated, boolean pushEnabled) throws IOException {
if (finalStream()) {
return false;
}
if (clientInitiated && (lastReservedClientStreamid + 2) >= MAX_CLIENT_STREAM_ID) {
// If requesting to reserve a stream for an exchange for which push is enabled,
// we will reserve the stream in this connection only if this connection is also
// push enabled, unless pushes are globally disabled.
boolean pushCompatible = !clientInitiated || !pushEnabled
|| this.serverPushEnabled()
|| client2.serverPushDisabled();
if (clientInitiated && (lastReservedClientStreamid >= MAX_CLIENT_STREAM_ID -2 || !pushCompatible)) {
setFinalStream();
client2.removeFromPool(this);
return false;
} else if (!clientInitiated && (lastReservedServerStreamid + 2) >= MAX_SERVER_STREAM_ID) {
} else if (!clientInitiated && (lastReservedServerStreamid >= MAX_SERVER_STREAM_ID - 2)) {
setFinalStream();
client2.removeFromPool(this);
return false;
@ -564,6 +633,15 @@ class Http2Connection {
return true;
}
boolean shouldClose() {
stateLock.lock();
try {
return finalStream() && streams.isEmpty();
} finally {
stateLock.unlock();
}
}
/**
* Throws an IOException if h2 was not negotiated
*/
@ -691,6 +769,10 @@ class Http2Connection {
return this.key;
}
public boolean serverPushEnabled() {
return clientSettings.getParameter(SettingsFrame.ENABLE_PUSH) == 1;
}
boolean offerConnection() {
return client2.offerConnection(this);
}
@ -795,7 +877,7 @@ class Http2Connection {
}
Throwable getRecordedCause() {
return cause;
return cause.get();
}
void shutdown(Throwable t) {
@ -804,11 +886,11 @@ class Http2Connection {
stateLock.lock();
try {
if (!markShutdownRequested()) return;
Throwable initialCause = this.cause;
if (initialCause == null && t != null) this.cause = t;
cause.compareAndSet(null, t);
} finally {
stateLock.unlock();
}
if (Log.errors()) {
if (t!= null && (!(t instanceof EOFException) || isActive())) {
Log.logError(t);
@ -819,6 +901,7 @@ class Http2Connection {
}
}
client2.removeFromPool(this);
subscriber.stop(cause.get());
for (Stream<?> s : streams.values()) {
try {
s.connectionClosing(t);
@ -872,17 +955,39 @@ class Http2Connection {
return;
}
if (frame instanceof PushPromiseFrame && !serverPushEnabled()) {
String protocolError = "received a PUSH_PROMISE when SETTINGS_ENABLE_PUSH is 0";
protocolError(ResetFrame.PROTOCOL_ERROR, protocolError);
return;
}
Stream<?> stream = getStream(streamid);
var nextstreamid = this.nextstreamid;
if (stream == null && (streamid & 0x01) == 0x01 && streamid >= nextstreamid) {
String protocolError = String.format(
"received a frame for a non existing streamid(%s) >= nextstreamid(%s)",
streamid, nextstreamid);
protocolError(ResetFrame.PROTOCOL_ERROR, protocolError);
return;
}
if (stream == null && pushContinuationState == null) {
// Should never receive a frame with unknown stream id
if (frame instanceof HeaderFrame) {
if (frame instanceof HeaderFrame hf) {
String protocolError = checkMaxOrphanedHeadersExceeded(hf);
if (protocolError != null) {
protocolError(ResetFrame.PROTOCOL_ERROR, protocolError);
return;
}
// always decode the headers as they may affect
// connection-level HPACK decoding state
DecodingCallback decoder = new ValidatingHeadersConsumer()::onDecoded;
if (orphanedConsumer == null || frame.getClass() != ContinuationFrame.class) {
orphanedConsumer = new ValidatingHeadersConsumer();
}
DecodingCallback decoder = orphanedConsumer::onDecoded;
try {
decodeHeaders((HeaderFrame) frame, decoder);
} catch (UncheckedIOException e) {
decodeHeaders(hf, decoder);
} catch (IOException | UncheckedIOException e) {
protocolError(ResetFrame.PROTOCOL_ERROR, e.getMessage());
return;
}
@ -910,29 +1015,41 @@ class Http2Connection {
// While push frame is not null, the only acceptable frame on this
// stream is a Continuation frame
if (pushContinuationState != null) {
PushContinuationState pcs = pushContinuationState;
if (pcs != null) {
if (frame instanceof ContinuationFrame cf) {
if (stream == null) {
String protocolError = checkMaxOrphanedHeadersExceeded(cf);
if (protocolError != null) {
protocolError(ResetFrame.PROTOCOL_ERROR, protocolError);
return;
}
}
try {
if (streamid == pushContinuationState.pushContFrame.streamid())
handlePushContinuation(stream, cf);
else
protocolError(ErrorFrame.PROTOCOL_ERROR, "Received a Continuation Frame with an " +
"unexpected stream id");
} catch (UncheckedIOException e) {
if (streamid == pcs.pushContFrame.streamid())
handlePushContinuation(pcs, stream, cf);
else {
String protocolError = "Received a CONTINUATION with " +
"unexpected stream id: " + streamid + " != "
+ pcs.pushContFrame.streamid();
protocolError(ErrorFrame.PROTOCOL_ERROR, protocolError);
}
} catch (IOException | UncheckedIOException e) {
debug.log("Error handling Push Promise with Continuation: " + e.getMessage(), e);
protocolError(ErrorFrame.PROTOCOL_ERROR, e.getMessage());
return;
}
} else {
pushContinuationState = null;
protocolError(ErrorFrame.PROTOCOL_ERROR, "Expected a Continuation frame but received " + frame);
String protocolError = "Expected a CONTINUATION frame but received " + frame;
protocolError(ErrorFrame.PROTOCOL_ERROR, protocolError);
return;
}
} else {
if (frame instanceof PushPromiseFrame pp) {
try {
handlePushPromise(stream, pp);
} catch (UncheckedIOException e) {
} catch (IOException | UncheckedIOException e) {
protocolError(ErrorFrame.PROTOCOL_ERROR, e.getMessage());
return;
}
@ -940,7 +1057,7 @@ class Http2Connection {
// decode headers
try {
decodeHeaders(hf, stream.rspHeadersConsumer());
} catch (UncheckedIOException e) {
} catch (IOException | UncheckedIOException e) {
debug.log("Error decoding headers: " + e.getMessage(), e);
protocolError(ErrorFrame.PROTOCOL_ERROR, e.getMessage());
return;
@ -953,6 +1070,16 @@ class Http2Connection {
}
}
private String checkMaxOrphanedHeadersExceeded(HeaderFrame hf) {
if (MAX_ORPHANED_HEADERS > 0 ) {
int orphaned = orphanedHeaders.incrementAndGet();
if (orphaned < 0 || orphaned > MAX_ORPHANED_HEADERS) {
return "Too many orphaned header frames received on connection";
}
}
return null;
}
final void dropDataFrame(DataFrame df) {
if (isMarked(closedState, SHUTDOWN_REQUESTED)) return;
if (debug.on()) {
@ -977,38 +1104,65 @@ class Http2Connection {
private <T> void handlePushPromise(Stream<T> parent, PushPromiseFrame pp)
throws IOException
{
int promisedStreamid = pp.getPromisedStream();
if ((promisedStreamid & 0x01) != 0x00) {
throw new ProtocolException("Received PUSH_PROMISE for stream " + promisedStreamid);
}
int streamId = pp.streamid();
if ((streamId & 0x01) != 0x01) {
throw new ProtocolException("Received PUSH_PROMISE on stream " + streamId);
}
// always decode the headers as they may affect connection-level HPACK
// decoding state
assert pushContinuationState == null;
HeaderDecoder decoder = new HeaderDecoder();
decodeHeaders(pp, decoder::onDecoded);
int promisedStreamid = pp.getPromisedStream();
PushPromiseDecoder decoder = new PushPromiseDecoder(streamId, promisedStreamid, parent);
decodeHeaders(pp, decoder);
if (pp.endHeaders()) {
completePushPromise(promisedStreamid, parent, decoder.headers());
if (decoder.errorRef.get() == null) {
completePushPromise(promisedStreamid, parent, decoder.headers());
}
} else {
pushContinuationState = new PushContinuationState(decoder, pp);
}
}
private <T> void handlePushContinuation(Stream<T> parent, ContinuationFrame cf)
private <T> void handlePushContinuation(PushContinuationState pcs, Stream<T> parent, ContinuationFrame cf)
throws IOException {
var pcs = pushContinuationState;
decodeHeaders(cf, pcs.pushContDecoder::onDecoded);
assert pcs.pushContFrame.streamid() == cf.streamid() : String.format(
"Received CONTINUATION on a different stream %s != %s",
cf.streamid(), pcs.pushContFrame.streamid());
decodeHeaders(cf, pcs.pushContDecoder);
// if all continuations are sent, set pushWithContinuation to null
if (cf.endHeaders()) {
completePushPromise(pcs.pushContFrame.getPromisedStream(), parent,
pcs.pushContDecoder.headers());
if (pcs.pushContDecoder.errorRef.get() == null) {
completePushPromise(pcs.pushContFrame.getPromisedStream(), parent,
pcs.pushContDecoder.headers());
}
pushContinuationState = null;
}
}
private <T> void completePushPromise(int promisedStreamid, Stream<T> parent, HttpHeaders headers)
throws IOException {
if (parent == null) {
resetStream(promisedStreamid, ResetFrame.REFUSED_STREAM);
return;
}
HttpRequestImpl parentReq = parent.request;
if (promisedStreamid < nextPushStream) {
// From RFC 9113 section 5.1.1:
// The identifier of a newly established stream MUST be numerically
// greater than all streams that the initiating endpoint has
// opened or reserved.
protocolError(ResetFrame.PROTOCOL_ERROR, String.format(
"Unexpected stream identifier: %s < %s", promisedStreamid, nextPushStream));
return;
}
if (promisedStreamid != nextPushStream) {
// we don't support skipping stream ids;
resetStream(promisedStreamid, ResetFrame.PROTOCOL_ERROR);
return;
} else if (!reserveStream(false)) {
} else if (!reserveStream(false, true)) {
resetStream(promisedStreamid, ResetFrame.REFUSED_STREAM);
return;
} else {
@ -1177,11 +1331,17 @@ class Http2Connection {
private void protocolError(int errorCode, String msg)
throws IOException
{
String protocolError = "protocol error" + (msg == null?"":(": " + msg));
ProtocolException protocolException =
new ProtocolException(protocolError);
if (markHalfClosedLocal()) {
framesDecoder.close(protocolError);
subscriber.stop(protocolException);
if (debug.on()) debug.log("Sending GOAWAY due to " + protocolException);
GoAwayFrame frame = new GoAwayFrame(0, errorCode);
sendFrame(frame);
}
shutdown(new IOException("protocol error" + (msg == null?"":(": " + msg))));
shutdown(protocolException);
}
private void handleSettings(SettingsFrame frame)
@ -1356,7 +1516,7 @@ class Http2Connection {
<T> Stream.PushedStream<T> createPushStream(Stream<T> parent, Exchange<T> pushEx) {
PushGroup<T> pg = parent.exchange.getPushGroup();
return new Stream.PushedStream<>(pg, this, pushEx);
return new Stream.PushedStream<>(parent, pg, this, pushEx);
}
/**
@ -1466,16 +1626,18 @@ class Http2Connection {
private List<ByteBuffer> encodeHeadersImpl(int bufferSize, HttpHeaders... headers) {
ByteBuffer buffer = getHeaderBuffer(bufferSize);
List<ByteBuffer> buffers = new ArrayList<>();
for(HttpHeaders header : headers) {
for (HttpHeaders header : headers) {
for (Map.Entry<String, List<String>> e : header.map().entrySet()) {
String lKey = e.getKey().toLowerCase(Locale.US);
List<String> values = e.getValue();
for (String value : values) {
hpackOut.header(lKey, value);
while (!hpackOut.encode(buffer)) {
buffer.flip();
buffers.add(buffer);
buffer = getHeaderBuffer(bufferSize);
if (!buffer.hasRemaining()) {
buffer.flip();
buffers.add(buffer);
buffer = getHeaderBuffer(bufferSize);
}
}
}
}
@ -1514,7 +1676,7 @@ class Http2Connection {
Throwable cause = null;
synchronized (this) {
if (isMarked(closedState, SHUTDOWN_REQUESTED)) {
cause = this.cause;
cause = this.cause.get();
if (cause == null) {
cause = new IOException("Connection closed");
}
@ -1553,6 +1715,8 @@ class Http2Connection {
Stream<?> stream = registerNewStream(oh);
// provide protection from inserting unordered frames between Headers and Continuation
if (stream != null) {
// we are creating a new stream: reset orphaned header count
orphanedHeaders.set(0);
publisher.enqueue(encodeHeaders(oh, stream));
}
} else {
@ -1621,7 +1785,7 @@ class Http2Connection {
private volatile Flow.Subscription subscription;
private volatile boolean completed;
private volatile boolean dropped;
private volatile Throwable error;
private final AtomicReference<Throwable> errorRef = new AtomicReference<>();
private final ConcurrentLinkedQueue<ByteBuffer> queue
= new ConcurrentLinkedQueue<>();
private final SequentialScheduler scheduler =
@ -1642,10 +1806,9 @@ class Http2Connection {
asyncReceive(buffer);
}
} catch (Throwable t) {
Throwable x = error;
if (x == null) error = t;
errorRef.compareAndSet(null, t);
} finally {
Throwable x = error;
Throwable x = errorRef.get();
if (x != null) {
if (debug.on()) debug.log("Stopping scheduler", x);
scheduler.stop();
@ -1680,6 +1843,7 @@ class Http2Connection {
@Override
public void onNext(List<ByteBuffer> item) {
if (completed) return;
if (debug.on()) debug.log(() -> "onNext: got " + Utils.remaining(item)
+ " bytes in " + item.size() + " buffers");
queue.addAll(item);
@ -1688,19 +1852,21 @@ class Http2Connection {
@Override
public void onError(Throwable throwable) {
if (completed) return;
if (debug.on()) debug.log(() -> "onError: " + throwable);
error = throwable;
errorRef.compareAndSet(null, throwable);
completed = true;
runOrSchedule();
}
@Override
public void onComplete() {
if (completed) return;
String msg = isActive()
? "EOF reached while reading"
: "Idle connection closed by HTTP/2 peer";
if (debug.on()) debug.log(msg);
error = new EOFException(msg);
errorRef.compareAndSet(null, new EOFException(msg));
completed = true;
runOrSchedule();
}
@ -1712,6 +1878,18 @@ class Http2Connection {
// then we might not need the 'dropped' boolean?
dropped = true;
}
void stop(Throwable error) {
if (errorRef.compareAndSet(null, error)) {
completed = true;
scheduler.stop();
queue.clear();
if (subscription != null) {
subscription.cancel();
}
queue.clear();
}
}
}
boolean isActive() {

View File

@ -964,7 +964,9 @@ final class HttpClientImpl extends HttpClient implements Trackable {
// SSLException
throw new SSLException(msg, throwable);
} else if (throwable instanceof ProtocolException) {
throw new ProtocolException(msg);
ProtocolException pe = new ProtocolException(msg);
pe.initCause(throwable);
throw pe;
} else if (throwable instanceof IOException) {
throw new IOException(msg, throwable);
} else {

View File

@ -287,10 +287,10 @@ public class HttpRequestImpl extends HttpRequest implements WebSocketRequest {
InetSocketAddress authority() { return authority; }
void setH2Upgrade(Http2ClientImpl h2client) {
void setH2Upgrade(Exchange<?> exchange) {
systemHeadersBuilder.setHeader("Connection", "Upgrade, HTTP2-Settings");
systemHeadersBuilder.setHeader("Upgrade", Alpns.H2C);
systemHeadersBuilder.setHeader("HTTP2-Settings", h2client.getSettingsString());
systemHeadersBuilder.setHeader("HTTP2-Settings", exchange.h2cSettingsStrings());
}
@Override

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 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
@ -37,6 +37,7 @@ import java.nio.file.Paths;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Function;
@ -137,16 +138,21 @@ public final class ResponseBodyHandlers {
if (!initiatingURI.getHost().equalsIgnoreCase(pushRequestURI.getHost()))
return;
String initiatingScheme = initiatingURI.getScheme();
String pushRequestScheme = pushRequestURI.getScheme();
if (!initiatingScheme.equalsIgnoreCase(pushRequestScheme)) return;
int initiatingPort = initiatingURI.getPort();
if (initiatingPort == -1 ) {
if ("https".equalsIgnoreCase(initiatingURI.getScheme()))
if ("https".equalsIgnoreCase(initiatingScheme))
initiatingPort = 443;
else
initiatingPort = 80;
}
int pushPort = pushRequestURI.getPort();
if (pushPort == -1 ) {
if ("https".equalsIgnoreCase(pushRequestURI.getScheme()))
if ("https".equalsIgnoreCase(pushRequestScheme))
pushPort = 443;
else
pushPort = 80;

View File

@ -30,6 +30,7 @@ import java.io.IOException;
import java.io.UncheckedIOException;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.net.ProtocolException;
import java.net.URI;
import java.net.http.HttpResponse.BodyHandler;
import java.net.http.HttpResponse.ResponseInfo;
@ -43,6 +44,7 @@ import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.Flow;
import java.util.concurrent.Flow.Subscription;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@ -52,10 +54,13 @@ import java.net.http.HttpHeaders;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodySubscriber;
import jdk.internal.net.http.common.*;
import jdk.internal.net.http.frame.*;
import jdk.internal.net.http.hpack.DecodingCallback;
import static jdk.internal.net.http.Exchange.MAX_NON_FINAL_RESPONSES;
/**
* Http/2 Stream handling.
*
@ -140,6 +145,9 @@ class Stream<T> extends ExchangeImpl<T> {
private volatile boolean closed;
private volatile boolean endStreamSent;
private volatile boolean finalResponseCodeReceived;
private volatile boolean trailerReceived;
private AtomicInteger nonFinalResponseCount = new AtomicInteger();
// Indicates the first reason that was invoked when sending a ResetFrame
// to the server. A streamState of 0 indicates that no reset was sent.
// (see markStream(int code)
@ -520,16 +528,38 @@ class Stream<T> extends ExchangeImpl<T> {
// The Hpack decoder decodes into one of these consumers of name,value pairs
DecodingCallback rspHeadersConsumer() {
return rspHeadersConsumer::onDecoded;
return rspHeadersConsumer;
}
String checkInterimResponseCountExceeded() {
// this is also checked by Exchange - but tracking it here too provides
// a more informative message.
int count = nonFinalResponseCount.incrementAndGet();
if (MAX_NON_FINAL_RESPONSES > 0 && (count < 0 || count > MAX_NON_FINAL_RESPONSES)) {
return String.format(
"Stream %s PROTOCOL_ERROR: too many interim responses received: %s > %s",
streamid, count, MAX_NON_FINAL_RESPONSES);
}
return null;
}
protected void handleResponse(HeaderFrame hf) throws IOException {
HttpHeaders responseHeaders = responseHeadersBuilder.build();
if (!finalResponseCodeReceived) {
responseCode = (int) responseHeaders
.firstValueAsLong(":status")
.orElseThrow(() -> new IOException("no statuscode in response"));
try {
responseCode = (int) responseHeaders
.firstValueAsLong(":status")
.orElseThrow(() -> new ProtocolException(String.format(
"Stream %s PROTOCOL_ERROR: no status code in response",
streamid)));
} catch (ProtocolException cause) {
cancelImpl(cause, ResetFrame.PROTOCOL_ERROR);
rspHeadersConsumer.reset();
return;
}
String protocolErrorMsg = null;
// If informational code, response is partially complete
if (responseCode < 100 || responseCode > 199) {
this.finalResponseCodeReceived = true;
@ -537,23 +567,31 @@ class Stream<T> extends ExchangeImpl<T> {
// see RFC 9113 section 8.1:
// A HEADERS frame with the END_STREAM flag set that carries an
// informational status code is malformed
String msg = ("Stream %s PROTOCOL_ERROR: " +
"HEADERS frame with status %s has END_STREAM flag set")
.formatted(streamid, responseCode);
protocolErrorMsg = String.format(
"Stream %s PROTOCOL_ERROR: " +
"HEADERS frame with status %s has END_STREAM flag set",
streamid, responseCode);
} else {
protocolErrorMsg = checkInterimResponseCountExceeded();
}
if (protocolErrorMsg != null) {
if (debug.on()) {
debug.log(msg);
debug.log(protocolErrorMsg);
}
cancelImpl(new IOException(msg), ResetFrame.PROTOCOL_ERROR);
cancelImpl(new ProtocolException(protocolErrorMsg), ResetFrame.PROTOCOL_ERROR);
rspHeadersConsumer.reset();
return;
}
response = new Response(
request, exchange, responseHeaders, connection(),
responseCode, HttpClient.Version.HTTP_2);
/* TODO: review if needs to be removed
the value is not used, but in case `content-length` doesn't parse as
long, there will be NumberFormatException. If left as is, make sure
code up the stack handles NFE correctly. */
/* TODO: review if needs to be removed
the value is not used, but in case `content-length` doesn't parse as
long, there will be NumberFormatException. If left as is, make sure
code up the stack handles NFE correctly. */
responseHeaders.firstValueAsLong("content-length");
if (Log.headers()) {
@ -572,6 +610,15 @@ class Stream<T> extends ExchangeImpl<T> {
Log.dumpHeaders(sb, " ", responseHeaders);
Log.logHeaders(sb.toString());
}
if (trailerReceived) {
String protocolErrorMsg = String.format(
"Stream %s PROTOCOL_ERROR: trailers already received", streamid);
if (debug.on()) {
debug.log(protocolErrorMsg);
}
cancelImpl(new ProtocolException(protocolErrorMsg), ResetFrame.PROTOCOL_ERROR);
}
trailerReceived = true;
rspHeadersConsumer.reset();
}
@ -1182,7 +1229,7 @@ class Stream<T> extends ExchangeImpl<T> {
/**
* A List of responses relating to this stream. Normally there is only
* one response, but intermediate responses like 100 are allowed
* one response, but interim responses like 100 are allowed
* and must be passed up to higher level before continuing. Deals with races
* such as if responses are returned before the CFs get created by
* getResponseAsync()
@ -1401,7 +1448,7 @@ class Stream<T> extends ExchangeImpl<T> {
cancelImpl(e, ResetFrame.CANCEL);
}
private void cancelImpl(final Throwable e, final int resetFrameErrCode) {
void cancelImpl(final Throwable e, final int resetFrameErrCode) {
errorRef.compareAndSet(null, e);
if (debug.on()) {
if (streamid == 0) debug.log("cancelling stream: %s", (Object)e);
@ -1511,6 +1558,7 @@ class Stream<T> extends ExchangeImpl<T> {
}
static class PushedStream<T> extends Stream<T> {
final Stream<T> parent;
final PushGroup<T> pushGroup;
// push streams need the response CF allocated up front as it is
// given directly to user via the multi handler callback function.
@ -1520,16 +1568,17 @@ class Stream<T> extends ExchangeImpl<T> {
volatile HttpResponse.BodyHandler<T> pushHandler;
private volatile boolean finalPushResponseCodeReceived;
PushedStream(PushGroup<T> pushGroup,
PushedStream(Stream<T> parent,
PushGroup<T> pushGroup,
Http2Connection connection,
Exchange<T> pushReq) {
// ## no request body possible, null window controller
super(connection, pushReq, null);
this.parent = parent;
this.pushGroup = pushGroup;
this.pushReq = pushReq.request();
this.pushCF = new MinimalFuture<>();
this.responseCF = new MinimalFuture<>();
}
CompletableFuture<HttpResponse<T>> responseCF() {
@ -1617,7 +1666,16 @@ class Stream<T> extends ExchangeImpl<T> {
.orElse(-1);
if (responseCode == -1) {
completeResponseExceptionally(new IOException("No status code"));
cancelImpl(new ProtocolException("No status code"), ResetFrame.PROTOCOL_ERROR);
rspHeadersConsumer.reset();
return;
} else if (responseCode >= 100 && responseCode < 200) {
String protocolErrorMsg = checkInterimResponseCountExceeded();
if (protocolErrorMsg != null) {
cancelImpl(new ProtocolException(protocolErrorMsg), ResetFrame.PROTOCOL_ERROR);
rspHeadersConsumer.reset();
return;
}
}
this.finalPushResponseCodeReceived = true;
@ -1727,7 +1785,9 @@ class Stream<T> extends ExchangeImpl<T> {
}
}
private class HeadersConsumer extends ValidatingHeadersConsumer {
private class HeadersConsumer extends ValidatingHeadersConsumer implements DecodingCallback {
boolean maxHeaderListSizeReached;
@Override
public void reset() {
@ -1740,6 +1800,9 @@ class Stream<T> extends ExchangeImpl<T> {
public void onDecoded(CharSequence name, CharSequence value)
throws UncheckedIOException
{
if (maxHeaderListSizeReached) {
return;
}
try {
String n = name.toString();
String v = value.toString();
@ -1762,6 +1825,23 @@ class Stream<T> extends ExchangeImpl<T> {
protected String formatMessage(String message, String header) {
return "malformed response: " + super.formatMessage(message, header);
}
@Override
public void onMaxHeaderListSizeReached(long size, int maxHeaderListSize) throws ProtocolException {
if (maxHeaderListSizeReached) return;
try {
DecodingCallback.super.onMaxHeaderListSizeReached(size, maxHeaderListSize);
} catch (ProtocolException cause) {
maxHeaderListSizeReached = true;
// If this is a push stream: cancel the parent.
if (Stream.this instanceof Stream.PushedStream<?> ps) {
ps.parent.onProtocolError(cause);
}
// cancel the stream, continue processing
onProtocolError(cause);
reset();
}
}
}
final class Http2StreamResponseSubscriber<U> extends HttpBodySubscriberWrapper<U> {

View File

@ -39,7 +39,11 @@ public class HeaderDecoder extends ValidatingHeadersConsumer {
String n = name.toString();
String v = value.toString();
super.onDecoded(n, v);
headersBuilder.addHeader(n, v);
addHeader(n, v);
}
protected void addHeader(String name, String value) {
headersBuilder.addHeader(name, value);
}
public HttpHeaders headers() {

View File

@ -616,6 +616,19 @@ public final class Utils {
Integer.parseInt(System.getProperty(name, String.valueOf(defaultValue))));
}
public static int getIntegerNetProperty(String property, int min, int max, int defaultValue, boolean log) {
int value = Utils.getIntegerNetProperty(property, defaultValue);
// use default value if misconfigured
if (value < min || value > max) {
if (log && Log.errors()) {
Log.logError("Property value for {0}={1} not in [{2}..{3}]: " +
"using default={4}", property, value, min, max, defaultValue);
}
value = defaultValue;
}
return value;
}
public static SSLParameters copySSLParameters(SSLParameters p) {
SSLParameters p1 = new SSLParameters();
p1.setAlgorithmConstraints(p.getAlgorithmConstraints());

View File

@ -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
@ -27,6 +27,7 @@ package jdk.internal.net.http.hpack;
import jdk.internal.net.http.hpack.HPACK.Logger;
import java.io.IOException;
import java.net.ProtocolException;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
@ -107,12 +108,16 @@ public final class Decoder {
private final StringReader stringReader;
private final StringBuilder name;
private final StringBuilder value;
private final int maxHeaderListSize;
private final int maxIndexed;
private int intValue;
private boolean firstValueRead;
private boolean firstValueIndex;
private boolean nameHuffmanEncoded;
private boolean valueHuffmanEncoded;
private int capacity;
private long size;
private int indexed;
/**
* Constructs a {@code Decoder} with the specified initial capacity of the
@ -129,6 +134,31 @@ public final class Decoder {
* if capacity is negative
*/
public Decoder(int capacity) {
this(capacity, 0, 0);
}
/**
* Constructs a {@code Decoder} with the specified initial capacity of the
* header table, a max header list size, and a maximum number of literals
* with indexing per header section.
*
* <p> 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 <a href="https://tools.ietf.org/html/rfc7541#section-4.2">4.2. Maximum Table Size</a>).
*
* @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 final class Decoder {
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 final class Decoder {
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 @@ public final class Decoder {
}
}
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 @@ public final class Decoder {
}
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 @@ public final class Decoder {
}
}
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 @@ public final class Decoder {
}
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 @@ public final class Decoder {
//
private void resumeLiteral(ByteBuffer input, DecodingCallback action)
throws IOException {
if (!completeReading(input)) {
if (!completeReading(input, action)) {
return;
}
try {
@ -385,6 +431,8 @@ public final class Decoder {
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 @@ public final class Decoder {
"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 @@ public final class Decoder {
private void resumeLiteralWithIndexing(ByteBuffer input,
DecodingCallback action)
throws IOException {
if (!completeReading(input)) {
if (!completeReading(input, action)) {
return;
}
try {
@ -445,6 +495,8 @@ public final class Decoder {
}
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 @@ public final class Decoder {
"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 @@ public final class Decoder {
private void resumeLiteralNeverIndexed(ByteBuffer input,
DecodingCallback action)
throws IOException {
if (!completeReading(input)) {
if (!completeReading(input, action)) {
return;
}
try {
@ -497,6 +551,8 @@ public final class Decoder {
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 @@ public final class Decoder {
"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 @@ public final class Decoder {
}
}
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 @@ public final class Decoder {
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 @@ public final class Decoder {
return false;
} else {
if (!stringReader.read(input, value)) {
long sz = size + 32 + name.length() + value.length();
checkMaxHeaderListSize(sz, action);
return false;
}
}

View File

@ -24,6 +24,7 @@
*/
package jdk.internal.net.http.hpack;
import java.net.ProtocolException;
import java.nio.ByteBuffer;
/**
@ -292,4 +293,17 @@ public interface DecodingCallback {
* 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));
}
}

View File

@ -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 class Encoder {
}
}
}
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 @@ public class Encoder {
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 class Encoder {
writer.reset(); // FIXME: WHY?
encoding = false;
}
assert done || encoding : "done: " + done + ", encoding: " + encoding;
return done;
}
@ -542,4 +548,8 @@ public class Encoder {
"Previous encoding operation hasn't finished yet");
}
}
protected final Logger logger() {
return logger;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 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
@ -115,6 +115,25 @@
* The HTTP/2 client maximum frame size in bytes. The server is not permitted to send a frame
* larger than this.
* </li>
* <li><p><b>{@systemProperty jdk.httpclient.maxLiteralWithIndexing}</b> (default: 512)<br>
* The maximum number of header field lines (header name and value pairs) that a
* client is willing to add to the HPack Decoder dynamic table during the decoding
* of an entire header field section.
* This is purely an implementation limit.
* If a peer sends a field section with encoding that
* exceeds this limit a {@link java.net.ProtocolException ProtocolException} will be raised.
* A value of zero or a negative value means no limit.
* </li>
* <li><p><b>{@systemProperty jdk.httpclient.maxNonFinalResponses}</b> (default: 8)<br>
* The maximum number of interim (non-final) responses that a client is prepared
* to accept on a request-response stream before the final response is received.
* Interim responses are responses with a status in the range [100, 199] inclusive.
* This is purely an implementation limit.
* If a peer sends a number of interim response that exceeds this limit before
* sending the final response, a {@link java.net.ProtocolException ProtocolException}
* will be raised.
* A value of zero or a negative value means no limit.
* </li>
* <li><p><b>{@systemProperty jdk.httpclient.maxstreams}</b> (default: 100)<br>
* The maximum number of HTTP/2 push streams that the client will permit servers to open
* simultaneously.
@ -155,6 +174,15 @@
* conf/net.properties)<br>A comma separated list of HTTP authentication scheme names, that
* are disallowed for use by the HTTP client implementation, for HTTP CONNECT tunneling.
* </li>
* <li><p><b>{@systemProperty jdk.http.maxHeaderSize}</b> (default: 393216 or 384kB)
* <br>The maximum header field section size that the 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 protocol. A value of zero or a negative
* value means no limit.
* </li>
* </ul>
* @moduleGraph
* @since 11

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2023, 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
@ -170,7 +170,7 @@ public final class EncryptionKey implements SecretKey {
if (destroyed) {
return "Destroyed EncryptionKey";
}
return "key " + key.toString();
return "EncryptionKey: " + key.toString();
}
/**

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2023, 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 @@ package javax.security.auth.kerberos;
import javax.security.auth.Destroyable;
import java.util.Arrays;
import java.util.Base64;
import java.util.Objects;
/**
@ -140,8 +139,7 @@ public final class KerberosCredMessage implements Destroyable {
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;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2023, 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
@ -273,9 +273,9 @@ public class KerberosKey implements SecretKey {
if (destroyed) {
return "Destroyed KerberosKey";
}
return "Kerberos Principal " + principal +
"Key Version " + versionNum +
"key " + key.toString();
return "KerberosKey: principal " + principal +
", version " + versionNum +
", key " + key.toString();
}
/**

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2022, 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 java.util.Arrays;
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;
@ -225,15 +226,8 @@ class KeyImpl implements SecretKey, Destroyable, Serializable {
}
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() {

View File

@ -33,8 +33,10 @@ import sun.security.jgss.spi.*;
import sun.security.jgss.TokenTracker;
import sun.security.krb5.*;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.InvalidObjectException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.OutputStream;
import java.security.*;
import javax.security.auth.Subject;
import javax.security.auth.kerberos.ServicePermission;
@ -899,15 +901,11 @@ class Krb5Context implements GSSContextSpi {
public final byte[] wrap(byte[] inBuf, int offset, int len,
MessageProp msgProp) throws GSSException {
if (DEBUG != null) {
DEBUG.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 {
@ -1050,12 +1048,6 @@ class Krb5Context implements GSSContextSpi {
setSequencingAndReplayProps(token, msgProp);
}
if (DEBUG != null) {
DEBUG.println("Krb5Context.unwrap: data=["
+ getHexBytes(data, 0, data.length)
+ "]");
}
return data;
}
@ -1405,8 +1397,22 @@ class Krb5Context implements GSSContextSpi {
@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());
}
/**
* Restores the state of this object from the stream.
*
* @param stream the {@code ObjectInputStream} from which data is read
* @throws IOException if an I/O error occurs
* @throws ClassNotFoundException if a serialized class cannot be loaded
*/
@java.io.Serial
private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException {
throw new InvalidObjectException
("KerberosSessionKey not directly deserializable");
}
}
@ -1477,5 +1483,4 @@ class Krb5Context implements GSSContextSpi {
public void setAuthzData(AuthorizationData authzData) {
this.authzData = authzData;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2022, 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
@ -33,7 +33,9 @@ import javax.security.auth.kerberos.KerberosTicket;
import javax.security.auth.kerberos.KerberosPrincipal;
import java.io.Serial;
import java.net.InetAddress;
import java.io.InvalidObjectException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.Date;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
@ -400,4 +402,17 @@ public class Krb5InitCredential
throw ge;
}
}
/**
* Restores the state of this object from the stream.
*
* @param stream the {@code ObjectInputStream} from which data is read
* @throws IOException if an I/O error occurs
* @throws ClassNotFoundException if a serialized class cannot be loaded
*/
@java.io.Serial
private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException {
throw new InvalidObjectException("Krb5InitCredential not deserializable");
}
}

View File

@ -187,4 +187,19 @@ public class Krb5Util {
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";
}
}
}

View File

@ -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 class EncryptionKey
public String toString() {
return "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);
}
/**

View File

@ -83,28 +83,36 @@ import static sun.security.krb5.internal.Krb5.DEBUG;
* <a href="http://www.ietf.org/rfc/rfc4120.txt">
* http://www.ietf.org/rfc/rfc4120.txt</a>.
*/
// The instance fields not statically typed as Serializable are ASN.1
// encoded and written by the writeObject method.
@SuppressWarnings("serial")
public class KRBError implements java.io.Serializable {
static final long serialVersionUID = 3643809337475284503L;
private int pvno;
private int msgType;
private KerberosTime cTime; //optional
private Integer cuSec; //optional
private KerberosTime sTime;
private Integer suSec;
private int errorCode;
private Realm crealm; //optional
private PrincipalName cname; //optional
private PrincipalName sname;
private String eText; //optional
private byte[] eData; //optional
private Checksum eCksum; //optional
private transient int pvno;
private transient int msgType;
private transient KerberosTime cTime; //optional
private transient Integer cuSec; //optional
private transient KerberosTime sTime;
private transient Integer suSec;
private transient int errorCode;
private transient Realm crealm; //optional
private transient PrincipalName cname; //optional
private transient PrincipalName sname;
private transient String eText; //optional
private transient byte[] eData; //optional
private transient Checksum eCksum; //optional
private PAData[] pa; // PA-DATA in eData
private transient PAData[] pa; // PA-DATA in eData
/**
* Restores the state of this object from the stream.
*
* @param is the {@code ObjectInputStream} from which data is read
* @throws IOException if an I/O error occurs
* @throws ClassNotFoundException if a serialized class cannot be loaded
*/
@java.io.Serial
private void readObject(ObjectInputStream is)
throws IOException, ClassNotFoundException {
try {

View File

@ -320,9 +320,6 @@ public class Krb5 {
public static final Debug DEBUG = Debug.of("krb5", GetPropertyAction
.privilegedGetProperty("sun.security.krb5.debug"));
public static final sun.security.util.HexDumpEncoder hexDumper =
new sun.security.util.HexDumpEncoder();
static {
errMsgList = new Hashtable<Integer,String> ();
errMsgList.put(KDC_ERR_NONE, "No error");

View File

@ -195,10 +195,6 @@ public class Kinit {
System.out.print("Password for " + princName + ":");
System.out.flush();
psswd = Password.readPassword(System.in);
if (DEBUG != null) {
DEBUG.println(">>> Kinit console input " +
new String(psswd));
}
}
builder = new KrbAsReqBuilder(principal, psswd);
} else {

View File

@ -240,6 +240,19 @@ abstract class P11Key implements Key, Length {
return new KeyRep(type, getAlgorithm(), format, getEncodedInternal());
}
/**
* Restores the state of this object from the stream.
*
* @param stream the {@code ObjectInputStream} from which data is read
* @throws IOException if an I/O error occurs
* @throws ClassNotFoundException if a serialized class cannot be loaded
*/
@java.io.Serial
private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException {
throw new InvalidObjectException("P11Key not directly deserializable");
}
public String toString() {
token.ensureValid();
String s1 = token.provider.getName() + " " + algorithm + " " + type

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 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
@ -187,9 +187,23 @@ final class P11SecureRandom extends SecureRandomSpi {
}
}
/**
* Restores the state of this object from the stream.
*
* @param in the {@code ObjectInputStream} from which data is read
* @throws IOException if an I/O error occurs
* @throws ClassNotFoundException if a serialized class cannot be loaded
*/
@java.io.Serial
private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException {
in.defaultReadObject();
if (token == null) {
throw new InvalidObjectException("token is null");
}
if (mixBuffer != null) {
mixBuffer = mixBuffer.clone();
}
// assign default values to non-null transient fields
iBuffer = new byte[IBUFFER_SIZE];
ibuffered = 0;

View File

@ -1947,6 +1947,19 @@ public final class SunPKCS11 extends AuthProvider {
return new SunPKCS11Rep(this);
}
/**
* Restores the state of this object from the stream.
*
* @param stream the {@code ObjectInputStream} from which data is read
* @throws IOException if an I/O error occurs
* @throws ClassNotFoundException if a serialized class cannot be loaded
*/
@java.io.Serial
private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException {
throw new InvalidObjectException("SunPKCS11 not directly deserializable");
}
/**
* Serialized representation of the SunPKCS11 provider.
*/

View File

@ -127,11 +127,6 @@ public class CK_PBE_PARAMS {
sb.append(pPassword.length);
sb.append(Constants.NEWLINE);
sb.append(Constants.INDENT);
sb.append("pPassword: ");
sb.append(pPassword);
sb.append(Constants.NEWLINE);
sb.append(Constants.INDENT);
sb.append("ulSaltLen: ");
sb.append(pSalt.length);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2022, 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
@ -70,6 +70,16 @@
* while the headers are being read, then the connection is terminated and the request ignored.
* If the value is less than or equal to zero, then the default value is used.
* </li>
* <li><p><b>{@systemProperty sun.net.httpserver.maxReqHeaderSize}</b> (default: 393216 or 384kB)<br>
* The maximum header field section size that the server is prepared to accept.
* This is computed as the sum of the size of the header name, plus
* the size of the header value, plus an overhead of 32 bytes for
* each field section line. The request line counts as a first field section line,
* where the name is empty and the value is the whole line.
* If this limit is exceeded while the headers are being read, then the connection
* is terminated and the request ignored.
* If the value is less than or equal to zero, there is no limit.
* </li>
* <li><p><b>{@systemProperty sun.net.httpserver.maxReqTime}</b> (default: -1)<br>
* The maximum time in milliseconds allowed to receive a request headers and body.
* In practice, the actual time is a function of request size, network speed, and handler

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 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
@ -44,8 +44,10 @@ class Request {
private SocketChannel chan;
private InputStream is;
private OutputStream os;
private final int maxReqHeaderSize;
Request (InputStream rawInputStream, OutputStream rawout) throws IOException {
this.maxReqHeaderSize = ServerConfig.getMaxReqHeaderSize();
is = rawInputStream;
os = rawout;
do {
@ -75,6 +77,7 @@ class Request {
public String readLine () throws IOException {
boolean gotCR = false, gotLF = false;
pos = 0; lineBuf = new StringBuffer();
long lsize = 32;
while (!gotLF) {
int c = is.read();
if (c == -1) {
@ -87,20 +90,27 @@ class Request {
gotCR = false;
consume (CR);
consume (c);
lsize = lsize + 2;
}
} else {
if (c == CR) {
gotCR = true;
} else {
consume (c);
lsize = lsize + 1;
}
}
if (maxReqHeaderSize > 0 && lsize > maxReqHeaderSize) {
throw new IOException("Maximum header (" +
"sun.net.httpserver.maxReqHeaderSize) exceeded, " +
ServerConfig.getMaxReqHeaderSize() + ".");
}
}
lineBuf.append (buf, 0, pos);
return new String (lineBuf);
}
private void consume (int c) {
private void consume (int c) throws IOException {
if (pos == BUF_LEN) {
lineBuf.append (buf);
pos = 0;
@ -138,13 +148,22 @@ class Request {
len = 1;
firstc = c;
}
long hsize = startLine.length() + 32L;
while (firstc != LF && firstc != CR && firstc >= 0) {
int keyend = -1;
int c;
boolean inKey = firstc > ' ';
s[len++] = (char) firstc;
hsize = hsize + 1;
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 = maxReqHeaderSize > 0
? maxReqHeaderSize - hsize - 32
: Long.MAX_VALUE;
while ((c = is.read()) >= 0) {
switch (c) {
/*fallthrough*/
@ -178,6 +197,11 @@ class Request {
s = ns;
}
s[len++] = (char) c;
if (maxReqHeaderSize > 0 && len > maxRemaining) {
throw new IOException("Maximum header (" +
"sun.net.httpserver.maxReqHeaderSize) exceeded, " +
ServerConfig.getMaxReqHeaderSize() + ".");
}
}
firstc = -1;
}
@ -205,6 +229,13 @@ class Request {
"sun.net.httpserver.maxReqHeaders) exceeded, " +
ServerConfig.getMaxReqHeaders() + ".");
}
hsize = hsize + len + 32;
if (maxReqHeaderSize > 0 && hsize > maxReqHeaderSize) {
throw new IOException("Maximum header (" +
"sun.net.httpserver.maxReqHeaderSize) exceeded, " +
ServerConfig.getMaxReqHeaderSize() + ".");
}
if (k == null) { // Headers disallows null keys, use empty string
k = ""; // instead to represent invalid key
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2005, 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
@ -49,6 +49,7 @@ class ServerConfig {
// timing out request/response if max request/response time is configured
private static final long DEFAULT_REQ_RSP_TIMER_TASK_SCHEDULE_MILLIS = 1000;
private static final int DEFAULT_MAX_REQ_HEADERS = 200;
private static final int DEFAULT_MAX_REQ_HEADER_SIZE = 380 * 1024;
private static final long DEFAULT_DRAIN_AMOUNT = 64 * 1024;
private static long idleTimerScheduleMillis;
@ -62,6 +63,9 @@ class ServerConfig {
private static int maxIdleConnections;
// The maximum number of request headers allowable
private static int maxReqHeaders;
// a maximum value for the header list size. This is the
// names size + values size + 32 bytes per field line
private static int maxReqHeadersSize;
// max time a request or response is allowed to take
private static long maxReqTime;
private static long maxRspTime;
@ -107,6 +111,14 @@ class ServerConfig {
maxReqHeaders = DEFAULT_MAX_REQ_HEADERS;
}
// a value <= 0 means unlimited
maxReqHeadersSize = Integer.getInteger(
"sun.net.httpserver.maxReqHeaderSize",
DEFAULT_MAX_REQ_HEADER_SIZE);
if (maxReqHeadersSize <= 0) {
maxReqHeadersSize = 0;
}
maxReqTime = Long.getLong("sun.net.httpserver.maxReqTime",
DEFAULT_MAX_REQ_TIME);
@ -215,6 +227,10 @@ class ServerConfig {
return maxReqHeaders;
}
static int getMaxReqHeaderSize() {
return maxReqHeadersSize;
}
/**
* @return Returns the maximum amount of time the server will wait for the request to be read
* completely. This method can return a value of 0 or negative to imply no maximum limit has

View File

@ -43,7 +43,7 @@ import sun.security.krb5.*;
import sun.security.jgss.krb5.Krb5Util;
import sun.security.krb5.Credentials;
import sun.security.util.Debug;
import sun.security.util.HexDumpEncoder;
import static sun.security.util.ResourcesMgr.getAuthResourceString;
/**
@ -769,15 +769,11 @@ public class Krb5LoginModule implements LoginModule {
if (debug != null) {
debug.println("principal is " + principal);
HexDumpEncoder hd = new HexDumpEncoder();
if (ktab != null) {
debug.println("Will use keytab");
} else if (storeKey) {
for (int i = 0; i < encKeys.length; i++) {
debug.println("EncryptionKey: keyType=" +
encKeys[i].getEType() +
" keyBytes (hex dump)=" +
hd.encodeBuffer(encKeys[i].getBytes()));
debug.println(encKeys[i].toString());
}
}
}
@ -868,7 +864,7 @@ public class Krb5LoginModule implements LoginModule {
}
if (debug != null) {
debug.println
("password is " + new String(password));
("Get password from shared state");
}
return;
}

View File

@ -1,5 +1,6 @@
/*
* Copyright (c) 2023, Red Hat, Inc. All rights reserved.
* Copyright (c) 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
@ -35,7 +36,6 @@ import java.nio.ByteOrder;
* @test
* @bug 8300258
* @key randomness
* @requires (os.simpleArch == "x64") | (os.simpleArch == "aarch64")
* @summary C2: vectorization fails on simple ByteBuffer loop
* @modules java.base/jdk.internal.misc
* @library /test/lib /
@ -147,193 +147,420 @@ public class TestVectorizationMismatchedAccess {
}
@Test
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" })
public static void testByteLong1(byte[] dest, long[] src) {
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"},
applyIfPlatform = {"64-bit", "true"})
// 32-bit: offsets are badly aligned (UNSAFE.ARRAY_BYTE_BASE_OFFSET is 4 byte aligned, but not 8 byte aligned).
// might get fixed with JDK-8325155.
public static void testByteLong1a(byte[] dest, long[] src) {
for (int i = 0; i < src.length; i++) {
UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * i, src[i]);
}
}
@Run(test = "testByteLong1")
public static void testByteLong1_runner() {
runAndVerify(() -> testByteLong1(byteArray, longArray), 0);
@Test
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"},
applyIfPlatform = {"64-bit", "true"})
// 32-bit: address has ConvL2I for cast of long to address, not supported.
public static void testByteLong1b(byte[] dest, long[] src) {
for (int i = 0; i < src.length; i++) {
UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * i, src[i]);
}
}
@Test
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" })
public static void testByteLong2(byte[] dest, long[] src) {
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"})
public static void testByteLong1c(byte[] dest, long[] src) {
long base = 64; // make sure it is big enough and 8 byte aligned (required for 32-bit)
for (int i = 0; i < src.length - 8; i++) {
UNSAFE.putLongUnaligned(dest, base + 8 * i, src[i]);
}
}
@Test
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"},
applyIfPlatform = {"64-bit", "true"})
// 32-bit: address has ConvL2I for cast of long to address, not supported.
public static void testByteLong1d(byte[] dest, long[] src) {
long base = 64; // make sure it is big enough and 8 byte aligned (required for 32-bit)
for (int i = 0; i < src.length - 8; i++) {
UNSAFE.putLongUnaligned(dest, base + 8L * i, src[i]);
}
}
@Run(test = {"testByteLong1a", "testByteLong1b", "testByteLong1c", "testByteLong1d"})
public static void testByteLong1_runner() {
runAndVerify(() -> testByteLong1a(byteArray, longArray), 0);
runAndVerify(() -> testByteLong1b(byteArray, longArray), 0);
testByteLong1c(byteArray, longArray);
testByteLong1d(byteArray, longArray);
}
@Test
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"},
applyIfPlatform = {"64-bit", "true"})
// 32-bit: offsets are badly aligned (UNSAFE.ARRAY_BYTE_BASE_OFFSET is 4 byte aligned, but not 8 byte aligned).
// might get fixed with JDK-8325155.
public static void testByteLong2a(byte[] dest, long[] src) {
for (int i = 1; i < src.length; i++) {
UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * (i - 1), src[i]);
}
}
@Run(test = "testByteLong2")
@Test
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"},
applyIfPlatform = {"64-bit", "true"})
// 32-bit: address has ConvL2I for cast of long to address, not supported.
public static void testByteLong2b(byte[] dest, long[] src) {
for (int i = 1; i < src.length; i++) {
UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * (i - 1), src[i]);
}
}
@Run(test = {"testByteLong2a", "testByteLong2b"})
public static void testByteLong2_runner() {
runAndVerify(() -> testByteLong2(byteArray, longArray), -8);
runAndVerify(() -> testByteLong2a(byteArray, longArray), -8);
runAndVerify(() -> testByteLong2b(byteArray, longArray), -8);
}
@Test
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" })
public static void testByteLong3(byte[] dest, long[] src) {
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"},
applyIfPlatform = {"64-bit", "true"})
// 32-bit: offsets are badly aligned (UNSAFE.ARRAY_BYTE_BASE_OFFSET is 4 byte aligned, but not 8 byte aligned).
// might get fixed with JDK-8325155.
public static void testByteLong3a(byte[] dest, long[] src) {
for (int i = 0; i < src.length - 1; i++) {
UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * (i + 1), src[i]);
}
}
@Run(test = "testByteLong3")
@Test
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"},
applyIfPlatform = {"64-bit", "true"})
// 32-bit: address has ConvL2I for cast of long to address, not supported.
public static void testByteLong3b(byte[] dest, long[] src) {
for (int i = 0; i < src.length - 1; i++) {
UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * (i + 1), src[i]);
}
}
@Run(test = {"testByteLong3a", "testByteLong3b"})
public static void testByteLong3_runner() {
runAndVerify(() -> testByteLong3(byteArray, longArray), 8);
runAndVerify(() -> testByteLong3a(byteArray, longArray), 8);
runAndVerify(() -> testByteLong3b(byteArray, longArray), 8);
}
@Test
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"},
applyIfPlatform = {"64-bit", "true"},
applyIf = {"AlignVector", "false"})
// 32-bit: offsets are badly aligned (UNSAFE.ARRAY_BYTE_BASE_OFFSET is 4 byte aligned, but not 8 byte aligned).
// might get fixed with JDK-8325155.
// AlignVector cannot guarantee that invar is aligned.
public static void testByteLong4(byte[] dest, long[] src, int start, int stop) {
public static void testByteLong4a(byte[] dest, long[] src, int start, int stop) {
for (int i = start; i < stop; i++) {
UNSAFE.putLongUnaligned(dest, 8 * i + baseOffset, src[i]);
}
}
@Run(test = "testByteLong4")
@Test
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"},
applyIfPlatform = {"64-bit", "true"},
applyIf = {"AlignVector", "false"})
// 32-bit: address has ConvL2I for cast of long to address, not supported.
// AlignVector cannot guarantee that invar is aligned.
public static void testByteLong4b(byte[] dest, long[] src, int start, int stop) {
for (int i = start; i < stop; i++) {
UNSAFE.putLongUnaligned(dest, 8L * i + baseOffset, src[i]);
}
}
@Run(test = {"testByteLong4a", "testByteLong4b"})
public static void testByteLong4_runner() {
baseOffset = UNSAFE.ARRAY_BYTE_BASE_OFFSET;
runAndVerify(() -> testByteLong4(byteArray, longArray, 0, size), 0);
runAndVerify(() -> testByteLong4a(byteArray, longArray, 0, size), 0);
runAndVerify(() -> testByteLong4b(byteArray, longArray, 0, size), 0);
}
@Test
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" })
public static void testByteLong5(byte[] dest, long[] src, int start, int stop) {
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"},
applyIfPlatform = {"64-bit", "true"})
// 32-bit: offsets are badly aligned (UNSAFE.ARRAY_BYTE_BASE_OFFSET is 4 byte aligned, but not 8 byte aligned).
// might get fixed with JDK-8325155.
public static void testByteLong5a(byte[] dest, long[] src, int start, int stop) {
for (int i = start; i < stop; i++) {
UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * (i + baseOffset), src[i]);
}
}
@Run(test = "testByteLong5")
@Test
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"},
applyIfPlatform = {"64-bit", "true"})
// 32-bit: address has ConvL2I for cast of long to address, not supported.
public static void testByteLong5b(byte[] dest, long[] src, int start, int stop) {
for (int i = start; i < stop; i++) {
UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * (i + baseOffset), src[i]);
}
}
@Run(test = {"testByteLong5a", "testByteLong5b"})
public static void testByteLong5_runner() {
baseOffset = 1;
runAndVerify(() -> testByteLong5(byteArray, longArray, 0, size-1), 8);
runAndVerify(() -> testByteLong5a(byteArray, longArray, 0, size-1), 8);
runAndVerify(() -> testByteLong5b(byteArray, longArray, 0, size-1), 8);
}
@Test
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" })
public static void testByteByte1(byte[] dest, byte[] src) {
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"},
applyIfPlatform = {"64-bit", "true"})
// 32-bit: offsets are badly aligned (UNSAFE.ARRAY_BYTE_BASE_OFFSET is 4 byte aligned, but not 8 byte aligned).
// might get fixed with JDK-8325155.
public static void testByteByte1a(byte[] dest, byte[] src) {
for (int i = 0; i < src.length / 8; i++) {
UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * i, UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * i));
}
}
@Run(test = "testByteByte1")
@Test
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"},
applyIfPlatform = {"64-bit", "true"})
// 32-bit: address has ConvL2I for cast of long to address, not supported.
public static void testByteByte1b(byte[] dest, byte[] src) {
for (int i = 0; i < src.length / 8; i++) {
UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * i, UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * i));
}
}
@Run(test = {"testByteByte1a", "testByteByte1b"})
public static void testByteByte1_runner() {
runAndVerify2(() -> testByteByte1(byteArray, byteArray), 0);
runAndVerify2(() -> testByteByte1a(byteArray, byteArray), 0);
runAndVerify2(() -> testByteByte1b(byteArray, byteArray), 0);
}
@Test
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" })
public static void testByteByte2(byte[] dest, byte[] src) {
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"},
applyIfPlatform = {"64-bit", "true"})
// 32-bit: offsets are badly aligned (UNSAFE.ARRAY_BYTE_BASE_OFFSET is 4 byte aligned, but not 8 byte aligned).
// might get fixed with JDK-8325155.
public static void testByteByte2a(byte[] dest, byte[] src) {
for (int i = 1; i < src.length / 8; i++) {
UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * (i - 1), UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * i));
}
}
@Run(test = "testByteByte2")
@Test
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
applyIfCPUFeatureOr = {"sse2", "true", "asimd", "true"},
applyIfPlatform = {"64-bit", "true"})
// 32-bit: address has ConvL2I for cast of long to address, not supported.
public static void testByteByte2b(byte[] dest, byte[] src) {
for (int i = 1; i < src.length / 8; i++) {
UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * (i - 1), UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * i));
}
}
@Run(test = {"testByteByte2a", "testByteByte2b"})
public static void testByteByte2_runner() {
runAndVerify2(() -> testByteByte2(byteArray, byteArray), -8);
runAndVerify2(() -> testByteByte2a(byteArray, byteArray), -8);
runAndVerify2(() -> testByteByte2b(byteArray, byteArray), -8);
}
@Test
@IR(failOn = { IRNode.LOAD_VECTOR_L, IRNode.STORE_VECTOR })
public static void testByteByte3(byte[] dest, byte[] src) {
public static void testByteByte3a(byte[] dest, byte[] src) {
for (int i = 0; i < src.length / 8 - 1; i++) {
UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * (i + 1), UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * i));
}
}
@Run(test = "testByteByte3")
@Test
@IR(failOn = { IRNode.LOAD_VECTOR_L, IRNode.STORE_VECTOR })
public static void testByteByte3b(byte[] dest, byte[] src) {
for (int i = 0; i < src.length / 8 - 1; i++) {
UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * (i + 1), UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * i));
}
}
@Run(test = {"testByteByte3a", "testByteByte3b"})
public static void testByteByte3_runner() {
runAndVerify2(() -> testByteByte3(byteArray, byteArray), 8);
runAndVerify2(() -> testByteByte3a(byteArray, byteArray), 8);
runAndVerify2(() -> testByteByte3b(byteArray, byteArray), 8);
}
@Test
@IR(failOn = { IRNode.LOAD_VECTOR_L, IRNode.STORE_VECTOR })
public static void testByteByte4(byte[] dest, byte[] src, int start, int stop) {
public static void testByteByte4a(byte[] dest, byte[] src, int start, int stop) {
for (int i = start; i < stop; i++) {
UNSAFE.putLongUnaligned(dest, 8 * i + baseOffset, UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * i));
}
}
@Run(test = "testByteByte4")
@Test
@IR(failOn = { IRNode.LOAD_VECTOR_L, IRNode.STORE_VECTOR })
public static void testByteByte4b(byte[] dest, byte[] src, int start, int stop) {
for (int i = start; i < stop; i++) {
UNSAFE.putLongUnaligned(dest, 8L * i + baseOffset, UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * i));
}
}
@Run(test = {"testByteByte4a", "testByteByte4b"})
public static void testByteByte4_runner() {
baseOffset = UNSAFE.ARRAY_BYTE_BASE_OFFSET;
runAndVerify2(() -> testByteByte4(byteArray, byteArray, 0, size), 0);
runAndVerify2(() -> testByteByte4a(byteArray, byteArray, 0, size), 0);
runAndVerify2(() -> testByteByte4b(byteArray, byteArray, 0, size), 0);
}
@Test
@IR(failOn = { IRNode.LOAD_VECTOR_L, IRNode.STORE_VECTOR })
public static void testByteByte5(byte[] dest, byte[] src, int start, int stop) {
public static void testByteByte5a(byte[] dest, byte[] src, int start, int stop) {
for (int i = start; i < stop; i++) {
UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * (i + baseOffset), UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8 * i));
}
}
@Run(test = "testByteByte5")
@Test
@IR(failOn = { IRNode.LOAD_VECTOR_L, IRNode.STORE_VECTOR })
public static void testByteByte5b(byte[] dest, byte[] src, int start, int stop) {
for (int i = start; i < stop; i++) {
UNSAFE.putLongUnaligned(dest, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * (i + baseOffset), UNSAFE.getLongUnaligned(src, UNSAFE.ARRAY_BYTE_BASE_OFFSET + 8L * i));
}
}
@Run(test = {"testByteByte5a", "testByteByte5b"})
public static void testByteByte5_runner() {
baseOffset = 1;
runAndVerify2(() -> testByteByte5(byteArray, byteArray, 0, size-1), 8);
runAndVerify2(() -> testByteByte5a(byteArray, byteArray, 0, size-1), 8);
runAndVerify2(() -> testByteByte5b(byteArray, byteArray, 0, size-1), 8);
}
@Test
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" })
public static void testOffHeapLong1(long dest, long[] src) {
@IR(counts = { IRNode.LOAD_VECTOR_L, "=0", IRNode.STORE_VECTOR, "=0" }) // temporary
// @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" })
// FAILS: adr is CastX2P(dest + 8 * (i + int_con))
// See: JDK-8331576
public static void testOffHeapLong1a(long dest, long[] src) {
for (int i = 0; i < src.length; i++) {
UNSAFE.putLongUnaligned(null, dest + 8 * i, src[i]);
}
}
@Run(test = "testOffHeapLong1")
@Test
@IR(counts = { IRNode.LOAD_VECTOR_L, "=0", IRNode.STORE_VECTOR, "=0" }) // temporary
// @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" })
// FAILS: adr is CastX2P(dest + 8L * (i + int_con))
// See: JDK-8331576
public static void testOffHeapLong1b(long dest, long[] src) {
for (int i = 0; i < src.length; i++) {
UNSAFE.putLongUnaligned(null, dest + 8L * i, src[i]);
}
}
@Run(test = {"testOffHeapLong1a", "testOffHeapLong1b"})
public static void testOffHeapLong1_runner() {
runAndVerify3(() -> testOffHeapLong1(baseOffHeap, longArray), 0);
runAndVerify3(() -> testOffHeapLong1a(baseOffHeap, longArray), 0);
runAndVerify3(() -> testOffHeapLong1b(baseOffHeap, longArray), 0);
}
@Test
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" })
public static void testOffHeapLong2(long dest, long[] src) {
@IR(counts = { IRNode.LOAD_VECTOR_L, "=0", IRNode.STORE_VECTOR, "=0" }) // temporary
// @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" })
// FAILS: adr is CastX2P
// See: JDK-8331576
public static void testOffHeapLong2a(long dest, long[] src) {
for (int i = 1; i < src.length; i++) {
UNSAFE.putLongUnaligned(null, dest + 8 * (i - 1), src[i]);
}
}
@Run(test = "testOffHeapLong2")
@Test
@IR(counts = { IRNode.LOAD_VECTOR_L, "=0", IRNode.STORE_VECTOR, "=0" }) // temporary
// @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" })
// FAILS: adr is CastX2P
// See: JDK-8331576
public static void testOffHeapLong2b(long dest, long[] src) {
for (int i = 1; i < src.length; i++) {
UNSAFE.putLongUnaligned(null, dest + 8L * (i - 1), src[i]);
}
}
@Run(test = {"testOffHeapLong2a", "testOffHeapLong2b"})
public static void testOffHeapLong2_runner() {
runAndVerify3(() -> testOffHeapLong2(baseOffHeap, longArray), -8);
runAndVerify3(() -> testOffHeapLong2a(baseOffHeap, longArray), -8);
runAndVerify3(() -> testOffHeapLong2b(baseOffHeap, longArray), -8);
}
@Test
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" })
public static void testOffHeapLong3(long dest, long[] src) {
@IR(counts = { IRNode.LOAD_VECTOR_L, "=0", IRNode.STORE_VECTOR, "=0" }) // temporary
// @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" })
// FAILS: adr is CastX2P
// See: JDK-8331576
public static void testOffHeapLong3a(long dest, long[] src) {
for (int i = 0; i < src.length - 1; i++) {
UNSAFE.putLongUnaligned(null, dest + 8 * (i + 1), src[i]);
}
}
@Run(test = "testOffHeapLong3")
@Test
@IR(counts = { IRNode.LOAD_VECTOR_L, "=0", IRNode.STORE_VECTOR, "=0" }) // temporary
// @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" })
// FAILS: adr is CastX2P
// See: JDK-8331576
public static void testOffHeapLong3b(long dest, long[] src) {
for (int i = 0; i < src.length - 1; i++) {
UNSAFE.putLongUnaligned(null, dest + 8L * (i + 1), src[i]);
}
}
@Run(test = {"testOffHeapLong3a", "testOffHeapLong3b"})
public static void testOffHeapLong3_runner() {
runAndVerify3(() -> testOffHeapLong3(baseOffHeap, longArray), 8);
runAndVerify3(() -> testOffHeapLong3a(baseOffHeap, longArray), 8);
runAndVerify3(() -> testOffHeapLong3b(baseOffHeap, longArray), 8);
}
@Test
@IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
applyIf = {"AlignVector", "false"})
@IR(counts = { IRNode.LOAD_VECTOR_L, "=0", IRNode.STORE_VECTOR, "=0" }) // temporary
// @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
// applyIf = {"AlignVector", "false"})
// FAILS: adr is CastX2P
// See: JDK-8331576
// AlignVector cannot guarantee that invar is aligned.
public static void testOffHeapLong4(long dest, long[] src, int start, int stop) {
public static void testOffHeapLong4a(long dest, long[] src, int start, int stop) {
for (int i = start; i < stop; i++) {
UNSAFE.putLongUnaligned(null, dest + 8 * i + baseOffset, src[i]);
}
}
@Run(test = "testOffHeapLong4")
@Test
@IR(counts = { IRNode.LOAD_VECTOR_L, "=0", IRNode.STORE_VECTOR, "=0" }) // temporary
// @IR(counts = { IRNode.LOAD_VECTOR_L, ">=1", IRNode.STORE_VECTOR, ">=1" },
// applyIf = {"AlignVector", "false"})
// FAILS: adr is CastX2P
// See: JDK-8331576
// AlignVector cannot guarantee that invar is aligned.
public static void testOffHeapLong4b(long dest, long[] src, int start, int stop) {
for (int i = start; i < stop; i++) {
UNSAFE.putLongUnaligned(null, dest + 8L * i + baseOffset, src[i]);
}
}
@Run(test = {"testOffHeapLong4a", "testOffHeapLong4b"})
public static void testOffHeapLong4_runner() {
baseOffset = 8;
runAndVerify3(() -> testOffHeapLong4(baseOffHeap, longArray, 0, size-1), 8);
runAndVerify3(() -> testOffHeapLong4a(baseOffHeap, longArray, 0, size-1), 8);
runAndVerify3(() -> testOffHeapLong4b(baseOffHeap, longArray, 0, size-1), 8);
}
}

View File

@ -1363,7 +1363,7 @@ public class TestAlignVector {
static Object[] test17a(long[] a) {
// Unsafe: vectorizes with profiling (not xcomp)
for (int i = 0; i < RANGE; i++) {
int adr = UNSAFE.ARRAY_LONG_BASE_OFFSET + 8 * i;
long adr = UNSAFE.ARRAY_LONG_BASE_OFFSET + 8L * i;
long v = UNSAFE.getLongUnaligned(a, adr);
UNSAFE.putLongUnaligned(a, adr, v + 1);
}
@ -1375,7 +1375,7 @@ public class TestAlignVector {
static Object[] test17b(long[] a) {
// Not alignable
for (int i = 0; i < RANGE-1; i++) {
int adr = UNSAFE.ARRAY_LONG_BASE_OFFSET + 8 * i + 1;
long adr = UNSAFE.ARRAY_LONG_BASE_OFFSET + 8L * i + 1;
long v = UNSAFE.getLongUnaligned(a, adr);
UNSAFE.putLongUnaligned(a, adr, v + 1);
}
@ -1392,7 +1392,7 @@ public class TestAlignVector {
static Object[] test17c(long[] a) {
// Unsafe: aligned vectorizes
for (int i = 0; i < RANGE-1; i+=4) {
int adr = UNSAFE.ARRAY_LONG_BASE_OFFSET + 8 * i;
long adr = UNSAFE.ARRAY_LONG_BASE_OFFSET + 8L * i;
long v0 = UNSAFE.getLongUnaligned(a, adr + 0);
long v1 = UNSAFE.getLongUnaligned(a, adr + 8);
UNSAFE.putLongUnaligned(a, adr + 0, v0 + 1);
@ -1422,7 +1422,7 @@ public class TestAlignVector {
static Object[] test17d(long[] a) {
// Not alignable
for (int i = 0; i < RANGE-1; i+=4) {
int adr = UNSAFE.ARRAY_LONG_BASE_OFFSET + 8 * i + 1;
long adr = UNSAFE.ARRAY_LONG_BASE_OFFSET + 8L * i + 1;
long v0 = UNSAFE.getLongUnaligned(a, adr + 0);
long v1 = UNSAFE.getLongUnaligned(a, adr + 8);
UNSAFE.putLongUnaligned(a, adr + 0, v0 + 1);

View File

@ -1090,11 +1090,11 @@ public class TestAlignVectorFuzzer {
int init = init_con_or_var();
int limit = limit_con_or_var();
int stride = stride_con();
int scale = scale_con();
int offset = offset1_con_or_var();
long scale = scale_con();
long offset = offset1_con_or_var();
for (int i = init; i < limit; i += stride) {
int adr = UNSAFE.ARRAY_BYTE_BASE_OFFSET + offset + i * scale;
long adr = UNSAFE.ARRAY_BYTE_BASE_OFFSET + offset + i * scale;
int v = UNSAFE.getIntUnaligned(a, adr);
UNSAFE.putIntUnaligned(a, adr, v + 1);
}
@ -1105,19 +1105,19 @@ public class TestAlignVectorFuzzer {
int init = init_con_or_var();
int limit = limit_con_or_var();
int stride = stride_con();
int scale = scale_con();
int offset1 = offset1_con_or_var();
int offset2 = offset2_con_or_var();
int offset3 = offset3_con_or_var();
long scale = scale_con();
long offset1 = offset1_con_or_var();
long offset2 = offset2_con_or_var();
long offset3 = offset3_con_or_var();
int h1 = hand_unrolling1_con();
int h2 = hand_unrolling2_con();
int h3 = hand_unrolling3_con();
for (int i = init; i < limit; i += stride) {
int adr1 = UNSAFE.ARRAY_BYTE_BASE_OFFSET + offset1 + i * scale;
int adr2 = UNSAFE.ARRAY_BYTE_BASE_OFFSET + offset2 + i * scale;
int adr3 = UNSAFE.ARRAY_BYTE_BASE_OFFSET + offset3 + i * scale;
long adr1 = UNSAFE.ARRAY_BYTE_BASE_OFFSET + offset1 + i * scale;
long adr2 = UNSAFE.ARRAY_BYTE_BASE_OFFSET + offset2 + i * scale;
long adr3 = UNSAFE.ARRAY_BYTE_BASE_OFFSET + offset3 + i * scale;
if (h1 >= 1) { UNSAFE.putIntUnaligned(a, adr1 + 0*4, UNSAFE.getIntUnaligned(a, adr1 + 0*4) + 1); }
if (h1 >= 2) { UNSAFE.putIntUnaligned(a, adr1 + 1*4, UNSAFE.getIntUnaligned(a, adr1 + 1*4) + 1); }

View File

@ -172,10 +172,10 @@ public class TestIndependentPacksWithCyclicDependency {
static void test2(int[] dataIa, int[] dataIb, float[] dataFa, float[] dataFb) {
for (int i = 0; i < RANGE; i+=2) {
// int and float arrays are two slices. But we pretend both are of type int.
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0, dataIa[i+0] + 1);
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4, dataIa[i+1] + 1);
dataIb[i+0] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0);
dataIb[i+1] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4);
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0, dataIa[i+0] + 1);
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4, dataIa[i+1] + 1);
dataIb[i+0] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0);
dataIb[i+1] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4);
}
}
@ -248,10 +248,10 @@ public class TestIndependentPacksWithCyclicDependency {
for (int i = 0; i < RANGE; i+=2) {
// same as test2, except that reordering leads to different semantics
// explanation analogue to test4
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0, dataIa[i+0] + 1); // A
dataIb[i+0] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0); // X
dataIb[i+1] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4); // Y
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4, dataIa[i+1] + 1); // B
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0, dataIa[i+0] + 1); // A
dataIb[i+0] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0); // X
dataIb[i+1] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4); // Y
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4, dataIa[i+1] + 1); // B
}
}
@ -275,18 +275,18 @@ public class TestIndependentPacksWithCyclicDependency {
long[] dataLa, long[] dataLb) {
for (int i = 0; i < RANGE; i+=2) {
// Chain of parallelizable op and conversion
int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0) + 3;
int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4) + 3;
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0, v00);
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4, v01);
int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0) * 45;
int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4) * 45;
unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0, v10);
unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4, v11);
float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0) + 0.55f;
float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4) + 0.55f;
unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0, v20);
unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4, v21);
int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0) + 3;
int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4) + 3;
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0, v00);
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4, v01);
int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0) * 45;
int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4) * 45;
unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0, v10);
unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4, v11);
float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0) + 0.55f;
float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4) + 0.55f;
unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0, v20);
unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4, v21);
}
}
@ -307,18 +307,18 @@ public class TestIndependentPacksWithCyclicDependency {
long[] dataLa, long[] dataLb) {
for (int i = 0; i < RANGE; i+=2) {
// Cycle involving 3 memory slices
int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0) + 3;
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0, v00);
int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0) * 45;
int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4) * 45;
unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0, v10);
unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4, v11);
float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0) + 0.55f;
float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4) + 0.55f;
unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0, v20);
unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4, v21);
int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4) + 3; // moved down
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4, v01);
int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0) + 3;
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0, v00);
int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0) * 45;
int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4) * 45;
unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0, v10);
unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4, v11);
float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0) + 0.55f;
float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4) + 0.55f;
unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0, v20);
unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4, v21);
int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4) + 3; // moved down
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4, v01);
}
}
@ -340,19 +340,19 @@ public class TestIndependentPacksWithCyclicDependency {
long[] dataLa, long[] dataLb) {
for (int i = 0; i < RANGE; i+=2) {
// 2-cycle, with more ops after
int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0) + 3;
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0, v00);
int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0) * 45;
int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4) * 45;
unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0, v10);
unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4, v11);
int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4) + 3;
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4, v01);
int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0) + 3;
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0, v00);
int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0) * 45;
int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4) * 45;
unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0, v10);
unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4, v11);
int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4) + 3;
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4, v01);
// more stuff after
float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0) + 0.55f;
float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4) + 0.55f;
unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0, v20);
unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4, v21);
float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0) + 0.55f;
float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4) + 0.55f;
unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0, v20);
unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4, v21);
}
}
@ -373,19 +373,19 @@ public class TestIndependentPacksWithCyclicDependency {
long[] dataLa, long[] dataLb) {
for (int i = 0; i < RANGE; i+=2) {
// 2-cycle, with more stuff before
float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0) + 0.55f;
float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4) + 0.55f;
unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0, v20);
unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4, v21);
float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0) + 0.55f;
float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4) + 0.55f;
unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0, v20);
unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4, v21);
// 2-cycle
int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0) + 3;
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0, v00);
int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0) * 45;
int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4) * 45;
unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0, v10);
unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4, v11);
int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4) + 3;
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4, v01);
int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0) + 3;
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0, v00);
int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0) * 45;
int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4) * 45;
unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0, v10);
unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4, v11);
int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4) + 3;
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4, v01);
}
}
@ -423,18 +423,18 @@ public class TestIndependentPacksWithCyclicDependency {
//
// The cycle thus does not only go via packs, but also scalar ops.
//
int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0) + 3; // A
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0, v00);
int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0) * 45; // R: constant mismatch
int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4) + 43; // S
unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0, v10);
unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4, v11);
float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0) + 0.55f; // U
float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4) + 0.55f; // V
unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0, v20);
unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4, v21);
int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4) + 3; // B: moved down
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4, v01);
int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0) + 3; // A
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0, v00);
int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0) * 45; // R: constant mismatch
int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4) + 43; // S
unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0, v10);
unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4, v11);
float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0) + 0.55f; // U
float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4) + 0.55f; // V
unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0, v20);
unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4, v21);
int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4) + 3; // B: moved down
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4, v01);
}
}
@ -463,8 +463,8 @@ public class TestIndependentPacksWithCyclicDependency {
static void verify(String name, float[] data, float[] gold) {
for (int i = 0; i < RANGE; i++) {
int datav = unsafe.getInt(data, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i);
int goldv = unsafe.getInt(gold, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i);
int datav = unsafe.getInt(data, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i);
int goldv = unsafe.getInt(gold, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i);
if (datav != goldv) {
throw new RuntimeException(" Invalid " + name + " result: dataF[" + i + "]: " + datav + " != " + goldv);
}

View File

@ -58,18 +58,18 @@ public class TestIndependentPacksWithCyclicDependency2 {
long[] dataLa, long[] dataLb) {
for (int i = 0; i < RANGE; i+=2) {
// For explanation, see test 10 in TestIndependentPacksWithCyclicDependency.java
int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0) + 3;
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0, v00);
int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0) * 45;
int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4) + 43;
unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0, v10);
unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4, v11);
float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 0) + 0.55f;
float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4 * i + 4) + 0.55f;
unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0, v20);
unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4, v21);
int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4) + 3; // moved down
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4, v01);
int v00 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0) + 3;
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0, v00);
int v10 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0) * 45;
int v11 = unsafe.getInt(dataFb, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4) + 43;
unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0, v10);
unsafe.putInt(dataLa, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4, v11);
float v20 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 0) + 0.55f;
float v21 = unsafe.getFloat(dataLb, unsafe.ARRAY_LONG_BASE_OFFSET + 4L * i + 4) + 0.55f;
unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0, v20);
unsafe.putFloat(dataIb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4, v21);
int v01 = unsafe.getInt(dataIa, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4) + 3; // moved down
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4, v01);
}
}
@ -83,8 +83,8 @@ public class TestIndependentPacksWithCyclicDependency2 {
static void verify(String name, float[] data, float[] gold) {
for (int i = 0; i < RANGE; i++) {
int datav = unsafe.getInt(data, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i);
int goldv = unsafe.getInt(gold, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i);
int datav = unsafe.getInt(data, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i);
int goldv = unsafe.getInt(gold, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i);
if (datav != goldv) {
throw new RuntimeException(" Invalid " + name + " result: dataF[" + i + "]: " + datav + " != " + goldv);
}

View File

@ -124,10 +124,10 @@ public class TestScheduleReordersScalarMemops {
for (int i = 0; i < RANGE; i+=2) {
// Do the same as test0, but without int-float conversion.
// This should reproduce on machines where conversion is not implemented.
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 0, dataIa[i+0] + 1); // A +1
dataIb[i+0] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 0); // X
dataIb[i+1] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4 * i + 4); // Y
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4 * i + 4, dataIa[i+1] * 11); // B *11
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 0, dataIa[i+0] + 1); // A +1
dataIb[i+0] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 0); // X
dataIb[i+1] = 11 * unsafe.getInt(dataFb, unsafe.ARRAY_INT_BASE_OFFSET + 4L * i + 4); // Y
unsafe.putInt(dataFa, unsafe.ARRAY_FLOAT_BASE_OFFSET + 4L * i + 4, dataIa[i+1] * 11); // B *11
}
}

View File

@ -59,6 +59,7 @@ import java.io.PrintWriter;
import java.io.Writer;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ProtocolException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URI;
@ -361,7 +362,7 @@ public class ExpectContinueTest implements HttpServerAdapters {
}
if (exceptionally && testThrowable != null) {
err.println("Finished exceptionally Test throwable: " + testThrowable);
assertEquals(IOException.class, testThrowable.getClass());
assertEquals(testThrowable.getClass(), ProtocolException.class);
} else if (exceptionally) {
throw new TestException("Expected case to finish with an IOException but testException is null");
} else if (resp != null) {

View File

@ -136,6 +136,7 @@ public class ShutdownNow implements HttpServerAdapters {
if (message.equals("shutdownNow")) return true;
// exception from cancelling an HTTP/2 stream
if (message.matches("Stream [0-9]+ cancelled")) return true;
if (message.contains("connection closed locally")) return true;
return false;
}

View File

@ -40,6 +40,7 @@ import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ProtocolException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpHeaders;
@ -195,7 +196,8 @@ public class PushPromiseContinuation {
client.sendAsync(hreq, HttpResponse.BodyHandlers.ofString(UTF_8), pph);
CompletionException t = expectThrows(CompletionException.class, () -> cf.join());
assertEquals(t.getCause().getClass(), IOException.class, "Expected an IOException but got " + t.getCause());
assertEquals(t.getCause().getClass(), ProtocolException.class,
"Expected a ProtocolException but got " + t.getCause());
System.err.println("Client received the following expected exception: " + t.getCause());
faultyServer.stop();
}
@ -222,7 +224,10 @@ public class PushPromiseContinuation {
static class Http2PushPromiseHeadersExchangeImpl extends Http2TestExchangeImpl {
Http2PushPromiseHeadersExchangeImpl(int streamid, String method, HttpHeaders reqheaders, HttpHeadersBuilder rspheadersBuilder, URI uri, InputStream is, SSLSession sslSession, BodyOutputStream os, Http2TestServerConnection conn, boolean pushAllowed) {
Http2PushPromiseHeadersExchangeImpl(int streamid, String method, HttpHeaders reqheaders,
HttpHeadersBuilder rspheadersBuilder, URI uri, InputStream is,
SSLSession sslSession, BodyOutputStream os,
Http2TestServerConnection conn, boolean pushAllowed) {
super(streamid, method, reqheaders, rspheadersBuilder, uri, is, sslSession, os, conn, pushAllowed);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 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
@ -241,6 +241,7 @@ public interface HttpServerAdapters {
public abstract void close();
public abstract InetSocketAddress getRemoteAddress();
public abstract String getConnectionKey();
public abstract InetSocketAddress getLocalAddress();
public void serverPush(URI uri, HttpHeaders headers, byte[] body) {
ByteArrayInputStream bais = new ByteArrayInputStream(body);
serverPush(uri, headers, bais);
@ -303,7 +304,10 @@ public interface HttpServerAdapters {
public InetSocketAddress getRemoteAddress() {
return exchange.getRemoteAddress();
}
@Override
public InetSocketAddress getLocalAddress() {
return exchange.getLocalAddress();
}
@Override
public URI getRequestURI() { return exchange.getRequestURI(); }
@Override
@ -370,6 +374,10 @@ public interface HttpServerAdapters {
public InetSocketAddress getRemoteAddress() {
return exchange.getRemoteAddress();
}
@Override
public InetSocketAddress getLocalAddress() {
return exchange.getLocalAddress();
}
@Override
public String getConnectionKey() {

View File

@ -0,0 +1,174 @@
/*
* Copyright (c) 2015, 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
* 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 jdk.httpclient.test.lib.http2;
import java.util.function.*;
import jdk.internal.net.http.hpack.Encoder;
import static java.lang.String.format;
import static java.util.Objects.requireNonNull;
import static jdk.internal.net.http.hpack.HPACK.Logger.Level.EXTRA;
import static jdk.internal.net.http.hpack.HPACK.Logger.Level.NORMAL;
public class HpackTestEncoder extends Encoder {
public HpackTestEncoder(int maxCapacity) {
super(maxCapacity);
}
/**
* Sets up the given header {@code (name, value)} with possibly sensitive
* value.
*
* <p> If the {@code value} is sensitive (think security, secrecy, etc.)
* this encoder will compress it using a special representation
* (see <a href="https://tools.ietf.org/html/rfc7541#section-6.2.3">6.2.3. Literal Header Field Never Indexed</a>).
*
* <p> Fixates {@code name} and {@code value} for the duration of encoding.
*
* @param name
* the name
* @param value
* the value
* @param sensitive
* whether or not the value is sensitive
*
* @throws NullPointerException
* if any of the arguments are {@code null}
* @throws IllegalStateException
* if the encoder hasn't fully encoded the previous header, or
* hasn't yet started to encode it
* @see #header(CharSequence, CharSequence)
* @see DecodingCallback#onDecoded(CharSequence, CharSequence, boolean)
*/
public void header(CharSequence name,
CharSequence value,
boolean sensitive) throws IllegalStateException {
if (sensitive || getMaxCapacity() == 0) {
super.header(name, value, true);
} else {
header(name, value, false, (n,v) -> false);
}
}
/**
* Sets up the given header {@code (name, value)} with possibly sensitive
* value.
*
* <p> If the {@code value} is sensitive (think security, secrecy, etc.)
* this encoder will compress it using a special representation
* (see <a href="https://tools.ietf.org/html/rfc7541#section-6.2.3">6.2.3. Literal Header Field Never Indexed</a>).
*
* <p> Fixates {@code name} and {@code value} for the duration of encoding.
*
* @param name
* the name
* @param value
* the value
* @param insertionPolicy
* a bipredicate to indicate whether a name value pair
* should be added to the dynamic table
*
* @throws NullPointerException
* if any of the arguments are {@code null}
* @throws IllegalStateException
* if the encoder hasn't fully encoded the previous header, or
* hasn't yet started to encode it
* @see #header(CharSequence, CharSequence)
* @see DecodingCallback#onDecoded(CharSequence, CharSequence, boolean)
*/
public void header(CharSequence name,
CharSequence value,
BiPredicate<CharSequence, CharSequence> insertionPolicy)
throws IllegalStateException {
header(name, value, false, insertionPolicy);
}
/**
* Sets up the given header {@code (name, value)} with possibly sensitive
* value.
*
* <p> If the {@code value} is sensitive (think security, secrecy, etc.)
* this encoder will compress it using a special representation
* (see <a href="https://tools.ietf.org/html/rfc7541#section-6.2.3">
* 6.2.3. Literal Header Field Never Indexed</a>).
*
* <p> Fixates {@code name} and {@code value} for the duration of encoding.
*
* @param name
* the name
* @param value
* the value
* @param sensitive
* whether or not the value is sensitive
* @param insertionPolicy
* a bipredicate to indicate whether a name value pair
* should be added to the dynamic table
*
* @throws NullPointerException
* if any of the arguments are {@code null}
* @throws IllegalStateException
* if the encoder hasn't fully encoded the previous header, or
* hasn't yet started to encode it
* @see #header(CharSequence, CharSequence)
* @see DecodingCallback#onDecoded(CharSequence, CharSequence, boolean)
*/
public void header(CharSequence name,
CharSequence value,
boolean sensitive,
BiPredicate<CharSequence, CharSequence> insertionPolicy)
throws IllegalStateException {
if (sensitive == true || getMaxCapacity() == 0 || !insertionPolicy.test(name, value)) {
super.header(name, value, sensitive);
return;
}
var logger = logger();
// Arguably a good balance between complexity of implementation and
// efficiency of encoding
requireNonNull(name, "name");
requireNonNull(value, "value");
var t = getHeaderTable();
int index = tableIndexOf(name, value);
if (logger.isLoggable(NORMAL)) {
logger.log(NORMAL, () -> format("encoding with indexing ('%s', '%s'): index:%s",
name, value, index));
}
if (index > 0) {
indexed(index);
} else {
boolean huffmanValue = isHuffmanBetterFor(value);
if (index < 0) {
literalWithIndexing(-index, value, huffmanValue);
} else {
boolean huffmanName = isHuffmanBetterFor(name);
literalWithIndexing(name, huffmanName, value, huffmanValue);
}
}
}
protected int calculateCapacity(int maxCapacity) {
return maxCapacity;
}
}

View File

@ -29,9 +29,12 @@ import java.io.OutputStream;
import java.net.URI;
import java.net.InetSocketAddress;
import java.net.http.HttpHeaders;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiPredicate;
import javax.net.ssl.SSLSession;
import jdk.internal.net.http.common.HttpHeadersBuilder;
import jdk.internal.net.http.frame.Http2Frame;
public interface Http2TestExchange {
@ -53,6 +56,12 @@ public interface Http2TestExchange {
void sendResponseHeaders(int rCode, long responseLength) throws IOException;
default void sendResponseHeaders(int rCode, long responseLength,
BiPredicate<CharSequence, CharSequence> insertionPolicy)
throws IOException {
sendResponseHeaders(rCode, responseLength);
}
InetSocketAddress getRemoteAddress();
int getResponseCode();
@ -65,6 +74,10 @@ public interface Http2TestExchange {
void serverPush(URI uri, HttpHeaders headers, InputStream content);
default void sendFrames(List<Http2Frame> frames) throws IOException {
throw new UnsupportedOperationException("not implemented");
}
/**
* Send a PING on this exchanges connection, and completes the returned CF
* with the number of milliseconds it took to get a valid response.

View File

@ -27,6 +27,7 @@ import jdk.httpclient.test.lib.http2.Http2TestServerConnection.ResponseHeaders;
import jdk.internal.net.http.common.HttpHeadersBuilder;
import jdk.internal.net.http.frame.HeaderFrame;
import jdk.internal.net.http.frame.HeadersFrame;
import jdk.internal.net.http.frame.Http2Frame;
import jdk.internal.net.http.frame.ResetFrame;
import javax.net.ssl.SSLSession;
@ -39,6 +40,7 @@ import java.net.http.HttpHeaders;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiPredicate;
public class Http2TestExchangeImpl implements Http2TestExchange {
@ -132,8 +134,13 @@ public class Http2TestExchangeImpl implements Http2TestExchange {
return os;
}
@Override
public void sendResponseHeaders(int rCode, long responseLength) throws IOException {
sendResponseHeaders(rCode, responseLength, (n,v) -> false);
}
@Override
public void sendResponseHeaders(int rCode, long responseLength,
BiPredicate<CharSequence, CharSequence> insertionPolicy)
throws IOException {
// Do not set Content-Length for 100, and do not set END_STREAM
if (rCode == 100) responseLength = 0;
@ -147,7 +154,7 @@ public class Http2TestExchangeImpl implements Http2TestExchange {
HttpHeaders headers = rspheadersBuilder.build();
ResponseHeaders response
= new ResponseHeaders(headers);
= new ResponseHeaders(headers, insertionPolicy);
response.streamid(streamid);
response.setFlag(HeaderFrame.END_HEADERS);
@ -172,6 +179,11 @@ public class Http2TestExchangeImpl implements Http2TestExchange {
conn.outputQ.put(response);
}
@Override
public void sendFrames(List<Http2Frame> frames) throws IOException {
conn.sendFrames(frames);
}
@Override
public InetSocketAddress getRemoteAddress() {
return (InetSocketAddress) conn.socket.getRemoteSocketAddress();

View File

@ -24,6 +24,8 @@
package jdk.httpclient.test.lib.http2;
import jdk.internal.net.http.common.HttpHeadersBuilder;
import jdk.internal.net.http.common.Log;
import jdk.internal.net.http.frame.ContinuationFrame;
import jdk.internal.net.http.frame.DataFrame;
import jdk.internal.net.http.frame.ErrorFrame;
import jdk.internal.net.http.frame.FramesDecoder;
@ -80,6 +82,7 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Predicate;
@ -87,6 +90,7 @@ import java.util.function.Predicate;
import static java.nio.charset.StandardCharsets.ISO_8859_1;
import static java.nio.charset.StandardCharsets.UTF_8;
import static jdk.internal.net.http.frame.ErrorFrame.REFUSED_STREAM;
import static jdk.internal.net.http.frame.SettingsFrame.DEFAULT_MAX_FRAME_SIZE;
import static jdk.internal.net.http.frame.SettingsFrame.HEADER_TABLE_SIZE;
/**
@ -105,7 +109,7 @@ public class Http2TestServerConnection {
final Http2TestExchangeSupplier exchangeSupplier;
final InputStream is;
final OutputStream os;
volatile Encoder hpackOut;
volatile HpackTestEncoder hpackOut;
volatile Decoder hpackIn;
volatile SettingsFrame clientSettings;
final SettingsFrame serverSettings;
@ -421,7 +425,9 @@ public class Http2TestServerConnection {
}
public int getMaxFrameSize() {
return clientSettings.getParameter(SettingsFrame.MAX_FRAME_SIZE);
var max = clientSettings.getParameter(SettingsFrame.MAX_FRAME_SIZE);
if (max <= 0) max = DEFAULT_MAX_FRAME_SIZE;
return max;
}
/** Sends a pre-canned HTTP/1.1 response. */
@ -482,7 +488,7 @@ public class Http2TestServerConnection {
//System.out.println("ServerSettings: " + serverSettings);
//System.out.println("ClientSettings: " + clientSettings);
hpackOut = new Encoder(serverSettings.getParameter(HEADER_TABLE_SIZE));
hpackOut = new HpackTestEncoder(serverSettings.getParameter(HEADER_TABLE_SIZE));
hpackIn = new Decoder(clientSettings.getParameter(HEADER_TABLE_SIZE));
if (!secure) {
@ -812,6 +818,14 @@ public class Http2TestServerConnection {
}
}
public void sendFrames(List<Http2Frame> frames) throws IOException {
synchronized (outputQ) {
for (var frame : frames) {
outputQ.put(frame);
}
}
}
protected HttpHeadersBuilder createNewHeadersBuilder() {
return new HttpHeadersBuilder();
}
@ -938,26 +952,38 @@ public class Http2TestServerConnection {
return (streamid & 0x01) == 0x00;
}
final ReentrantLock headersLock = new ReentrantLock();
/** Encodes an group of headers, without any ordering guarantees. */
public List<ByteBuffer> encodeHeaders(HttpHeaders headers) {
return encodeHeaders(headers, (n,v) -> false);
}
public List<ByteBuffer> encodeHeaders(HttpHeaders headers,
BiPredicate<CharSequence, CharSequence> insertionPolicy) {
List<ByteBuffer> buffers = new LinkedList<>();
ByteBuffer buf = getBuffer();
boolean encoded;
for (Map.Entry<String, List<String>> entry : headers.map().entrySet()) {
List<String> values = entry.getValue();
String key = entry.getKey().toLowerCase();
for (String value : values) {
do {
hpackOut.header(key, value);
encoded = hpackOut.encode(buf);
if (!encoded) {
buf.flip();
buffers.add(buf);
buf = getBuffer();
}
} while (!encoded);
headersLock.lock();
try {
for (Map.Entry<String, List<String>> entry : headers.map().entrySet()) {
List<String> values = entry.getValue();
String key = entry.getKey().toLowerCase();
for (String value : values) {
hpackOut.header(key, value, insertionPolicy);
do {
encoded = hpackOut.encode(buf);
if (!encoded && !buf.hasRemaining()) {
buf.flip();
buffers.add(buf);
buf = getBuffer();
}
} while (!encoded);
}
}
} finally {
headersLock.unlock();
}
buf.flip();
buffers.add(buf);
@ -970,18 +996,23 @@ public class Http2TestServerConnection {
ByteBuffer buf = getBuffer();
boolean encoded;
for (Map.Entry<String, String> entry : headers) {
String value = entry.getValue();
String key = entry.getKey().toLowerCase();
do {
headersLock.lock();
try {
for (Map.Entry<String, String> entry : headers) {
String value = entry.getValue();
String key = entry.getKey().toLowerCase();
hpackOut.header(key, value);
encoded = hpackOut.encode(buf);
if (!encoded) {
buf.flip();
buffers.add(buf);
buf = getBuffer();
}
} while (!encoded);
do {
encoded = hpackOut.encode(buf);
if (!encoded && !buf.hasRemaining()) {
buf.flip();
buffers.add(buf);
buf = getBuffer();
}
} while (!encoded);
}
} finally {
headersLock.unlock();
}
buf.flip();
buffers.add(buf);
@ -1008,10 +1039,50 @@ public class Http2TestServerConnection {
break;
} else throw x;
}
if (frame instanceof ResponseHeaders) {
ResponseHeaders rh = (ResponseHeaders)frame;
HeadersFrame hf = new HeadersFrame(rh.streamid(), rh.getFlags(), encodeHeaders(rh.headers));
writeFrame(hf);
if (frame instanceof ResponseHeaders rh) {
var buffers = encodeHeaders(rh.headers, rh.insertionPolicy);
int maxFrameSize = Math.min(rh.getMaxFrameSize(), getMaxFrameSize() - 64);
int next = 0;
int cont = 0;
do {
// If the total size of headers exceeds the max frame
// size we need to split the headers into one
// HeadersFrame + N x ContinuationFrames
int remaining = maxFrameSize;
var list = new ArrayList<ByteBuffer>(buffers.size());
for (; next < buffers.size(); next++) {
var b = buffers.get(next);
var len = b.remaining();
if (!b.hasRemaining()) continue;
if (len <= remaining) {
remaining -= len;
list.add(b);
} else {
if (next == 0) {
list.add(b.slice(b.position(), remaining));
b.position(b.position() + remaining);
remaining = 0;
}
break;
}
}
int flags = rh.getFlags();
if (next != buffers.size()) {
flags = flags & ~HeadersFrame.END_HEADERS;
}
if (cont > 0) {
flags = flags & ~HeadersFrame.END_STREAM;
}
HeaderFrame hf = cont == 0
? new HeadersFrame(rh.streamid(), flags, list)
: new ContinuationFrame(rh.streamid(), flags, list);
if (Log.headers()) {
// avoid too much chatter: log only if Log.headers() is enabled
System.err.println("TestServer writing " + hf);
}
writeFrame(hf);
cont++;
} while (next < buffers.size());
} else if (frame instanceof OutgoingPushPromise) {
handlePush((OutgoingPushPromise)frame);
} else
@ -1322,11 +1393,29 @@ public class Http2TestServerConnection {
// for the hashmap.
public static class ResponseHeaders extends Http2Frame {
HttpHeaders headers;
final HttpHeaders headers;
final BiPredicate<CharSequence, CharSequence> insertionPolicy;
final int maxFrameSize;
public ResponseHeaders(HttpHeaders headers) {
this(headers, (n,v) -> false);
}
public ResponseHeaders(HttpHeaders headers, BiPredicate<CharSequence, CharSequence> insertionPolicy) {
this(headers, insertionPolicy, Integer.MAX_VALUE);
}
public ResponseHeaders(HttpHeaders headers,
BiPredicate<CharSequence, CharSequence> insertionPolicy,
int maxFrameSize) {
super(0, 0);
this.headers = headers;
this.insertionPolicy = insertionPolicy;
this.maxFrameSize = maxFrameSize;
}
public int getMaxFrameSize() {
return maxFrameSize;
}
}

View File

@ -0,0 +1,97 @@
/*
* Copyright (c) 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8331446
* @summary Enforce the MAX_ARGUMENT_INDEX(10,000) implementation limit for the
* ArgumentIndex element in the MessageFormat pattern syntax. This
* should be checked during construction/applyPattern/readObject and should effectively
* prevent parse/format from being invoked with values over the limit.
* @run junit MaxArgumentIndexTest
*/
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import java.text.MessageFormat;
import java.util.Locale;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class MaxArgumentIndexTest {
// A MessageFormat pattern that contains an ArgumentIndex value
// which violates this implementation's limit: MAX_ARGUMENT_INDEX(10,000)
// As this check is exclusive, 10,000 will violate the limit
private static final String VIOLATES_MAX_ARGUMENT_INDEX = "{10000}";
// Check String constructor enforces the limit
@Test
public void constructorTest() {
assertThrows(IllegalArgumentException.class,
() -> new MessageFormat(VIOLATES_MAX_ARGUMENT_INDEX));
}
// Check String, Locale constructor enforces the limit
@ParameterizedTest
@MethodSource
public void constructorWithLocaleTest(Locale locale) {
assertThrows(IllegalArgumentException.class,
() -> new MessageFormat(VIOLATES_MAX_ARGUMENT_INDEX, locale));
}
// Provide some basic common locale values
private static Stream<Locale> constructorWithLocaleTest() {
return Stream.of(null, Locale.US, Locale.ROOT);
}
// Edge case: Test a locale dependent subformat (with null locale) with a
// violating ArgumentIndex. In this instance, the violating ArgumentIndex
// will be caught and IAE thrown instead of the NPE
@Test
public void localeDependentSubFormatTest() {
assertThrows(IllegalArgumentException.class,
() -> new MessageFormat("{10000,number,short}", null));
// For reference
assertThrows(NullPointerException.class,
() -> new MessageFormat("{999,number,short}", null));
}
// Check that the static format method enforces the limit
@Test
public void staticFormatTest() {
assertThrows(IllegalArgumentException.class,
() -> MessageFormat.format(VIOLATES_MAX_ARGUMENT_INDEX, new Object[]{1}));
}
// Check that applyPattern(String) enforces the limit
@Test
public void applyPatternTest() {
MessageFormat mf = new MessageFormat("");
assertThrows(IllegalArgumentException.class,
() -> mf.applyPattern(VIOLATES_MAX_ARGUMENT_INDEX));
}
}

View File

@ -0,0 +1,96 @@
/*
* Copyright (c) 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8331446
* @summary Check correctness of deserialization
* @run junit SerializationTest
*/
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.text.MessageFormat;
import java.util.Locale;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class SerializationTest {
// Ensure basic correctness of serialization round trip
@ParameterizedTest
@MethodSource
public void serializationRoundTrip(MessageFormat expectedMf)
throws IOException, ClassNotFoundException {
byte[] bytes = ser(expectedMf);
MessageFormat actualMf = (MessageFormat) deSer(bytes);
assertEquals(expectedMf, actualMf);
}
// Various valid MessageFormats
private static Stream<MessageFormat> serializationRoundTrip() {
return Stream.of(
// basic pattern
new MessageFormat("{0} foo"),
// Multiple arguments
new MessageFormat("{0} {1} foo"),
// duplicate arguments
new MessageFormat("{0} {0} {1} foo"),
// Non-ascending arguments
new MessageFormat("{1} {0} foo"),
// With locale
new MessageFormat("{1} {0} foo", Locale.UK),
// With null locale. (NPE not thrown, if no format defined)
new MessageFormat("{1} {0} foo", null),
// With formats
new MessageFormat("{0,number,short} {0} {1,date,long} foo")
);
}
// Utility method to serialize
private static byte[] ser(Object obj) throws IOException {
ByteArrayOutputStream byteArrayOutputStream = new
ByteArrayOutputStream();
ObjectOutputStream oos = new
ObjectOutputStream(byteArrayOutputStream);
oos.writeObject(obj);
return byteArrayOutputStream.toByteArray();
}
// Utility method to deserialize
private static Object deSer(byte[] bytes) throws
IOException, ClassNotFoundException {
ByteArrayInputStream byteArrayInputStream = new
ByteArrayInputStream(bytes);
ObjectInputStream ois = new
ObjectInputStream(byteArrayInputStream);
return ois.readObject();
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 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
@ -26,7 +26,7 @@
/*
* @test
* @bug 8043758
* @bug 8043758 8307383
* @summary Datagram Transport Layer Security (DTLS)
* @modules java.base/sun.security.util
* @library /test/lib
@ -36,6 +36,7 @@
import java.net.DatagramPacket;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.util.concurrent.atomic.AtomicBoolean;
/**
@ -73,11 +74,34 @@ public class InvalidRecords extends DTLSOverDatagram {
// ClientHello with cookie
needInvalidRecords.set(false);
System.out.println("invalidate ClientHello message");
if (ba[ba.length - 1] == (byte)0xFF) {
ba[ba.length - 1] = (byte)0xFE;
// We will alter the compression method field in order to make the cookie
// check fail.
ByteBuffer chRec = ByteBuffer.wrap(ba);
// Skip 59 bytes past the record header (13), the handshake header (12),
// the protocol version (2), and client random (32)
chRec.position(59);
// Jump past the session ID
int len = Byte.toUnsignedInt(chRec.get());
chRec.position(chRec.position() + len);
// Skip the cookie
len = Byte.toUnsignedInt(chRec.get());
chRec.position(chRec.position() + len);
// Skip past cipher suites
len = Short.toUnsignedInt(chRec.getShort());
chRec.position(chRec.position() + len);
// Read the data on the compression methods, should be at least 1
len = Byte.toUnsignedInt(chRec.get());
if (len >= 1) {
System.out.println("Detected compression methods (count = " + len + ")");
} else {
ba[ba.length - 1] = (byte)0xFF;
throw new RuntimeException("Got zero length comp methods");
}
// alter the first comp method.
int compMethodVal = Byte.toUnsignedInt(chRec.get(chRec.position()));
System.out.println("Changing value at position " + chRec.position() +
" from " + compMethodVal + " to " + ++compMethodVal);
chRec.put(chRec.position(), (byte)compMethodVal);
}
return super.createHandshakePacket(ba, socketAddr);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 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
@ -34,7 +34,15 @@ public class MFLNTest extends SSLEngineTestCase {
public static void main(String[] args) {
setUpAndStartKDCIfNeeded();
System.setProperty("jsse.enableMFLNExtension", "true");
for (int mfl = 4096; mfl >= 256; mfl /= 2) {
String testMode = System.getProperty("test.mode", "norm");
int mflLen;
if (testMode.equals("norm_sni")) {
mflLen = 512;
} else {
mflLen = 256;
}
for (int mfl = 4096; mfl >= mflLen; mfl /= 2) {
System.out.println("=============================================="
+ "==============");
System.out.printf("Testsing DTLS handshake with MFL = %d%n", mfl);