[PATCH] CipherStream produces new byte array on every update or doFinal operation

Valerie Peng valerie.peng at oracle.com
Fri Jun 12 18:22:00 UTC 2015


Thanks! I have test/modified the patch locally but have yet to create a 
bug to track it.
Valerie

On 6/12/2015 4:52 AM, Sean Mullan wrote:
> FYI, I created a bug to track this: 
> https://bugs.openjdk.java.net/browse/JDK-8087327
>
> --Sean
>
> On 02/20/2015 02:50 PM, Valerie Peng wrote:
>>
>> I started with this, but I have a pressing higher priority tasks at 
>> hand.
>> So, it will take me a few weeks to wrap this up.
>> Regards,
>> Valerie
>>
>> On 2/17/2015 4:06 PM, Dai Nakanishi wrote:
>>> Hi,
>>>
>>> CipherInputStream and CipherOutputStream call the Cipher methods update
>>> and doFinal which produce new byte array. It may consume a large amount
>>> of memory.
>>>
>>> The purpose of my patch is to avoid this. Could you review the patch?
>>>
>>> Thanks,
>>> Dai
>>>
>>> --- old/src/share/classes/javax/crypto/CipherInputStream.java    Thu
>>> Feb 12 11:55:05 2015 +0900
>>> +++ new/src/share/classes/javax/crypto/CipherInputStream.java    Tue
>>> Feb 17 17:12:08 2015 +0900
>>> @@ -108,34 +108,50 @@
>>>           read = true;
>>>           if (readin == -1) {
>>>               done = true;
>>> +            ensureCapacity(0);
>>>               try {
>>> -                obuffer = cipher.doFinal();
>>> -            } catch (IllegalBlockSizeException | BadPaddingException
>>> e) {
>>> +                ofinish = cipher.doFinal(obuffer, 0);
>>> +            } catch (IllegalBlockSizeException | BadPaddingException
>>> +                    | ShortBufferException e) {
>>>                   obuffer = null;
>>>                   throw new IOException(e);
>>>               }
>>> -            if (obuffer == null)
>>> +            if (ofinish == 0)
>>>                   return -1;
>>>               else {
>>>                   ostart = 0;
>>> -                ofinish = obuffer.length;
>>>                   return ofinish;
>>>               }
>>>           }
>>> +        ensureCapacity(readin);
>>>           try {
>>> -            obuffer = cipher.update(ibuffer, 0, readin);
>>> +            ofinish = cipher.update(ibuffer, 0, readin, obuffer);
>>>           } catch (IllegalStateException e) {
>>>               obuffer = null;
>>>               throw e;
>>> +        } catch (ShortBufferException e) {
>>> +            obuffer = null;
>>> +            throw new IOException(e);
>>>           }
>>>           ostart = 0;
>>> -        if (obuffer == null)
>>> -            ofinish = 0;
>>> -        else ofinish = obuffer.length;
>>>           return ofinish;
>>>       }
>>>
>>>       /**
>>> +     * Ensure the capacity of the output buffer for the next
>>> +     * update or doFinal operation, given the input length
>>> +     *<code>inputLen</code>  (in bytes)
>>> +     *
>>> +     * @param inputLen the input length (in bytes)
>>> +     */
>>> +    private void ensureCapacity(int inputLen) {
>>> +        int outputLen = cipher.getOutputSize(inputLen);
>>> +        if (obuffer == null || obuffer.length<  outputLen) {
>>> +            obuffer = new byte[outputLen];
>>> +        }
>>> +    }
>>> +
>>> +    /**
>>>        * Constructs a CipherInputStream from an InputStream and a
>>>        * Cipher.
>>>        *<br>Note: if the specified input stream or cipher is
>>> @@ -311,10 +327,12 @@
>>>           try {
>>>               // throw away the unprocessed data
>>>               if (!done) {
>>> -                cipher.doFinal();
>>> +                ensureCapacity(0);
>>> +                cipher.doFinal(obuffer, 0);
>>>               }
>>>           }
>>> -        catch (BadPaddingException | IllegalBlockSizeException ex) {
>>> +        catch (BadPaddingException | IllegalBlockSizeException
>>> +                | ShortBufferException ex) {
>>>               /* If no data has been read from the stream to be
>>> en/decrypted,
>>>                  we supress any exceptions, and close quietly. */
>>>               if (read) {
>>>
>>>
>>> --- old/src/share/classes/javax/crypto/CipherOutputStream.java    Thu
>>> Feb 12 11:55:05 2015 +0900
>>> +++ new/src/share/classes/javax/crypto/CipherOutputStream.java    Tue
>>> Feb 17 18:48:01 2015 +0900
>>> @@ -74,6 +74,9 @@
>>>       // the buffer holding data ready to be written out
>>>       private byte[] obuffer;
>>>
>>> +    // the number of bytes stored in the output buffer
>>> +    private int ostored = 0;
>>> +
>>>       // stream status
>>>       private boolean closed = false;
>>>
>>> @@ -118,10 +121,15 @@
>>>        */
>>>       public void write(int b) throws IOException {
>>>           ibuffer[0] = (byte) b;
>>> -        obuffer = cipher.update(ibuffer, 0, 1);
>>> -        if (obuffer != null) {
>>> -            output.write(obuffer);
>>> -            obuffer = null;
>>> +        ensureCapacity(1);
>>> +        try {
>>> +            ostored = cipher.update(ibuffer, 0, 1, obuffer);
>>> +        } catch (ShortBufferException e) {
>>> +            throw new IOException(e);
>>> +        }
>>> +        if (ostored>  0) {
>>> +            output.write(obuffer, 0, ostored);
>>> +            ostored = 0;
>>>           }
>>>       };
>>>
>>> @@ -155,10 +163,15 @@
>>>        * @since      JCE1.2
>>>        */
>>>       public void write(byte b[], int off, int len) throws 
>>> IOException {
>>> -        obuffer = cipher.update(b, off, len);
>>> -        if (obuffer != null) {
>>> -            output.write(obuffer);
>>> -            obuffer = null;
>>> +        ensureCapacity(len);
>>> +        try {
>>> +            ostored = cipher.update(b, off, len, obuffer);
>>> +        } catch (ShortBufferException e) {
>>> +            throw new IOException(e);
>>> +        }
>>> +        if (ostored>  0) {
>>> +            output.write(obuffer, 0, ostored);
>>> +            ostored = 0;
>>>           }
>>>       }
>>>
>>> @@ -177,9 +190,9 @@
>>>        * @since      JCE1.2
>>>        */
>>>       public void flush() throws IOException {
>>> -        if (obuffer != null) {
>>> -            output.write(obuffer);
>>> -            obuffer = null;
>>> +        if (obuffer != null&&  ostored>  0) {
>>> +            output.write(obuffer, 0, ostored);
>>> +            ostored = 0;
>>>           }
>>>           output.flush();
>>>       }
>>> @@ -206,14 +219,31 @@
>>>           }
>>>
>>>           closed = true;
>>> +        ensureCapacity(0);
>>>           try {
>>> -            obuffer = cipher.doFinal();
>>> -        } catch (IllegalBlockSizeException | BadPaddingException e) {
>>> +            ostored = cipher.doFinal(obuffer, 0);
>>> +        } catch (IllegalBlockSizeException | BadPaddingException
>>> +                | ShortBufferException e) {
>>>               obuffer = null;
>>> +            ostored = 0;
>>>           }
>>>           try {
>>>               flush();
>>>           } catch (IOException ignored) {}
>>>           out.close();
>>> +    }
>>> +
>>> +    /**
>>> +     * Ensure the capacity of the output buffer for the next
>>> +     * update or doFinal operation, given the input length
>>> +     *<code>inputLen</code>  (in bytes)
>>> +     *
>>> +     * @param inputLen the input length (in bytes)
>>> +     */
>>> +    private void ensureCapacity(int inputLen) {
>>> +        int outputLen = cipher.getOutputSize(inputLen);
>>> +        if (obuffer == null || obuffer.length<  outputLen) {
>>> +            obuffer = new byte[outputLen];
>>> +        }
>>>       }
>>>   }
>>>



More information about the security-dev mailing list