JDK-8028591 : NegativeArraySizeException in sun.security.util.DerInputStream.getUnalignedBitString()
Type:Bug
Component:security-libs
Sub-Component:java.security
Affected Version:7,8
Priority:P4
Status:Closed
Resolution:Fixed
OS:generic
CPU:generic
Submitted:2013-11-19
Updated:2020-07-28
Resolved:2014-03-13
The Version table provides details related to the release that this issue/RFE will be addressed.
Unresolved : Release in which this issue/RFE will be addressed. Resolved: Release in which this issue/RFE has been resolved. Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.
sun.security.util.DerInputStream.getUnalignedBitString() tries to read DER-encoded bit string, but it does not check that length tag is zero or negative. As a result, NegativeArraySizeException can be thrown.
Comments
8u approval:
Reviewed by Martin Balao: https://mail.openjdk.java.net/pipermail/jdk8u-dev/2020-May/011765.html
Patch is a stable fix (present since 2014-03-25 in 9u) that adds necessary range checks. It's also on the Oracle parity list (8u261).
21-05-2020
RFR for 8u: https://mail.openjdk.java.net/pipermail/jdk8u-dev/2020-May/011747.html
20-05-2020
The problem was found during fuzz testing. The following application was used:
import java.io.FileInputStream;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
public class X509CertificateLoad {
/**
* Load a X509 certificate from file.
*/
public static void main(String[] args) throws Exception {
FileInputStream fis = new FileInputStream(args[0]);
CertificateFactory cf = CertificateFactory.getInstance("X509");
X509Certificate cert = (X509Certificate) cf.generateCertificate(fis);
System.out.println("SUCCESS");
}
}
It just loads x509 certificate from file that is fuzzed. Please see a test in webrev below that is based on this code. The test fails with the following output:
java.lang.NegativeArraySizeException
at sun.security.util.DerInputStream.getUnalignedBitString(DerInputStream.java:238)
at sun.security.x509.X509Key.parse(X509Key.java:170)
at sun.security.x509.CertificateX509Key.<init>(CertificateX509Key.java:75)
at sun.security.x509.X509CertInfo.parse(X509CertInfo.java:705)
at sun.security.x509.X509CertInfo.<init>(X509CertInfo.java:169)
at sun.security.x509.X509CertImpl.parse(X509CertImpl.java:1747)
at sun.security.x509.X509CertImpl.<init>(X509CertImpl.java:196)
at sun.security.provider.X509Factory.engineGenerateCertificate(X509Factory.java:97)
at java.security.cert.CertificateFactory.generateCertificate(CertificateFactory.java:339)
at X509BadCertificate.main(X509BadCertificate.java:48)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at com.sun.javatest.regtest.MainWrapper$MainThread.run(MainWrapper.java:94)
at java.lang.Thread.run(Thread.java:722)
The problem is in sun.security.util.DerInputStream.getUnalignedBitString() method:
http://hg.openjdk.java.net/jdk7u/jdk7u-dev/jdk/file/837746b32b81/src/share/classes/sun/security/util/DerInputStream.java
...
222 /**
223 * Get a bit string from the input stream. The bit string need
224 * not be byte-aligned.
225 */
226 public BitArray getUnalignedBitString() throws IOException {
227 if (buffer.read() != DerValue.tag_BitString)
228 throw new IOException("DER input not a bit string");
229
230 int length = getLength(buffer) - 1;
231
232 /*
233 * First byte = number of excess bits in the last octet of the
234 * representation.
235 */
236 int validBits = length*8 - buffer.read();
237
238 byte[] repn = new byte[length];
239
240 if ((length != 0) && (buffer.read(repn) != length))
241 throw new IOException("short read of DER bit string");
242 return new BitArray(validBits, repn);
243 }
...
sun.security.util.DerInputStream.getLength() method can return 0 or -1 values. Negative value means that indefinite-length encoding. There is not a check for 0 or -1 values, so NegativeArraySizeException can occur in 238 line.
This is not a regression in 7u, NPE is thrown on JDK 7 b147. The same issue is for JDK 8.
The certificate that was used for testing is invalid. Since indefinite-length encoding is not allowed for DER encoding, I think there could not be a valid certificate to reproduce this NegativeArraySizeException. But anyway it is expected that getLength() can return 0 or -1, and return length should be checked. So I think it is P4.
The problem can be fixed by adding a check that length is not 0 or negative:
http://cr.openjdk.java.net/~asmotrak/8028591/webrev.00/
07-12-2014
That's good. I guess you will give different names to the bad certs and each time a new one is found a new @run line will be added to X509BadCertificate.java. Also, why is othervm used? (We'd better move the discussion to a code review mail thread).
21-11-2013
Recently I found only JDK-8028431 on fuzzed certificates. The same test is used in both bugs.
20-11-2013
I see several bugs on fuzzed certificates. Is it possible to use the same test? We can create a sub-directory holding these bad certs and the test just iterates through all of them.