1 /*
2  * Copyright (C) 2020 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 com.android.test.protoinputstream;
18 
19 import android.util.proto.ProtoInputStream;
20 import android.util.proto.ProtoParseException;
21 import android.util.proto.ProtoStream;
22 
23 import junit.framework.TestCase;
24 
25 import java.io.ByteArrayInputStream;
26 import java.io.IOException;
27 import java.io.InputStream;
28 
29 public class ProtoInputStreamIncompleteValueTest extends TestCase {
30 
31     /**
32      * Test that an incomplete varint at the end of a stream throws an exception
33      */
testIncompleteVarint()34     public void testIncompleteVarint() throws IOException {
35         final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_INT32;
36 
37         final long fieldId1 = fieldFlags | ((long) 1 & 0x0ffffffffL);
38 
39         final byte[] protobuf = new byte[]{
40                 // 1 : varint -> invalid varint value
41                 (byte) 0x08,
42                 (byte) 0xff,
43         };
44 
45         InputStream stream = new ByteArrayInputStream(protobuf);
46         final ProtoInputStream pi = new ProtoInputStream(stream);
47 
48         while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
49             try {
50                 switch (pi.getFieldNumber()) {
51                     case (int) fieldId1:
52                         pi.readInt(fieldId1);
53                         fail("Should have thrown a ProtoParseException");
54                         break;
55                     default:
56                         fail("Unexpected field id " + pi.getFieldNumber());
57                 }
58             } catch (ProtoParseException ppe) {
59                 // good
60                 stream.close();
61                 return;
62             }
63         }
64         stream.close();
65         fail("Test should not have reached this point...");
66     }
67 
68     /**
69      * Test that an incomplete fixed64 at the end of a stream throws an exception
70      */
testIncompleteFixed64()71     public void testIncompleteFixed64() throws IOException {
72         final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FIXED64;
73 
74         final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
75 
76         final byte[] protobuf = new byte[]{
77                 // 2 -> invalid fixed64
78                 (byte) 0x11,
79                 (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
80                 (byte) 0x00, (byte) 0x00, (byte) 0x00,
81         };
82 
83         InputStream stream = new ByteArrayInputStream(protobuf);
84         final ProtoInputStream pi = new ProtoInputStream(stream);
85 
86         while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
87             try {
88                 switch (pi.getFieldNumber()) {
89                     case (int) fieldId2:
90                         pi.readLong(fieldId2);
91                         fail("Should have thrown a ProtoParseException");
92                         break;
93                     default:
94                         fail("Unexpected field id " + pi.getFieldNumber());
95                 }
96             } catch (ProtoParseException ppe) {
97                 // good
98                 stream.close();
99                 return;
100             }
101         }
102         stream.close();
103         fail("Test should not have reached this point...");
104     }
105 
106     /**
107      * Test that an incomplete length delimited value at the end of a stream throws an exception
108      */
testIncompleteLengthDelimited()109     public void testIncompleteLengthDelimited() throws IOException {
110         final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_BYTES;
111 
112         final long fieldId5 = fieldFlags | ((long) 5 & 0x0ffffffffL);
113 
114         final byte[] protobuf = new byte[]{
115                 // 5 -> invalid byte array (has size 5 but only 4 values)
116                 (byte) 0x2a,
117                 (byte) 0x05,
118                 (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x03,
119         };
120 
121         InputStream stream = new ByteArrayInputStream(protobuf);
122         final ProtoInputStream pi = new ProtoInputStream(stream);
123 
124         while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
125             try {
126                 switch (pi.getFieldNumber()) {
127                     case (int) fieldId5:
128                         pi.readBytes(fieldId5);
129                         fail("Should have thrown a ProtoParseException");
130                         break;
131                     default:
132                         fail("Unexpected field id " + pi.getFieldNumber());
133                 }
134             } catch (ProtoParseException ppe) {
135                 // good
136                 stream.close();
137                 return;
138             }
139         }
140         stream.close();
141         fail("Test should not have reached this point...");
142     }
143 
144     /**
145      * Test that an incomplete fixed32 at the end of a stream throws an exception
146      */
testIncompleteFixed32()147     public void testIncompleteFixed32() throws IOException {
148         final long fieldFlags = ProtoStream.FIELD_COUNT_SINGLE | ProtoStream.FIELD_TYPE_FIXED32;
149 
150         final long fieldId2 = fieldFlags | ((long) 2 & 0x0ffffffffL);
151 
152         final byte[] protobuf = new byte[]{
153                 // 2 ->  invalid fixed32
154                 (byte) 0x15,
155                 (byte) 0x01, (byte) 0x00, (byte) 0x00,
156         };
157 
158         InputStream stream = new ByteArrayInputStream(protobuf);
159         final ProtoInputStream pi = new ProtoInputStream(stream);
160 
161         while (pi.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
162             try {
163                 switch (pi.getFieldNumber()) {
164                     case (int) fieldId2:
165                         pi.readInt(fieldId2);
166                         fail("Should have thrown a ProtoParseException");
167                         break;
168                     default:
169                         fail("Unexpected field id " + pi.getFieldNumber());
170                 }
171             } catch (ProtoParseException ppe) {
172                 // good
173                 stream.close();
174                 return;
175             }
176         }
177         stream.close();
178         fail("Test should not have reached this point...");
179     }
180 }
181