1 /*
2  * Copyright (C) 2009 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package libcore.java.nio.charset;
18 
19 import java.nio.ByteBuffer;
20 import java.nio.CharBuffer;
21 import java.nio.charset.Charset;
22 import java.nio.charset.CharsetDecoder;
23 import java.nio.charset.CharsetEncoder;
24 import junit.framework.TestCase;
25 
26 
27 /* See bug http://b/1844104.
28  * Checks for ICU encoder/decoder buffer corruption.
29  */
30 public class OldCharsetEncoderDecoderBufferTest extends TestCase {
31 
32     /* Checks for a buffer corruption that happens in ICU
33      * (CharsetDecoderICU) when a decode operation
34      * is done first with an out-buffer with hasArray()==true, and next with an out-buffer with
35      * hasArray()==false. In that situation ICU may overwrite the first out-buffer.
36      */
testDecoderOutputBuffer()37     public void testDecoderOutputBuffer() {
38         CharsetDecoder decoder = Charset.forName("UTF-8").newDecoder();
39 
40         char[] cBuf = new char[10];
41         CharBuffer out = CharBuffer.wrap(cBuf);
42         assertTrue(out.hasArray());
43         decoder.decode(ByteBuffer.wrap(new byte[]{(byte)'a', (byte)'b', (byte)'c', (byte)'d'}),
44                        out, false);
45 
46         assertEquals("abcd", new String(cBuf, 0, 4));
47         assertEquals(0, cBuf[4]);
48         assertEquals(0, cBuf[5]);
49 
50         byte[] bBuf = new byte[10];
51         out = ByteBuffer.wrap(bBuf).asCharBuffer();
52         assertFalse(out.hasArray());
53         decoder.decode(ByteBuffer.wrap(new byte[]{(byte)'x'}), out, true);
54 
55         assertEquals('x', bBuf[1]);
56         assertEquals(0, bBuf[3]);
57 
58         // check if the first buffer was corrupted by the second decode
59         assertEquals("abcd", new String(cBuf, 0, 4));
60         assertEquals(0, cBuf[4]);
61         assertEquals(0, cBuf[5]);
62     }
63 
64     /* Checks for a buffer corruption that happens in ICU
65      * (CharsetDecoderICU) when a decode operation
66      * is done first with an in-buffer with hasArray()==true, and next with an in-buffer with
67      * hasArray()==false. In that situation ICU may overwrite the array of the first in-buffer.
68      */
testDecoderInputBuffer()69     public void testDecoderInputBuffer() {
70         CharsetDecoder decoder = Charset.forName("UTF-8").newDecoder();
71         CharBuffer out = CharBuffer.wrap(new char[10]);
72 
73         byte[] inArray = {(byte)'a', (byte)'b'};
74         ByteBuffer inWithArray = ByteBuffer.wrap(inArray);
75         assertTrue(inWithArray.hasArray());
76         decoder.decode(inWithArray, out, false);
77         assertEquals('a', inArray[0]);
78         assertEquals('b', inArray[1]);
79 
80         // A read-only ByteBuffer must not expose its array.
81         ByteBuffer inWithoutArray = ByteBuffer.wrap(new byte[] { (byte) 'x' }).asReadOnlyBuffer();
82         assertFalse(inWithoutArray.hasArray());
83         decoder.decode(inWithoutArray, out, true);
84 
85         // check whether the first buffer was corrupted by the second decode
86         assertEquals('a', inArray[0]);
87         assertEquals('b', inArray[1]);
88     }
89 
90     /* Checks for a buffer corruption that happens in ICU
91      * (CharsetEncoderICU) when an encode operation
92      * is done first with an out-buffer with hasArray()==true, and next with an out-buffer with
93      * hasArray()==false. In that situation ICU may overwrite the first out-buffer.
94      */
testEncoderOutputBuffer()95     public void testEncoderOutputBuffer() {
96         CharsetEncoder encoder = Charset.forName("UTF-8").newEncoder();
97 
98         byte[] buffer = new byte[10];
99         ByteBuffer out = ByteBuffer.wrap(buffer);
100 
101         assertTrue(out.hasArray());
102         encoder.encode(CharBuffer.wrap("ab"), out, false);
103 
104         assertEquals('a', buffer[0]);
105         assertEquals('b', buffer[1]);
106         assertEquals(0, buffer[2]);
107 
108         out = ByteBuffer.allocateDirect(10);
109         // It's no longer possible to get a byte buffer without a backing byte[] on Android.
110         // This test is useless on Android, unless that changes again. (You can't even
111         // subclass ByteBuffer because -- although it's non-final -- both the RI and Android
112         // have [different] package-private abstract methods you'd need to implement but can't.)
113         //assertFalse(out.hasArray());
114         encoder.encode(CharBuffer.wrap("x"), out, true);
115 
116         // check whether the second decode corrupted the first buffer
117         assertEquals('a', buffer[0]);
118         assertEquals('b', buffer[1]);
119         assertEquals(0, buffer[2]);
120     }
121 
122     /* Checks for a buffer corruption that happens in ICU
123      * (CharsetEncoderICU) when an encode operation
124      * is done first with an in-buffer with hasArray()==true, and next with an in-buffer with
125      * hasArray()==false. In that situation ICU may overwrite the array of the first in-buffer.
126      */
testEncoderInputBuffer()127     public void testEncoderInputBuffer() {
128         CharsetEncoder encoder = Charset.forName("UTF-8").newEncoder();
129         ByteBuffer out = ByteBuffer.wrap(new byte[10]);
130 
131         char[] inArray = {'a', 'b'};
132         CharBuffer inWithArray = CharBuffer.wrap(inArray);
133         assertTrue(inWithArray.hasArray());
134         encoder.encode(inWithArray, out, false);
135 
136         assertEquals('a', inArray[0]);
137         assertEquals('b', inArray[1]);
138 
139         CharBuffer inWithoutArray = CharBuffer.wrap("x");
140         assertFalse(inWithoutArray.hasArray());
141         encoder.encode(inWithoutArray, out, true);
142 
143         // check whether the second decode corrupted the first buffer
144         assertEquals('a', inArray[0]);
145         assertEquals('b', inArray[1]);
146     }
147 }
148