summaryrefslogtreecommitdiffstats
path: root/meta/recipes-extended/libarchive/libarchive/CVE-2021-36976-1.patch
blob: fca53fc9b6ba942d5b077be5331f880dfd598cd8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
From 05ebb55896d10a9737dad9ae0303f7f45489ba6f Mon Sep 17 00:00:00 2001
From: Grzegorz Antoniak <ga@anadoxin.org>
Date: Sat, 13 Feb 2021 09:08:13 +0100
Subject: [PATCH] RAR5 reader: fixed out of bounds read in some files

Added more range checks in the bit stream reading functions
(read_bits_16 and read_bits_32) in order to better guard against out of
memory reads.

This commit contains a test with OSSFuzz sample #30448.

Upstream-Status: Backport [https://git.launchpad.net/ubuntu/+source/libarchive/plain/debian/patches/CVE-2021-36976-1.patch?h=applied/3.4.3-2ubuntu0.1]
CVE: CVE-2021-36976
Signed-off-by: Virendra Thakur <virendra.thakur@kpit.com>
---
 Makefile.am                                   |   1 +
 libarchive/archive_read_support_format_rar5.c | 108 ++++++++++--------
 libarchive/test/test_read_format_rar5.c       |  16 +++
 ...r5_decode_number_out_of_bounds_read.rar.uu |  10 ++
 4 files changed, 89 insertions(+), 46 deletions(-)
 create mode 100644 libarchive/test/test_read_format_rar5_decode_number_out_of_bounds_read.rar.uu

--- a/Makefile.am
+++ b/Makefile.am
@@ -883,6 +883,7 @@ libarchive_test_EXTRA_DIST=\
 	libarchive/test/test_read_format_rar5_arm_filter_on_window_boundary.rar.uu \
 	libarchive/test/test_read_format_rar5_different_winsize_on_merge.rar.uu \
 	libarchive/test/test_read_format_rar5_block_size_is_too_small.rar.uu \
+	libarchive/test/test_read_format_rar5_decode_number_out_of_bounds_read.rar.uu \
 	libarchive/test/test_read_format_raw.bufr.uu \
 	libarchive/test/test_read_format_raw.data.gz.uu \
 	libarchive/test/test_read_format_raw.data.Z.uu \
--- a/libarchive/archive_read_support_format_rar5.c
+++ b/libarchive/archive_read_support_format_rar5.c
@@ -1012,7 +1012,16 @@ static int read_var_sized(struct archive
 	return ret;
 }
 
-static int read_bits_32(struct rar5* rar, const uint8_t* p, uint32_t* value) {
+static int read_bits_32(struct archive_read* a, struct rar5* rar,
+	const uint8_t* p, uint32_t* value)
+{
+	if(rar->bits.in_addr >= rar->cstate.cur_block_size) {
+		archive_set_error(&a->archive,
+			ARCHIVE_ERRNO_PROGRAMMER,
+			"Premature end of stream during extraction of data (#1)");
+		return ARCHIVE_FATAL;
+	}
+
 	uint32_t bits = ((uint32_t) p[rar->bits.in_addr]) << 24;
 	bits |= p[rar->bits.in_addr + 1] << 16;
 	bits |= p[rar->bits.in_addr + 2] << 8;
@@ -1023,7 +1032,16 @@ static int read_bits_32(struct rar5* rar
 	return ARCHIVE_OK;
 }
 
-static int read_bits_16(struct rar5* rar, const uint8_t* p, uint16_t* value) {
+static int read_bits_16(struct archive_read* a, struct rar5* rar,
+	const uint8_t* p, uint16_t* value)
+{
+	if(rar->bits.in_addr >= rar->cstate.cur_block_size) {
+		archive_set_error(&a->archive,
+			ARCHIVE_ERRNO_PROGRAMMER,
+			"Premature end of stream during extraction of data (#2)");
+		return ARCHIVE_FATAL;
+	}
+
 	int bits = (int) ((uint32_t) p[rar->bits.in_addr]) << 16;
 	bits |= (int) p[rar->bits.in_addr + 1] << 8;
 	bits |= (int) p[rar->bits.in_addr + 2];
@@ -1039,8 +1057,8 @@ static void skip_bits(struct rar5* rar,
 }
 
 /* n = up to 16 */
-static int read_consume_bits(struct rar5* rar, const uint8_t* p, int n,
-    int* value)
+static int read_consume_bits(struct archive_read* a, struct rar5* rar,
+	const uint8_t* p, int n, int* value)
 {
 	uint16_t v;
 	int ret, num;
@@ -1051,7 +1069,7 @@ static int read_consume_bits(struct rar5
 		return ARCHIVE_FATAL;
 	}
 
-	ret = read_bits_16(rar, p, &v);
+	ret = read_bits_16(a, rar, p, &v);
 	if(ret != ARCHIVE_OK)
 		return ret;
 
@@ -2425,13 +2443,13 @@ static int create_decode_tables(uint8_t*
 static int decode_number(struct archive_read* a, struct decode_table* table,
     const uint8_t* p, uint16_t* num)
 {
-	int i, bits, dist;
+	int i, bits, dist, ret;
 	uint16_t bitfield;
 	uint32_t pos;
 	struct rar5* rar = get_context(a);
 
-	if(ARCHIVE_OK != read_bits_16(rar, p, &bitfield)) {
-		return ARCHIVE_EOF;
+	if(ARCHIVE_OK != (ret = read_bits_16(a, rar, p, &bitfield))) {
+		return ret;
 	}
 
 	bitfield &= 0xfffe;
@@ -2537,14 +2555,6 @@ static int parse_tables(struct archive_r
 	for(i = 0; i < HUFF_TABLE_SIZE;) {
 		uint16_t num;
 
-		if((rar->bits.in_addr + 6) >= rar->cstate.cur_block_size) {
-			/* Truncated data, can't continue. */
-			archive_set_error(&a->archive,
-			    ARCHIVE_ERRNO_FILE_FORMAT,
-			    "Truncated data in huffman tables (#2)");
-			return ARCHIVE_FATAL;
-		}
-
 		ret = decode_number(a, &rar->cstate.bd, p, &num);
 		if(ret != ARCHIVE_OK) {
 			archive_set_error(&a->archive,
@@ -2561,8 +2571,8 @@ static int parse_tables(struct archive_r
 			/* 16..17: repeat previous code */
 			uint16_t n;
 
-			if(ARCHIVE_OK != read_bits_16(rar, p, &n))
-				return ARCHIVE_EOF;
+			if(ARCHIVE_OK != (ret = read_bits_16(a, rar, p, &n)))
+				return ret;
 
 			if(num == 16) {
 				n >>= 13;
@@ -2590,8 +2600,8 @@ static int parse_tables(struct archive_r
 			/* other codes: fill with zeroes `n` times */
 			uint16_t n;
 
-			if(ARCHIVE_OK != read_bits_16(rar, p, &n))
-				return ARCHIVE_EOF;
+			if(ARCHIVE_OK != (ret = read_bits_16(a, rar, p, &n)))
+				return ret;
 
 			if(num == 18) {
 				n >>= 13;
@@ -2707,22 +2717,22 @@ static int parse_block_header(struct arc
 }
 
 /* Convenience function used during filter processing. */
-static int parse_filter_data(struct rar5* rar, const uint8_t* p,
-    uint32_t* filter_data)
+static int parse_filter_data(struct archive_read* a, struct rar5* rar,
+	const uint8_t* p, uint32_t* filter_data)
 {
-	int i, bytes;
+	int i, bytes, ret;
 	uint32_t data = 0;
 
-	if(ARCHIVE_OK != read_consume_bits(rar, p, 2, &bytes))
-		return ARCHIVE_EOF;
+	if(ARCHIVE_OK != (ret = read_consume_bits(a, rar, p, 2, &bytes)))
+		return ret;
 
 	bytes++;
 
 	for(i = 0; i < bytes; i++) {
 		uint16_t byte;
 
-		if(ARCHIVE_OK != read_bits_16(rar, p, &byte)) {
-			return ARCHIVE_EOF;
+		if(ARCHIVE_OK != (ret = read_bits_16(a, rar, p, &byte))) {
+			return ret;
 		}
 
 		/* Cast to uint32_t will ensure the shift operation will not
@@ -2765,16 +2775,17 @@ static int parse_filter(struct archive_r
 	uint16_t filter_type;
 	struct filter_info* filt = NULL;
 	struct rar5* rar = get_context(ar);
+	int ret;
 
 	/* Read the parameters from the input stream. */
-	if(ARCHIVE_OK != parse_filter_data(rar, p, &block_start))
-		return ARCHIVE_EOF;
+	if(ARCHIVE_OK != (ret = parse_filter_data(ar, rar, p, &block_start)))
+		return ret;
 
-	if(ARCHIVE_OK != parse_filter_data(rar, p, &block_length))
-		return ARCHIVE_EOF;
+	if(ARCHIVE_OK != (ret = parse_filter_data(ar, rar, p, &block_length)))
+		return ret;
 
-	if(ARCHIVE_OK != read_bits_16(rar, p, &filter_type))
-		return ARCHIVE_EOF;
+	if(ARCHIVE_OK != (ret = read_bits_16(ar, rar, p, &filter_type)))
+		return ret;
 
 	filter_type >>= 13;
 	skip_bits(rar, 3);
@@ -2814,8 +2825,8 @@ static int parse_filter(struct archive_r
 	if(filter_type == FILTER_DELTA) {
 		int channels;
 
-		if(ARCHIVE_OK != read_consume_bits(rar, p, 5, &channels))
-			return ARCHIVE_EOF;
+		if(ARCHIVE_OK != (ret = read_consume_bits(ar, rar, p, 5, &channels)))
+			return ret;
 
 		filt->channels = channels + 1;
 	}
@@ -2823,10 +2834,11 @@ static int parse_filter(struct archive_r
 	return ARCHIVE_OK;
 }
 
-static int decode_code_length(struct rar5* rar, const uint8_t* p,
-    uint16_t code)
+static int decode_code_length(struct archive_read* a, struct rar5* rar,
+	const uint8_t* p, uint16_t code)
 {
 	int lbits, length = 2;
+
 	if(code < 8) {
 		lbits = 0;
 		length += code;
@@ -2838,7 +2850,7 @@ static int decode_code_length(struct rar
 	if(lbits > 0) {
 		int add;
 
-		if(ARCHIVE_OK != read_consume_bits(rar, p, lbits, &add))
+		if(ARCHIVE_OK != read_consume_bits(a, rar, p, lbits, &add))
 			return -1;
 
 		length += add;
@@ -2933,7 +2945,7 @@ static int do_uncompress_block(struct ar
 			continue;
 		} else if(num >= 262) {
 			uint16_t dist_slot;
-			int len = decode_code_length(rar, p, num - 262),
+			int len = decode_code_length(a, rar, p, num - 262),
 				dbits,
 				dist = 1;
 
@@ -2975,12 +2987,12 @@ static int do_uncompress_block(struct ar
 					uint16_t low_dist;
 
 					if(dbits > 4) {
-						if(ARCHIVE_OK != read_bits_32(
-						    rar, p, &add)) {
+						if(ARCHIVE_OK != (ret = read_bits_32(
+						    a, rar, p, &add))) {
 							/* Return EOF if we
 							 * can't read more
 							 * data. */
-							return ARCHIVE_EOF;
+							return ret;
 						}
 
 						skip_bits(rar, dbits - 4);
@@ -3015,11 +3027,11 @@ static int do_uncompress_block(struct ar
 					/* dbits is one of [0,1,2,3] */
 					int add;
 
-					if(ARCHIVE_OK != read_consume_bits(rar,
-					     p, dbits, &add)) {
+					if(ARCHIVE_OK != (ret = read_consume_bits(a, rar,
+					     p, dbits, &add))) {
 						/* Return EOF if we can't read
 						 * more data. */
-						return ARCHIVE_EOF;
+						return ret;
 					}
 
 					dist += add;
@@ -3076,7 +3088,11 @@ static int do_uncompress_block(struct ar
 				return ARCHIVE_FATAL;
 			}
 
-			len = decode_code_length(rar, p, len_slot);
+			len = decode_code_length(a, rar, p, len_slot);
+			if (len == -1) {
+				return ARCHIVE_FATAL;
+			}
+
 			rar->cstate.last_len = len;
 
 			if(ARCHIVE_OK != copy_string(a, len, dist))
--- a/libarchive/test/test_read_format_rar5.c
+++ b/libarchive/test/test_read_format_rar5.c
@@ -1271,3 +1271,20 @@ DEFINE_TEST(test_read_format_rar5_block_
 
 	EPILOGUE();
 }
+
+DEFINE_TEST(test_read_format_rar5_decode_number_out_of_bounds_read)
+{
+	/* oss fuzz 30448 */
+
+	char buf[4096];
+	PROLOGUE("test_read_format_rar5_decode_number_out_of_bounds_read.rar");
+
+	/* Return codes of those calls are ignored, because this sample file
+	 * is invalid. However, the unpacker shouldn't produce any SIGSEGV
+	 * errors during processing. */
+
+	(void) archive_read_next_header(a, &ae);
+	while(0 < archive_read_data(a, buf, sizeof(buf))) {}
+
+	EPILOGUE();
+}
--- /dev/null
+++ b/libarchive/test/test_read_format_rar5_decode_number_out_of_bounds_read.rar.uu
@@ -0,0 +1,10 @@
+begin 644 test_read_format_rar5_decode_number_out_of_bounds_read.rar
+M4F%R(1H'`0!3@"KT`P+G(@(0("`@@`L!!"`@("`@(($D_[BJ2"!::7!)210V
+M+0#ZF#)Q!`+>YPW_("`@("``_R````````````````````````````!__P``
+M``````!T72`@/EW_(/\@("`@("`@("`@("`@("`@("`@("`@("`@(/\@("`@
+M("`@("#_("`@("`@("`@("`@("`@("`@("`@("`@("#_("`@("`@("`@_R`@
+M("`@("`@("`@("`@("`@("`@("`@("`@_R`@("`@("`@(/\@("`@("`@("`@
+M("`@("`@("`@("`@("`@(/\@("`@("`@("#_("`@("`@("`@("`@("`@("`@
+E("`@("`@("#_("`@("`@("`@_R`@("`@("`@("`@("`@("`@(```
+`
+end