diff --git a/closed/openjdk-tag.gmk b/closed/openjdk-tag.gmk index 122a2dab8c0..fda56401338 100644 --- a/closed/openjdk-tag.gmk +++ b/closed/openjdk-tag.gmk @@ -1 +1 @@ -OPENJDK_TAG := jdk8u352-b07 +OPENJDK_TAG := jdk8u352-b08 diff --git a/jdk/src/share/classes/com/sun/jndi/dns/DNSDatagramSocketFactory.java b/jdk/src/share/classes/com/sun/jndi/dns/DNSDatagramSocketFactory.java index 04b983f6920..e4a15d3a30f 100644 --- a/jdk/src/share/classes/com/sun/jndi/dns/DNSDatagramSocketFactory.java +++ b/jdk/src/share/classes/com/sun/jndi/dns/DNSDatagramSocketFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,6 +30,8 @@ import java.net.SocketException; import java.net.InetSocketAddress; import java.nio.channels.DatagramChannel; +import java.security.AccessController; +import java.security.PrivilegedExceptionAction; import java.util.Objects; import java.util.Random; @@ -50,6 +52,21 @@ private EphemeralPortRange() {} static final int RANGE = UPPER - LOWER + 1; } + private static int findFirstFreePort() { + PrivilegedExceptionAction action = () -> new DatagramSocket(0); + int port; + try { + @SuppressWarnings({"deprecated", "removal"}) + DatagramSocket ds = AccessController.doPrivileged(action); + try (DatagramSocket ds1 = ds) { + port = ds1.getLocalPort(); + } + } catch (Exception x) { + port = 0; + } + return port; + } + // Records a subset of max {@code capacity} previously used ports static final class PortHistory { final int capacity; @@ -74,7 +91,10 @@ public boolean contains(int port) { public boolean add(int port) { if (ports[index] != 0) { // at max capacity // remove one port at random and store the new port there - ports[random.nextInt(capacity)] = port; + // don't remove the last port + int remove = random.nextInt(capacity); + if ((remove +1) % capacity == index) remove = index; + ports[index = remove] = port; } else { // there's a free slot ports[index] = port; } @@ -90,7 +110,8 @@ public boolean offer(int port) { } } - int lastport = 0; + int lastport = findFirstFreePort(); + int lastSystemAllocated = lastport; int suitablePortCount; int unsuitablePortCount; final ProtocolFamily family; // null (default) means dual stack @@ -147,13 +168,16 @@ public synchronized DatagramSocket open() throws SocketException { s = openDefault(); lastport = s.getLocalPort(); if (lastseen == 0) { + lastSystemAllocated = lastport; history.offer(lastport); return s; } thresholdCrossed = suitablePortCount > thresholdCount; - boolean farEnough = Integer.bitCount(lastseen ^ lastport) > BIT_DEVIATION - && Math.abs(lastport - lastseen) > deviation; + boolean farEnough = farEnough(lastseen); + if (farEnough && lastSystemAllocated > 0) { + farEnough = farEnough(lastSystemAllocated); + } boolean recycled = history.contains(lastport); boolean suitable = (thresholdCrossed || farEnough && !recycled); if (suitable && !recycled) history.add(lastport); @@ -168,6 +192,7 @@ public synchronized DatagramSocket open() throws SocketException { // Either the underlying stack supports random UDP port allocation, // or the new port is sufficiently distant from last port to make // it look like it is. Let's use it. + lastSystemAllocated = lastport; return s; } @@ -218,24 +243,48 @@ synchronized boolean isUndecided() { && !isUsingNativePortRandomization(); } + private boolean farEnough(int port) { + return Integer.bitCount(port ^ lastport) > BIT_DEVIATION + && Math.abs(port - lastport) > deviation; + } + private DatagramSocket openRandom() { int maxtries = MAX_RANDOM_TRIES; while (maxtries-- > 0) { - int port = EphemeralPortRange.LOWER - + random.nextInt(EphemeralPortRange.RANGE); + int port; + boolean suitable; + boolean recycled; + int maxrandom = MAX_RANDOM_TRIES; + do { + port = EphemeralPortRange.LOWER + + random.nextInt(EphemeralPortRange.RANGE); + recycled = history.contains(port); + suitable = lastport == 0 || (farEnough(port) && !recycled); + } while (maxrandom-- > 0 && !suitable); + + // if no suitable port was found, try again + // this means we might call random MAX_RANDOM_TRIES x MAX_RANDOM_TRIES + // times - but that should be OK with MAX_RANDOM_TRIES = 5. + if (!suitable) continue; + try { if (family != null) { DatagramChannel c = DatagramChannel.open(family); try { DatagramSocket s = c.socket(); s.bind(new InetSocketAddress(port)); + lastport = s.getLocalPort(); + if (!recycled) history.add(port); return s; } catch (Throwable x) { c.close(); throw x; } } - return new DatagramSocket(port); + DatagramSocket s = new DatagramSocket(port); + lastport = s.getLocalPort(); + if (!recycled) history.add(port); + return s; } catch (IOException x) { // try again until maxtries == 0; } diff --git a/jdk/src/share/classes/com/sun/security/ntlm/Client.java b/jdk/src/share/classes/com/sun/security/ntlm/Client.java index 9aacd433793..d2226eba29d 100644 --- a/jdk/src/share/classes/com/sun/security/ntlm/Client.java +++ b/jdk/src/share/classes/com/sun/security/ntlm/Client.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -117,9 +117,10 @@ public byte[] type1() { * {@code nonce} is null for NTLM v1. */ public byte[] type3(byte[] type2, byte[] nonce) throws NTLMException { - if (type2 == null || (v != Version.NTLM && nonce == null)) { + if (type2 == null || (v != Version.NTLM && nonce == null) || + (nonce != null && nonce.length != 8)) { throw new NTLMException(NTLMException.PROTOCOL, - "type2 and nonce cannot be null"); + "type2 cannot be null, and nonce must be 8-byte long"); } debug("NTLM Client: Type 2 received\n"); debug(type2); diff --git a/jdk/src/share/classes/com/sun/security/ntlm/NTLM.java b/jdk/src/share/classes/com/sun/security/ntlm/NTLM.java index 747c4b7c7e9..bc3bab1259d 100644 --- a/jdk/src/share/classes/com/sun/security/ntlm/NTLM.java +++ b/jdk/src/share/classes/com/sun/security/ntlm/NTLM.java @@ -228,23 +228,27 @@ void writeBytes(int offset, byte[] data) { System.arraycopy(data, 0, internal, offset, data.length); } - void writeSecurityBuffer(int offset, byte[] data) { + void writeSecurityBuffer(int offset, byte[] data) throws NTLMException { if (data == null) { - writeShort(offset+4, current); + writeInt(offset+4, current); } else { int len = data.length; + if (len > 65535) { + throw new NTLMException(NTLMException.INVALID_INPUT, + "Invalid data length " + len); + } if (current + len > internal.length) { internal = Arrays.copyOf(internal, current + len + 256); } writeShort(offset, len); writeShort(offset+2, len); - writeShort(offset+4, current); + writeInt(offset+4, current); System.arraycopy(data, 0, internal, current, len); current += len; } } - void writeSecurityBuffer(int offset, String str, boolean unicode) { + void writeSecurityBuffer(int offset, String str, boolean unicode) throws NTLMException { try { writeSecurityBuffer(offset, str == null ? null : str.getBytes( unicode ? "UnicodeLittleUnmarked" : "ISO8859_1")); diff --git a/jdk/src/share/classes/com/sun/security/ntlm/NTLMException.java b/jdk/src/share/classes/com/sun/security/ntlm/NTLMException.java index 7275b4ae6d9..8681a79fd3c 100644 --- a/jdk/src/share/classes/com/sun/security/ntlm/NTLMException.java +++ b/jdk/src/share/classes/com/sun/security/ntlm/NTLMException.java @@ -70,6 +70,11 @@ public final class NTLMException extends GeneralSecurityException { */ public final static int PROTOCOL = 6; + /** + * If an invalid input is provided. + */ + public static final int INVALID_INPUT = 7; + private int errorCode; /** diff --git a/jdk/src/share/classes/com/sun/security/ntlm/Server.java b/jdk/src/share/classes/com/sun/security/ntlm/Server.java index adec1a3fa29..90919b5d459 100644 --- a/jdk/src/share/classes/com/sun/security/ntlm/Server.java +++ b/jdk/src/share/classes/com/sun/security/ntlm/Server.java @@ -1,6 +1,6 @@ /* - * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -86,9 +86,9 @@ public Server(String version, String domain) throws NTLMException { * {@code nonce} is null. */ public byte[] type2(byte[] type1, byte[] nonce) throws NTLMException { - if (nonce == null) { + if (nonce == null || nonce.length != 8) { throw new NTLMException(NTLMException.PROTOCOL, - "nonce cannot be null"); + "nonce must be 8-byte long"); } debug("NTLM Server: Type 1 received\n"); if (type1 != null) debug(type1); diff --git a/jdk/src/share/classes/java/math/BigDecimal.java b/jdk/src/share/classes/java/math/BigDecimal.java index 23ac74cddc8..9e70a59bb91 100644 --- a/jdk/src/share/classes/java/math/BigDecimal.java +++ b/jdk/src/share/classes/java/math/BigDecimal.java @@ -30,6 +30,10 @@ package java.math; import static java.math.BigInteger.LONG_MASK; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.ObjectStreamException; +import java.io.StreamCorruptedException; import java.util.Arrays; /** @@ -975,6 +979,15 @@ public BigDecimal(double val, MathContext mc) { this.precision = prec; } + /** + * Accept no subclasses. + */ + private static BigInteger toStrictBigInteger(BigInteger val) { + return (val.getClass() == BigInteger.class) ? + val : + new BigInteger(val.toByteArray().clone()); + } + /** * Translates a {@code BigInteger} into a {@code BigDecimal}. * The scale of the {@code BigDecimal} is zero. @@ -984,8 +997,8 @@ public BigDecimal(double val, MathContext mc) { */ public BigDecimal(BigInteger val) { scale = 0; - intVal = val; - intCompact = compactValFor(val); + intVal = toStrictBigInteger(val); + intCompact = compactValFor(intVal); } /** @@ -1001,7 +1014,7 @@ public BigDecimal(BigInteger val) { * @since 1.5 */ public BigDecimal(BigInteger val, MathContext mc) { - this(val,0,mc); + this(toStrictBigInteger(val), 0, mc); } /** @@ -1015,8 +1028,8 @@ public BigDecimal(BigInteger val, MathContext mc) { */ public BigDecimal(BigInteger unscaledVal, int scale) { // Negative scales are now allowed - this.intVal = unscaledVal; - this.intCompact = compactValFor(unscaledVal); + this.intVal = toStrictBigInteger(unscaledVal); + this.intCompact = compactValFor(this.intVal); this.scale = scale; } @@ -1036,6 +1049,7 @@ public BigDecimal(BigInteger unscaledVal, int scale) { * @since 1.5 */ public BigDecimal(BigInteger unscaledVal, int scale, MathContext mc) { + unscaledVal = toStrictBigInteger(unscaledVal); long compactVal = compactValFor(unscaledVal); int mcp = mc.precision; int prec = 0; @@ -3747,6 +3761,7 @@ private static class UnsafeHolder { private static final sun.misc.Unsafe unsafe; private static final long intCompactOffset; private static final long intValOffset; + private static final long scaleOffset; static { try { unsafe = sun.misc.Unsafe.getUnsafe(); @@ -3754,12 +3769,17 @@ private static class UnsafeHolder { (BigDecimal.class.getDeclaredField("intCompact")); intValOffset = unsafe.objectFieldOffset (BigDecimal.class.getDeclaredField("intVal")); + scaleOffset = unsafe.objectFieldOffset + (BigDecimal.class.getDeclaredField("scale")); } catch (Exception ex) { throw new ExceptionInInitializerError(ex); } } - static void setIntCompactVolatile(BigDecimal bd, long val) { - unsafe.putLongVolatile(bd, intCompactOffset, val); + + static void setIntValAndScaleVolatile(BigDecimal bd, BigInteger intVal, int scale) { + unsafe.putObjectVolatile(bd, intValOffset, intVal); + unsafe.putIntVolatile(bd, scaleOffset, scale); + unsafe.putLongVolatile(bd, intCompactOffset, compactValFor(intVal)); } static void setIntValVolatile(BigDecimal bd, BigInteger val) { @@ -3775,15 +3795,29 @@ static void setIntValVolatile(BigDecimal bd, BigInteger val) { */ private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { - // Read in all fields - s.defaultReadObject(); - // validate possibly bad fields - if (intVal == null) { - String message = "BigDecimal: null intVal in stream"; - throw new java.io.StreamCorruptedException(message); - // [all values of scale are now allowed] + // prepare to read the fields + ObjectInputStream.GetField fields = s.readFields(); + BigInteger serialIntVal = (BigInteger) fields.get("intVal", null); + + // Validate field data + if (serialIntVal == null) { + throw new StreamCorruptedException("Null or missing intVal in BigDecimal stream"); } - UnsafeHolder.setIntCompactVolatile(this, compactValFor(intVal)); + // Validate provenance of serialIntVal object + serialIntVal = toStrictBigInteger(serialIntVal); + + // Any integer value is valid for scale + int serialScale = fields.get("scale", 0); + + UnsafeHolder.setIntValAndScaleVolatile(this, serialIntVal, serialScale); + } + + /** + * Serialization without data not supported for this class. + */ + private void readObjectNoData() + throws ObjectStreamException { + throw new InvalidObjectException("Deserialized BigDecimal objects need data"); } /** diff --git a/jdk/src/share/classes/java/math/BigInteger.java b/jdk/src/share/classes/java/math/BigInteger.java index 43052c1b6e9..752556d542a 100644 --- a/jdk/src/share/classes/java/math/BigInteger.java +++ b/jdk/src/share/classes/java/math/BigInteger.java @@ -30,9 +30,11 @@ package java.math; import java.io.IOException; +import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ObjectStreamField; +import java.io.ObjectStreamException; import java.util.Arrays; import java.util.Random; import java.util.concurrent.ThreadLocalRandom; @@ -4506,17 +4508,21 @@ private void readObject(java.io.ObjectInputStream s) // prepare to read the alternate persistent fields ObjectInputStream.GetField fields = s.readFields(); - // Read the alternate persistent fields that we care about - int sign = fields.get("signum", -2); - byte[] magnitude = (byte[])fields.get("magnitude", null); + // Read and validate the alternate persistent fields that we + // care about, signum and magnitude - // Validate signum + // Read and validate signum + int sign = fields.get("signum", -2); if (sign < -1 || sign > 1) { String message = "BigInteger: Invalid signum value"; if (fields.defaulted("signum")) message = "BigInteger: Signum not present in stream"; throw new java.io.StreamCorruptedException(message); } + + // Read and validate magnitude + byte[] magnitude = (byte[])fields.get("magnitude", null); + magnitude = magnitude.clone(); // defensive copy int[] mag = stripLeadingZeroBytes(magnitude); if ((mag.length == 0) != (sign == 0)) { String message = "BigInteger: signum-magnitude mismatch"; @@ -4525,18 +4531,23 @@ private void readObject(java.io.ObjectInputStream s) throw new java.io.StreamCorruptedException(message); } + // Equivalent to checkRange() on mag local without assigning + // this.mag field + if (mag.length > MAX_MAG_LENGTH || + (mag.length == MAX_MAG_LENGTH && mag[0] < 0)) { + throw new java.io.StreamCorruptedException("BigInteger: Out of the supported range"); + } + // Commit final fields via Unsafe - UnsafeHolder.putSign(this, sign); + UnsafeHolder.putSignAndMag(this, sign, mag); + } - // Calculate mag field from magnitude and discard magnitude - UnsafeHolder.putMag(this, mag); - if (mag.length >= MAX_MAG_LENGTH) { - try { - checkRange(); - } catch (ArithmeticException e) { - throw new java.io.StreamCorruptedException("BigInteger: Out of the supported range"); - } - } + /** + * Serialization without data not supported for this class. + */ + private void readObjectNoData() + throws ObjectStreamException { + throw new InvalidObjectException("Deserialized BigInteger objects need data"); } // Support for resetting final fields while deserializing @@ -4556,11 +4567,8 @@ private static class UnsafeHolder { } } - static void putSign(BigInteger bi, int sign) { + static void putSignAndMag(BigInteger bi, int sign, int[] magnitude) { unsafe.putIntVolatile(bi, signumOffset, sign); - } - - static void putMag(BigInteger bi, int[] magnitude) { unsafe.putObjectVolatile(bi, magOffset, magnitude); } } diff --git a/jdk/src/share/classes/java/security/UnresolvedPermission.java b/jdk/src/share/classes/java/security/UnresolvedPermission.java index c7eee55af7c..4ef81b1bcaf 100644 --- a/jdk/src/share/classes/java/security/UnresolvedPermission.java +++ b/jdk/src/share/classes/java/security/UnresolvedPermission.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -151,7 +151,7 @@ public final class UnresolvedPermission extends Permission * Each chain is ordered bottom-to-top (i.e., with the signer certificate * first and the (root) certificate authority last). The signer * certificates are copied from the array. Subsequent changes to - * the array will not affect this UnsolvedPermission. + * the array will not affect this UnresolvedPermission. */ public UnresolvedPermission(String type, String name, @@ -163,59 +163,63 @@ public UnresolvedPermission(String type, if (type == null) throw new NullPointerException("type can't be null"); + // Perform a defensive copy and reassign certs if we have a non-null + // reference + if (certs != null) { + certs = certs.clone(); + } + this.type = type; this.name = name; this.actions = actions; + if (certs != null) { // Extract the signer certs from the list of certificates. - for (int i=0; i signerCerts = - new ArrayList<>(); - i = 0; - while (i < certs.length) { - signerCerts.add(certs[i]); - while (((i+1) < certs.length) && - ((X509Certificate)certs[i]).getIssuerDN().equals( - ((X509Certificate)certs[i+1]).getSubjectDN())) { - i++; - } - i++; - } - this.certs = - new java.security.cert.Certificate[signerCerts.size()]; - signerCerts.toArray(this.certs); + // extract the signer certs + ArrayList signerCerts = + new ArrayList<>(); + i = 0; + while (i < certs.length) { + signerCerts.add(certs[i]); + while (((i + 1) < certs.length) && + ((X509Certificate)certs[i]).getIssuerDN().equals( + ((X509Certificate)certs[i + 1]).getSubjectDN())) { + i++; } + i++; } + this.certs = + new java.security.cert.Certificate[signerCerts.size()]; + signerCerts.toArray(this.certs); } } @@ -308,6 +312,7 @@ Permission resolve(Permission p, java.security.cert.Certificate certs[]) { * * @return false. */ + @Override public boolean implies(Permission p) { return false; } @@ -328,6 +333,7 @@ public boolean implies(Permission p) { * type (class) name, permission name, actions, and * certificates as this object. */ + @Override public boolean equals(Object obj) { if (obj == this) return true; @@ -401,7 +407,7 @@ public boolean equals(Object obj) { * * @return a hash code value for this object. */ - + @Override public int hashCode() { int hash = type.hashCode(); if (name != null) @@ -421,6 +427,7 @@ public int hashCode() { * * @return the empty string "". */ + @Override public String getActions() { return ""; @@ -488,6 +495,7 @@ public java.security.cert.Certificate[] getUnresolvedCerts() { * * @return information about this UnresolvedPermission. */ + @Override public String toString() { return "(unresolved " + type + " " + name + " " + actions + ")"; } @@ -499,7 +507,7 @@ public String toString() { * @return a new PermissionCollection object suitable for * storing UnresolvedPermissions. */ - + @Override public PermissionCollection newPermissionCollection() { return new UnresolvedPermissionCollection(); } diff --git a/jdk/src/share/classes/sun/net/httpserver/HttpConnection.java b/jdk/src/share/classes/sun/net/httpserver/HttpConnection.java index c9f31efb7d4..dd3246db763 100644 --- a/jdk/src/share/classes/sun/net/httpserver/HttpConnection.java +++ b/jdk/src/share/classes/sun/net/httpserver/HttpConnection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,8 +29,6 @@ import javax.net.ssl.*; import java.nio.channels.*; import java.util.logging.Logger; -import com.sun.net.httpserver.*; -import com.sun.net.httpserver.spi.*; /** * encapsulates all the connection specific state for a HTTP/S connection @@ -54,14 +52,14 @@ class HttpConnection { SocketChannel chan; SelectionKey selectionKey; String protocol; - long time; - volatile long creationTime; // time this connection was created + long idleStartTime; // absolute time in milli seconds, starting when the connection was marked idle + volatile long reqStartedTime; // time when the request was initiated volatile long rspStartedTime; // time we started writing the response int remaining; boolean closed = false; Logger logger; - public enum State {IDLE, REQUEST, RESPONSE}; + public enum State {IDLE, REQUEST, RESPONSE, NEWLY_ACCEPTED}; volatile State state; public String toString() { diff --git a/jdk/src/share/classes/sun/net/httpserver/SSLStreams.java b/jdk/src/share/classes/sun/net/httpserver/SSLStreams.java index 04949f0adb8..bda5cceb215 100644 --- a/jdk/src/share/classes/sun/net/httpserver/SSLStreams.java +++ b/jdk/src/share/classes/sun/net/httpserver/SSLStreams.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -44,7 +44,6 @@ class SSLStreams { SSLContext sslctx; SocketChannel chan; - TimeSource time; ServerImpl server; SSLEngine engine; EngineWrapper wrapper; @@ -56,7 +55,6 @@ class SSLStreams { SSLStreams (ServerImpl server, SSLContext sslctx, SocketChannel chan) throws IOException { this.server = server; - this.time= (TimeSource)server; this.sslctx= sslctx; this.chan= chan; InetSocketAddress addr = diff --git a/jdk/src/share/classes/sun/net/httpserver/ServerConfig.java b/jdk/src/share/classes/sun/net/httpserver/ServerConfig.java index a6b7427822d..13efa315f10 100644 --- a/jdk/src/share/classes/sun/net/httpserver/ServerConfig.java +++ b/jdk/src/share/classes/sun/net/httpserver/ServerConfig.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,29 +35,35 @@ class ServerConfig { - private static final int DEFAULT_CLOCK_TICK = 10000 ; // 10 sec. + private static final int DEFAULT_IDLE_TIMER_SCHEDULE_MILLIS = 10000 ; // 10 sec. - /* These values must be a reasonable multiple of clockTick */ - private static final long DEFAULT_IDLE_INTERVAL = 30 ; // 5 min + private static final long DEFAULT_IDLE_INTERVAL_IN_SECS = 30; + private static final int DEFAULT_MAX_CONNECTIONS = -1 ; // no limit on maximum connections private static final int DEFAULT_MAX_IDLE_CONNECTIONS = 200 ; private static final long DEFAULT_MAX_REQ_TIME = -1; // default: forever private static final long DEFAULT_MAX_RSP_TIME = -1; // default: forever - private static final long DEFAULT_TIMER_MILLIS = 1000; + // default timer schedule, in milli seconds, for the timer task that's responsible for + // 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 long DEFAULT_DRAIN_AMOUNT = 64 * 1024; - private static int clockTick; - private static long idleInterval; + private static long idleTimerScheduleMillis; + private static long idleIntervalMillis; // The maximum number of bytes to drain from an inputstream private static long drainAmount; + // the maximum number of connections that the server will allow to be open + // after which it will no longer "accept()" any new connections, till the + // current connection count goes down due to completion of processing the requests + private static int maxConnections; private static int maxIdleConnections; // The maximum number of request headers allowable private static int maxReqHeaders; // max time a request or response is allowed to take private static long maxReqTime; private static long maxRspTime; - private static long timerMillis; + private static long reqRspTimerScheduleMillis; private static boolean debug; // the value of the TCP_NODELAY socket-level option @@ -68,11 +74,22 @@ class ServerConfig { new PrivilegedAction() { @Override public Void run () { - idleInterval = Long.getLong("sun.net.httpserver.idleInterval", - DEFAULT_IDLE_INTERVAL) * 1000; + idleIntervalMillis = Long.getLong("sun.net.httpserver.idleInterval", + DEFAULT_IDLE_INTERVAL_IN_SECS) * 1000; + if (idleIntervalMillis <= 0) { + idleIntervalMillis = DEFAULT_IDLE_INTERVAL_IN_SECS * 1000; + } + + idleTimerScheduleMillis = Long.getLong("sun.net.httpserver.clockTick", + DEFAULT_IDLE_TIMER_SCHEDULE_MILLIS); + if (idleTimerScheduleMillis <= 0) { + // ignore zero or negative value and use the default schedule + idleTimerScheduleMillis = DEFAULT_IDLE_TIMER_SCHEDULE_MILLIS; + } - clockTick = Integer.getInteger("sun.net.httpserver.clockTick", - DEFAULT_CLOCK_TICK); + maxConnections = Integer.getInteger( + "jdk.httpserver.maxConnections", + DEFAULT_MAX_CONNECTIONS); maxIdleConnections = Integer.getInteger( "sun.net.httpserver.maxIdleConnections", @@ -91,8 +108,13 @@ public Void run () { maxRspTime = Long.getLong("sun.net.httpserver.maxRspTime", DEFAULT_MAX_RSP_TIME); - timerMillis = Long.getLong("sun.net.httpserver.timerMillis", - DEFAULT_TIMER_MILLIS); + reqRspTimerScheduleMillis = Long.getLong("sun.net.httpserver.timerMillis", + DEFAULT_REQ_RSP_TIMER_TASK_SCHEDULE_MILLIS); + if (reqRspTimerScheduleMillis <= 0) { + // ignore any negative or zero value for this configuration and reset + // to default schedule + reqRspTimerScheduleMillis = DEFAULT_REQ_RSP_TIMER_TASK_SCHEDULE_MILLIS; + } debug = Boolean.getBoolean("sun.net.httpserver.debug"); @@ -145,14 +167,34 @@ static boolean debugEnabled() { return debug; } - static long getIdleInterval() { - return idleInterval; + /** + * {@return Returns the maximum duration, in milli seconds, a connection can be idle} + */ + static long getIdleIntervalMillis() { + return idleIntervalMillis; + } + + /** + * {@return Returns the schedule, in milli seconds, for the timer task that is responsible + * for managing the idle connections} + */ + static long getIdleTimerScheduleMillis() { + return idleTimerScheduleMillis; } - static int getClockTick() { - return clockTick; + /** + * @return Returns the maximum number of connections that can be open at any given time. + * This method can return a value of 0 or negative to represent that the limit hasn't + * been configured. + */ + static int getMaxConnections() { + return maxConnections; } + /** + * @return Returns the maximum number of connections that can be idle. This method + * can return a value of 0 or negative. + */ static int getMaxIdleConnections() { return maxIdleConnections; } @@ -165,16 +207,30 @@ static int getMaxReqHeaders() { return maxReqHeaders; } + /** + * @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 + * been configured. + */ static long getMaxReqTime() { return maxReqTime; } + /** + * @return Returns the maximum amount of time the server will wait for the response to be generated + * for a request that is being processed. This method can return a value of 0 or negative to + * imply no maximum limit has been configured. + */ static long getMaxRspTime() { return maxRspTime; } - static long getTimerMillis() { - return timerMillis; + /** + * {@return Returns the timer schedule of the task that's responsible for timing out + * request/response that have been running longer than any configured timeout} + */ + static long getReqRspTimerScheduleMillis() { + return reqRspTimerScheduleMillis; } static boolean noDelay() { diff --git a/jdk/src/share/classes/sun/net/httpserver/ServerImpl.java b/jdk/src/share/classes/sun/net/httpserver/ServerImpl.java index 04ac0f69e40..a5adbf609f8 100644 --- a/jdk/src/share/classes/sun/net/httpserver/ServerImpl.java +++ b/jdk/src/share/classes/sun/net/httpserver/ServerImpl.java @@ -25,23 +25,49 @@ package sun.net.httpserver; -import java.net.*; -import java.io.*; -import java.nio.channels.*; -import java.util.*; -import java.util.concurrent.*; +import com.sun.net.httpserver.Filter; +import com.sun.net.httpserver.Headers; +import com.sun.net.httpserver.HttpContext; +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpServer; +import com.sun.net.httpserver.HttpsConfigurator; +import sun.net.httpserver.HttpConnection.State; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.util.logging.Logger; import java.util.logging.Level; -import javax.net.ssl.*; -import com.sun.net.httpserver.*; +import java.net.BindException; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.channels.CancelledKeyException; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; import java.security.AccessController; import java.security.PrivilegedAction; -import sun.net.httpserver.HttpConnection.State; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.Executor; /** * Provides implementation for both HTTP and HTTPS */ -class ServerImpl implements TimeSource { +class ServerImpl { private String protocol; private boolean https; @@ -53,32 +79,47 @@ class ServerImpl implements TimeSource { private ServerSocketChannel schan; private Selector selector; private SelectionKey listenerKey; - private Set idleConnections; - private Set allConnections; + private final Set idleConnections; + // connections which have been accepted() by the server but which haven't + // yet sent any byte on the connection yet + private final Set newlyAcceptedConnections; + private final Set allConnections; /* following two are used to keep track of the times * when a connection/request is first received * and when we start to send the response */ - private Set reqConnections; - private Set rspConnections; + private final Set reqConnections; + private final Set rspConnections; private List events; - private Object lolock = new Object(); + private final Object lolock = new Object(); private volatile boolean finished = false; private volatile boolean terminating = false; private boolean bound = false; private boolean started = false; - private volatile long time; /* current time */ - private volatile long subticks = 0; - private volatile long ticks; /* number of clock ticks since server started */ private HttpServer wrapper; - final static int CLOCK_TICK = ServerConfig.getClockTick(); - final static long IDLE_INTERVAL = ServerConfig.getIdleInterval(); + // schedule for the timer task that's responsible for idle connection management + static final long IDLE_TIMER_TASK_SCHEDULE = ServerConfig.getIdleTimerScheduleMillis(); + static final int MAX_CONNECTIONS = ServerConfig.getMaxConnections(); final static int MAX_IDLE_CONNECTIONS = ServerConfig.getMaxIdleConnections(); - final static long TIMER_MILLIS = ServerConfig.getTimerMillis (); - final static long MAX_REQ_TIME=getTimeMillis(ServerConfig.getMaxReqTime()); - final static long MAX_RSP_TIME=getTimeMillis(ServerConfig.getMaxRspTime()); - final static boolean timer1Enabled = MAX_REQ_TIME != -1 || MAX_RSP_TIME != -1; + // schedule for the timer task that's responsible for request/response timeout management + static final long REQ_RSP_TIMER_SCHEDULE = ServerConfig.getReqRspTimerScheduleMillis(); + static final long MAX_REQ_TIME = getTimeMillis(ServerConfig.getMaxReqTime()); + static final long MAX_RSP_TIME = getTimeMillis(ServerConfig.getMaxRspTime()); + static final boolean reqRspTimeoutEnabled = MAX_REQ_TIME != -1 || MAX_RSP_TIME != -1; + // the maximum idle duration for a connection which is currently idle but has served + // some request in the past + static final long IDLE_INTERVAL = ServerConfig.getIdleIntervalMillis(); + // the maximum idle duration for a newly accepted connection which hasn't yet received + // the first byte of data on that connection + static final long NEWLY_ACCEPTED_CONN_IDLE_INTERVAL; + static { + // the idle duration of a newly accepted connection is considered to be the least of the + // configured idle interval and the configured max request time (if any). + NEWLY_ACCEPTED_CONN_IDLE_INTERVAL = MAX_REQ_TIME > 0 + ? Math.min(IDLE_INTERVAL, MAX_REQ_TIME) + : IDLE_INTERVAL; + } private Timer timer, timer1; private Logger logger; @@ -108,13 +149,13 @@ class ServerImpl implements TimeSource { allConnections = Collections.synchronizedSet (new HashSet()); reqConnections = Collections.synchronizedSet (new HashSet()); rspConnections = Collections.synchronizedSet (new HashSet()); - time = System.currentTimeMillis(); - timer = new Timer ("server-timer", true); - timer.schedule (new ServerTimerTask(), CLOCK_TICK, CLOCK_TICK); - if (timer1Enabled) { - timer1 = new Timer ("server-timer1", true); - timer1.schedule (new ServerTimerTask1(),TIMER_MILLIS,TIMER_MILLIS); - logger.config ("HttpServer timer1 enabled period in ms: "+TIMER_MILLIS); + newlyAcceptedConnections = Collections.synchronizedSet(new HashSet<>()); + timer = new Timer ("idle-timeout-task", true); + timer.schedule (new IdleTimeoutTask(), IDLE_TIMER_TASK_SCHEDULE, IDLE_TIMER_TASK_SCHEDULE); + if (reqRspTimeoutEnabled) { + timer1 = new Timer ("req-rsp-timeout-task", true); + timer1.schedule (new ReqRspTimeoutTask(), REQ_RSP_TIMER_SCHEDULE, REQ_RSP_TIMER_SCHEDULE); + logger.config ("HttpServer request/response timeout task schedule ms: "+REQ_RSP_TIMER_SCHEDULE); logger.config ("MAX_REQ_TIME: "+MAX_REQ_TIME); logger.config ("MAX_RSP_TIME: "+MAX_RSP_TIME); } @@ -201,8 +242,9 @@ public void stop (int delay) { } allConnections.clear(); idleConnections.clear(); + newlyAcceptedConnections.clear(); timer.cancel(); - if (timer1Enabled) { + if (reqRspTimeoutEnabled) { timer1.cancel(); } } @@ -256,10 +298,6 @@ public InetSocketAddress run() { }); } - Selector getSelector () { - return selector; - } - void addEvent (Event r) { synchronized (lolock) { events.add (r); @@ -269,6 +307,70 @@ void addEvent (Event r) { /* main server listener task */ + /** + * The Dispatcher is responsible for accepting any connections and then using those connections + * to processing any incoming requests. A connection is represented as an instance of + * sun.net.httpserver.HttpConnection. + * + * Connection states: + * An instance of HttpConnection goes through the following states: + * + * - NEWLY_ACCEPTED: A connection is marked as newly accepted as soon as the Dispatcher + * accept()s a connection. A newly accepted connection is added to a newlyAcceptedConnections + * collection. A newly accepted connection also gets added to the allConnections collection. + * The newlyAcceptedConnections isn't checked for any size limits, however, if the server is + * configured with a maximum connection limit, then the elements in the + * newlyAcceptedConnections will never exceed that configured limit (no explicit size checks + * are done on the newlyAcceptedConnections collection, since the maximum connection limit + * applies to connections across different connection states). A connection in NEWLY_ACCEPTED + * state is considered idle and is eligible for idle connection management. + * + * - REQUEST: A connection is marked to be in REQUEST state when the request processing starts + * on that connection. This typically happens when the first byte of data is received on a + * NEWLY_ACCEPTED connection or when new data arrives on a connection which was previously + * in IDLE state. When a connection is in REQUEST state, it implies that the connection is + * active and thus isn't eligible for idle connection management. If the server is configured + * with a maximum request timeout, then connections in REQUEST state are eligible + * for Request/Response timeout management. + * + * - RESPONSE: A connection is marked to be in RESPONSE state when the server has finished + * reading the request. A connection is RESPONSE state is considered active and isn't eligible + * for idle connection management. If the server is configured with a maximum response timeout, + * then connections in RESPONSE state are eligible for Request/Response timeout management. + * + * - IDLE: A connection is marked as IDLE when a request/response cycle (successfully) completes + * on that particular connection. Idle connections are held in a idleConnections collection. + * The idleConnections collection is limited in size and the size is decided by a server + * configuration. Connections in IDLE state get added to the idleConnections collection only + * if that collection hasn't reached the configured limit. If a connection has reached IDLE + * state and there's no more room in the idleConnections collection, then such a connection + * gets closed. Connections in idleConnections collection are eligible for idle connection + * management. + * + * Idle connection management: + * A timer task is responsible for closing idle connections. Each connection that is in a state + * which is eligible for idle timeout management (see above section on connection states) + * will have a corresponding idle expiration time associated with it. The idle timeout management + * task will check the expiration time of each such connection against the current time and will + * close the connection if the current time is either equal to or past the expiration time. + * + * Request/Response timeout management: + * The server can be optionally configured with a maximum request timeout and/or maximum response + * timeout. If either of these timeouts have been configured, then an additional timer task is + * run by the server. This timer task is then responsible for closing connections which have + * been in REQUEST or RESPONSE state for a period of time that exceeds the respective configured + * timeouts. + * + * Maximum connection limit management: + * The server can be optionally configured with a maximum connection limit. A value of 0 or + * negative integer is ignored and considered to represent no connection limit. In case of a + * positive integer value, any newly accepted connections will be first checked against the + * current count of established connections (held by the allConnections collection) and if the + * configured limit has reached, then the newly accepted connection will be closed immediately + * (even before setting its state to NEWLY_ACCEPTED or adding it to the newlyAcceptedConnections + * collection). + * + */ class Dispatcher implements Runnable { private void handleEvent (Event r) { @@ -318,8 +420,7 @@ void reRegister (HttpConnection c) { SelectionKey key = chan.register (selector, SelectionKey.OP_READ); key.attach (c); c.selectionKey = key; - c.time = getTime() + IDLE_INTERVAL; - idleConnections.add (c); + markIdle(c); } catch (IOException e) { dprint(e); logger.log(Level.FINER, "Dispatcher(8)", e); @@ -362,23 +463,33 @@ public void run() { continue; } SocketChannel chan = schan.accept(); + // optimist there's a channel + if (chan != null) { + if (MAX_CONNECTIONS > 0 && allConnections.size() >= MAX_CONNECTIONS) { + // we've hit max limit of current open connections, so we go + // ahead and close this connection without processing it + try { + chan.close(); + } catch (IOException ignore) { + } + // move on to next selected key + continue; + } - // Set TCP_NODELAY, if appropriate - if (ServerConfig.noDelay()) { - chan.socket().setTcpNoDelay(true); - } - - if (chan == null) { - continue; /* cancel something ? */ + // Set TCP_NODELAY, if appropriate + if (ServerConfig.noDelay()) { + chan.socket().setTcpNoDelay(true); + } + chan.configureBlocking (false); + SelectionKey newkey = + chan.register (selector, SelectionKey.OP_READ); + HttpConnection c = new HttpConnection (); + c.selectionKey = newkey; + c.setChannel (chan); + newkey.attach (c); + markNewlyAccepted(c); + allConnections.add (c); } - chan.configureBlocking (false); - SelectionKey newkey = chan.register (selector, SelectionKey.OP_READ); - HttpConnection c = new HttpConnection (); - c.selectionKey = newkey; - c.setChannel (chan); - newkey.attach (c); - requestStarted (c); - allConnections.add (c); } else { try { if (key.isReadable()) { @@ -388,10 +499,12 @@ public void run() { key.cancel(); chan.configureBlocking (true); - if (idleConnections.remove(conn)) { - // was an idle connection so add it - // to reqConnections set. - requestStarted (conn); + if (newlyAcceptedConnections.remove(conn) + || idleConnections.remove(conn)) { + // was either a newly accepted connection or an idle + // connection. In either case, we mark that the request + // has now started on this connection. + requestStarted(conn); } handle (chan, conn); } else { @@ -471,10 +584,14 @@ private void closeConnection(HttpConnection conn) { case IDLE: idleConnections.remove(conn); break; + case NEWLY_ACCEPTED: + newlyAcceptedConnections.remove(conn); + break; } assert !reqConnections.remove(conn); assert !rspConnections.remove(conn); assert !idleConnections.remove(conn); + assert !newlyAcceptedConnections.remove(conn); } /* per exchange task */ @@ -630,9 +747,9 @@ public void run () { rheaders.set ("Connection", "close"); } else if (chdr.equalsIgnoreCase ("keep-alive")) { rheaders.set ("Connection", "keep-alive"); - int idle=(int)(ServerConfig.getIdleInterval()/1000); + int idleSeconds = (int) (ServerConfig.getIdleIntervalMillis() / 1000); int max=ServerConfig.getMaxIdleConnections(); - String val = "timeout="+idle+", max="+max; + String val = "timeout="+idleSeconds+", max="+max; rheaders.set ("Keep-Alive", val); } } @@ -768,14 +885,6 @@ void logReply (int code, String requestStr, String text) { logger.fine (message); } - long getTicks() { - return ticks; - } - - public long getTime() { - return time; - } - void delay () { Thread.yield(); try { @@ -800,11 +909,23 @@ HttpServer getWrapper () { } void requestStarted (HttpConnection c) { - c.creationTime = getTime(); + c.reqStartedTime = System.currentTimeMillis(); c.setState (State.REQUEST); reqConnections.add (c); } + void markIdle(HttpConnection c) { + c.idleStartTime = System.currentTimeMillis(); + c.setState(State.IDLE); + idleConnections.add(c); + } + + void markNewlyAccepted(HttpConnection c) { + c.idleStartTime = System.currentTimeMillis(); + c.setState(State.NEWLY_ACCEPTED); + newlyAcceptedConnections.add(c); + } + // called after a request has been completely read // by the server. This stops the timer which would // close the connection if the request doesn't arrive @@ -815,7 +936,7 @@ void requestStarted (HttpConnection c) { void requestCompleted (HttpConnection c) { assert c.getState() == State.REQUEST; reqConnections.remove (c); - c.rspStartedTime = getTime(); + c.rspStartedTime = System.currentTimeMillis(); rspConnections.add (c); c.setState (State.RESPONSE); } @@ -828,38 +949,58 @@ void responseCompleted (HttpConnection c) { } /** + * Responsible for closing connections that have been idle. * TimerTask run every CLOCK_TICK ms */ - class ServerTimerTask extends TimerTask { + class IdleTimeoutTask extends TimerTask { public void run () { LinkedList toClose = new LinkedList(); - time = System.currentTimeMillis(); - ticks ++; + final long currentTime = System.currentTimeMillis(); synchronized (idleConnections) { - for (HttpConnection c : idleConnections) { - if (c.time <= time) { - toClose.add (c); + final Iterator it = idleConnections.iterator(); + while (it.hasNext()) { + final HttpConnection c = it.next(); + if (currentTime - c.idleStartTime >= IDLE_INTERVAL) { + toClose.add(c); + it.remove(); } } - for (HttpConnection c : toClose) { - idleConnections.remove (c); - allConnections.remove (c); - c.close(); + } + // if any newly accepted connection has been idle (i.e. no byte has been sent on that + // connection during the configured idle timeout period) then close it as well + synchronized (newlyAcceptedConnections) { + final Iterator it = newlyAcceptedConnections.iterator(); + while (it.hasNext()) { + final HttpConnection c = it.next(); + if (currentTime - c.idleStartTime >= NEWLY_ACCEPTED_CONN_IDLE_INTERVAL) { + toClose.add(c); + it.remove(); + } + } + } + for (HttpConnection c : toClose) { + allConnections.remove(c); + c.close(); + if (logger.isLoggable(Level.FINER)) { + logger.log (Level.FINER, "Closed idle connection " + c); } } } } - class ServerTimerTask1 extends TimerTask { + /** + * Responsible for closing connections which have timed out while in REQUEST or RESPONSE state + */ + class ReqRspTimeoutTask extends TimerTask { // runs every TIMER_MILLIS public void run () { LinkedList toClose = new LinkedList(); - time = System.currentTimeMillis(); + final long currentTime = System.currentTimeMillis(); synchronized (reqConnections) { if (MAX_REQ_TIME != -1) { for (HttpConnection c : reqConnections) { - if (c.creationTime + TIMER_MILLIS + MAX_REQ_TIME <= time) { + if (currentTime - c.reqStartedTime >= MAX_REQ_TIME) { toClose.add (c); } } @@ -875,7 +1016,7 @@ public void run () { synchronized (rspConnections) { if (MAX_RSP_TIME != -1) { for (HttpConnection c : rspConnections) { - if (c.rspStartedTime + TIMER_MILLIS +MAX_RSP_TIME <= time) { + if (currentTime - c.rspStartedTime >= MAX_RSP_TIME) { toClose.add (c); } } @@ -890,22 +1031,18 @@ public void run () { } } - void logStackTrace (String s) { - logger.finest (s); - StringBuilder b = new StringBuilder (); - StackTraceElement[] e = Thread.currentThread().getStackTrace(); - for (int i=0; i 0 ? milli : -1; } /* diff --git a/jdk/src/share/classes/sun/net/httpserver/TimeSource.java b/jdk/src/share/classes/sun/net/httpserver/TimeSource.java deleted file mode 100644 index 6a6827c3845..00000000000 --- a/jdk/src/share/classes/sun/net/httpserver/TimeSource.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2005, 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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 sun.net.httpserver; - -interface TimeSource { - public long getTime(); -} diff --git a/jdk/src/share/classes/sun/security/util/DerInputStream.java b/jdk/src/share/classes/sun/security/util/DerInputStream.java index 47aa976fbe1..ef51078ea29 100644 --- a/jdk/src/share/classes/sun/security/util/DerInputStream.java +++ b/jdk/src/share/classes/sun/security/util/DerInputStream.java @@ -272,6 +272,9 @@ public byte[] getOctetString() throws IOException { throw new IOException("DER input not an octet string"); int length = getDefiniteLength(buffer); + if (length > buffer.available()) + throw new IOException("short read of an octet string"); + byte[] retval = new byte[length]; if ((length != 0) && (buffer.read(retval) != length)) throw new IOException("Short read of DER octet string"); @@ -497,6 +500,10 @@ private String readString(byte stringTag, String stringName, stringName + " string"); int length = getDefiniteLength(buffer); + if (length > buffer.available()) + throw new IOException("short read of " + + stringName + " string"); + byte[] retval = new byte[length]; if ((length != 0) && (buffer.read(retval) != length)) throw new IOException("Short read of DER " + diff --git a/jdk/src/share/native/common/sizecalc.h b/jdk/src/share/native/common/sizecalc.h index 675750e8a46..8f9cc8d7bf8 100644 --- a/jdk/src/share/native/common/sizecalc.h +++ b/jdk/src/share/native/common/sizecalc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -46,8 +46,13 @@ #define IS_SAFE_SIZE_T(x) ((x) >= 0 && (unsigned long long)(x) <= SIZE_MAX) +#define IS_MUL_OVERFLOW(m, n) \ + ((m) != 0 && (n) != 0 && (((size_t)((m)*(n))) != (((size_t)(m)) * ((size_t)(n))))) + #define IS_SAFE_SIZE_MUL(m, n) \ - (IS_SAFE_SIZE_T(m) && IS_SAFE_SIZE_T(n) && ((m) == 0 || (n) == 0 || (size_t)(n) <= (SIZE_MAX / (size_t)(m)))) + (IS_SAFE_SIZE_T(m) && IS_SAFE_SIZE_T(n) && \ + ((m) == 0 || (n) == 0 || (size_t)(n) <= (SIZE_MAX / (size_t)(m))) && \ + !IS_MUL_OVERFLOW(m, n)) #define IS_SAFE_SIZE_ADD(a, b) \ (IS_SAFE_SIZE_T(a) && IS_SAFE_SIZE_T(b) && (size_t)(b) <= (SIZE_MAX - (size_t)(a))) diff --git a/jdk/src/share/native/sun/security/ec/impl/ec.c b/jdk/src/share/native/sun/security/ec/impl/ec.c index 264e1a86e11..3f016990aa7 100644 --- a/jdk/src/share/native/sun/security/ec/impl/ec.c +++ b/jdk/src/share/native/sun/security/ec/impl/ec.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2022, Oracle and/or its affiliates. All rights reserved. * Use is subject to license terms. * * This library is free software; you can redistribute it and/or @@ -924,6 +924,12 @@ ECDSA_VerifyDigest(ECPublicKey *key, const SECItem *signature, } ecParams = &(key->ecParams); + + if (EC_ValidatePublicKey(ecParams, &key->publicValue, kmflag) != SECSuccess) { + PORT_SetError(SEC_ERROR_BAD_KEY); + goto cleanup; + } + flen = (ecParams->fieldID.size + 7) >> 3; olen = ecParams->order.len; if (signature->len == 0 || signature->len%2 != 0 || diff --git a/jdk/test/com/sun/net/httpserver/bugs/6725892/Test.java b/jdk/test/com/sun/net/httpserver/bugs/6725892/Test.java index e09a33f653f..82063349cd3 100644 --- a/jdk/test/com/sun/net/httpserver/bugs/6725892/Test.java +++ b/jdk/test/com/sun/net/httpserver/bugs/6725892/Test.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,7 +24,7 @@ /** * @test * @bug 6725892 - * @run main/othervm -Dsun.net.httpserver.maxReqTime=2 Test + * @run main/othervm -Dsun.net.httpserver.maxReqTime=2 -Dsun.net.httpserver.clockTick=2000 Test * @summary */ diff --git a/jdk/test/java/math/BigDecimal/ConstructorUnscaledValue.java b/jdk/test/java/math/BigDecimal/ConstructorUnscaledValue.java new file mode 100644 index 00000000000..60eaeda44f9 --- /dev/null +++ b/jdk/test/java/math/BigDecimal/ConstructorUnscaledValue.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8282252 + * @summary Test constructors of BigDecimal to replace BigInteger subclasses + */ + +import java.math.*; + +public class ConstructorUnscaledValue { + public static void main(String... args) { + TestBigInteger tbi = new TestBigInteger(BigInteger.ONE); + // Create BigDecimal's using each of the three constructors + // with guards on the class of unscaledValue + BigDecimal[] values = { + new BigDecimal(tbi), + new BigDecimal(tbi, 2), + new BigDecimal(tbi, 3, MathContext.DECIMAL32), + }; + + for (BigDecimal bd : values) { + BigInteger unscaledValue = bd.unscaledValue(); + if (unscaledValue.getClass() != BigInteger.class) { + throw new RuntimeException("Bad class for unscaledValue"); + } + if (!unscaledValue.equals(BigInteger.ONE)) { + throw new RuntimeException("Bad value for unscaledValue"); + } + } + } + + private static class TestBigInteger extends BigInteger { + public TestBigInteger(BigInteger bi) { + super(bi.toByteArray()); + } + + @Override + public String toString() { + return java.util.Arrays.toString(toByteArray()); + } + } +} diff --git a/jdk/test/java/math/BigDecimal/SerializationTests.java b/jdk/test/java/math/BigDecimal/SerializationTests.java index 56b391ebdee..dec935718a6 100644 --- a/jdk/test/java/math/BigDecimal/SerializationTests.java +++ b/jdk/test/java/math/BigDecimal/SerializationTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,54 +23,128 @@ /* * @test - * @bug 6177836 + * @bug 6177836 8282252 * @summary Verify BigDecimal objects with collapsed values are serialized properly. - * @author Joseph D. Darcy */ import java.math.*; import java.io.*; +import java.util.Arrays; +import java.util.List; public class SerializationTests { - static void checkSerialForm(BigDecimal bd) throws Exception { + public static void main(String... args) throws Exception { + checkBigDecimalSerialRoundTrip(); + checkBigDecimalSubSerialRoundTrip(); + } + + private static void checkSerialForm(BigDecimal bd) throws Exception { + checkSerialForm0(bd); + checkSerialForm0(bd.negate()); + } + + private static void checkSerialForm0(BigDecimal bd) throws Exception { ByteArrayOutputStream bos = new ByteArrayOutputStream(); - ObjectOutputStream oos = new ObjectOutputStream(bos); - oos.writeObject(bd); - oos.flush(); - oos.close(); + try(ObjectOutputStream oos = new ObjectOutputStream(bos)) { + oos.writeObject(bd); + oos.flush(); + } + ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray())); BigDecimal tmp = (BigDecimal)ois.readObject(); if (!bd.equals(tmp) || - bd.hashCode() != tmp.hashCode()) { + bd.hashCode() != tmp.hashCode() || + bd.getClass() != tmp.getClass() || + // Directly test equality of components + bd.scale() != tmp.scale() || + !bd.unscaledValue().equals(tmp.unscaledValue())) { System.err.print(" original : " + bd); System.err.println(" (hash: 0x" + Integer.toHexString(bd.hashCode()) + ")"); System.err.print("serialized : " + tmp); System.err.println(" (hash: 0x" + Integer.toHexString(tmp.hashCode()) + ")"); throw new RuntimeException("Bad serial roundtrip"); } + + // If the class of the deserialized number is BigDecimal, + // verify the implementation constraint on the unscaled value + // having BigInteger class + if (tmp.getClass() == BigDecimal.class) { + if (tmp.unscaledValue().getClass() != BigInteger.class) { + throw new RuntimeException("Not using genuine BigInteger as an unscaled value"); + } + } } - public static void main(String[] args) throws Exception { - BigDecimal values[] = { - BigDecimal.ZERO, - BigDecimal.ONE, - BigDecimal.TEN, - new BigDecimal(0), - new BigDecimal(1), - new BigDecimal(10), - new BigDecimal(Integer.MAX_VALUE), - new BigDecimal(Long.MAX_VALUE-1), - new BigDecimal(BigInteger.valueOf(1), 1), - new BigDecimal(BigInteger.valueOf(100), 50), - }; + private static class BigIntegerSub extends BigInteger { + public BigIntegerSub(BigInteger bi) { + super(bi.toByteArray()); + } + + @Override + public String toString() { + return java.util.Arrays.toString(toByteArray()); + } + } + private static void checkBigDecimalSerialRoundTrip() throws Exception { + List values = + Arrays.asList(BigDecimal.ZERO, + BigDecimal.ONE, + BigDecimal.TEN, + new BigDecimal(0), + new BigDecimal(1), + new BigDecimal(10), + new BigDecimal(Integer.MAX_VALUE), + new BigDecimal(Long.MAX_VALUE-1), + new BigDecimal(BigInteger.valueOf(1), 1), + new BigDecimal(BigInteger.valueOf(100), 50), + new BigDecimal(new BigInteger("9223372036854775808"), // Long.MAX_VALUE + 1 + Integer.MAX_VALUE), + new BigDecimal(new BigInteger("9223372036854775808"), // Long.MAX_VALUE + 1 + Integer.MIN_VALUE), + new BigDecimal(new BigIntegerSub(BigInteger.ONE), 2)); for(BigDecimal value : values) { checkSerialForm(value); - checkSerialForm(value.negate()); } + } + private static class BigDecimalSub extends BigDecimal { + public BigDecimalSub(BigDecimal bd) { + super(bd.unscaledValue(), bd.scale()); + } + + @Override + public String toString() { + return unscaledValue() + "x10^" + (-scale()); + } + } + + // Subclass defining a serialVersionUID + private static class BigDecimalSubSVUID extends BigDecimal { + private static long serialVesionUID = 0x0123_4567_89ab_cdefL; + + public BigDecimalSubSVUID(BigDecimal bd) { + super(bd.unscaledValue(), bd.scale()); + } + } + + private static void checkBigDecimalSubSerialRoundTrip() throws Exception { + List values = + Arrays.asList(BigDecimal.ZERO, + BigDecimal.ONE, + BigDecimal.TEN, + new BigDecimal(BigInteger.TEN, 1234), + new BigDecimal(new BigInteger("9223372036854775808"), // Long.MAX_VALUE + 1 + Integer.MAX_VALUE), + new BigDecimal(new BigInteger("9223372036854775808"), // Long.MAX_VALUE + 1 + Integer.MIN_VALUE)); + + for(BigDecimal value : values) { + checkSerialForm(new BigDecimalSub(value)); + checkSerialForm(new BigDecimalSubSVUID(value)); + } } } diff --git a/jdk/test/java/math/BigInteger/SerializationTests.java b/jdk/test/java/math/BigInteger/SerializationTests.java new file mode 100644 index 00000000000..a8b3bfcb90a --- /dev/null +++ b/jdk/test/java/math/BigInteger/SerializationTests.java @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8282252 + * @summary Verify BigInteger objects are serialized properly. + */ + +import java.math.*; +import java.io.*; +import java.util.Arrays; +import java.util.List; + +public class SerializationTests { + + public static void main(String... args) throws Exception { + checkBigIntegerSerialRoundTrip(); + checkBigIntegerSubSerialRoundTrip(); + } + + private static void checkSerialForm(BigInteger bi) throws Exception { + checkSerialForm0(bi); + checkSerialForm0(bi.negate()); + } + + private static void checkSerialForm0(BigInteger bi) throws Exception { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + try(ObjectOutputStream oos = new ObjectOutputStream(bos)) { + oos.writeObject(bi); + oos.flush(); + } + + ObjectInputStream ois = new + ObjectInputStream(new ByteArrayInputStream(bos.toByteArray())); + BigInteger tmp = (BigInteger)ois.readObject(); + + if (!bi.equals(tmp) || + bi.hashCode() != tmp.hashCode() || + bi.getClass() != tmp.getClass() || + // For extra measure, directly test equality of components + bi.signum() != tmp.signum() || + !Arrays.equals(bi.toByteArray(), (tmp.toByteArray())) ) { + System.err.print(" original : " + bi); + System.err.println(" (hash: 0x" + Integer.toHexString(bi.hashCode()) + ")"); + System.err.print("serialized : " + tmp); + System.err.println(" (hash: 0x" + Integer.toHexString(tmp.hashCode()) + ")"); + throw new RuntimeException("Bad serial roundtrip"); + } + } + + private static void checkBigIntegerSerialRoundTrip() throws Exception { + List values = + Arrays.asList(BigInteger.ZERO, + BigInteger.ONE, + BigInteger.valueOf(2), + BigInteger.TEN, + BigInteger.valueOf(100), + BigInteger.valueOf(Integer.MAX_VALUE), + BigInteger.valueOf(Long.MAX_VALUE-1), + new BigInteger("9223372036854775808")); // Long.MAX_VALUE + 1 + + for(BigInteger value : values) { + checkSerialForm(value); + } + } + + // Subclass with specialized toString output + private static class BigIntegerSub extends BigInteger { + public BigIntegerSub(BigInteger bi) { + super(bi.toByteArray()); + } + + @Override + public String toString() { + return Arrays.toString(toByteArray()); + } + } + + // Subclass defining a serialVersionUID + private static class BigIntegerSubSVUID extends BigInteger { + private static long serialVesionUID = 0x0123_4567_89ab_cdefL; + + public BigIntegerSubSVUID(BigInteger bi) { + super(bi.toByteArray()); + } + + @Override + public String toString() { + return Arrays.toString(toByteArray()); + } + } + + // Subclass defining writeReplace + private static class BigIntegerSubWR extends BigInteger { + public BigIntegerSubWR(BigInteger bi) { + super(bi.toByteArray()); + } + + // Just return this; could use a serial proxy instead + private Object writeReplace() throws ObjectStreamException { + return this; + } + } + + + private static void checkBigIntegerSubSerialRoundTrip() throws Exception { + List values = Arrays.asList( + BigInteger.ZERO, + BigInteger.ONE, + BigInteger.TEN, + new BigInteger("9223372036854775808")); // Long.MAX_VALUE + 1 + + for(BigInteger value : values) { + checkSerialForm(new BigIntegerSub(value)); + checkSerialForm(new BigIntegerSubSVUID(value)); + checkSerialForm(new BigIntegerSubWR(value)); + } + } +} diff --git a/jdk/test/sun/net/www/protocol/http/NULLTargetInfoTest.java b/jdk/test/sun/net/www/protocol/http/NULLTargetInfoTest.java index b8b24c1ee08..b124c448550 100644 --- a/jdk/test/sun/net/www/protocol/http/NULLTargetInfoTest.java +++ b/jdk/test/sun/net/www/protocol/http/NULLTargetInfoTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /* * @test - * @bug 8151788 + * @bug 8151788 8286526 * @summary NullPointerException from ntlm.Client.type3 * @run main NULLTargetInfoTest */ @@ -41,7 +41,7 @@ public static void main(String[] args) throws Exception { "4E 54 4C 4D 53 53 50 00 02 00 00 00 00 00 00 00" + "00 00 00 00 05 82 89 00 0B 87 81 B6 2D 6E 8B C1" + "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"); - byte[] nonce = new byte[10]; + byte[] nonce = new byte[8]; c.type3(type2, nonce); }