Skip to content

Commit cc584b9

Browse files
herbertxvijay-suman
authored andcommitted
crypto: lzo - Fix compression buffer overrun
[ Upstream commit cc47f07 ] Unlike the decompression code, the compression code in LZO never checked for output overruns. It instead assumes that the caller always provides enough buffer space, disregarding the buffer length provided by the caller. Add a safe compression interface that checks for the end of buffer before each write. Use the safe interface in crypto/lzo. Signed-off-by: Herbert Xu <[email protected]> Reviewed-by: David Sterba <[email protected]> Signed-off-by: Herbert Xu <[email protected]> Signed-off-by: Sasha Levin <[email protected]> (cherry picked from commit 4b173bb2c4665c23f8fcf5241c7b06dfa6b5b111) Signed-off-by: Vijayendra Suman <[email protected]>
1 parent 963852c commit cc584b9

File tree

6 files changed

+106
-28
lines changed

6 files changed

+106
-28
lines changed

crypto/lzo-rle.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ static int __lzorle_compress(const u8 *src, unsigned int slen,
5555
size_t tmp_len = *dlen; /* size_t(ulong) <-> uint on 64 bit */
5656
int err;
5757

58-
err = lzorle1x_1_compress(src, slen, dst, &tmp_len, ctx);
58+
err = lzorle1x_1_compress_safe(src, slen, dst, &tmp_len, ctx);
5959

6060
if (err != LZO_E_OK)
6161
return -EINVAL;

crypto/lzo.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ static int __lzo_compress(const u8 *src, unsigned int slen,
5555
size_t tmp_len = *dlen; /* size_t(ulong) <-> uint on 64 bit */
5656
int err;
5757

58-
err = lzo1x_1_compress(src, slen, dst, &tmp_len, ctx);
58+
err = lzo1x_1_compress_safe(src, slen, dst, &tmp_len, ctx);
5959

6060
if (err != LZO_E_OK)
6161
return -EINVAL;

include/linux/lzo.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,18 @@
2424
int lzo1x_1_compress(const unsigned char *src, size_t src_len,
2525
unsigned char *dst, size_t *dst_len, void *wrkmem);
2626

27+
/* Same as above but does not write more than dst_len to dst. */
28+
int lzo1x_1_compress_safe(const unsigned char *src, size_t src_len,
29+
unsigned char *dst, size_t *dst_len, void *wrkmem);
30+
2731
/* This requires 'wrkmem' of size LZO1X_1_MEM_COMPRESS */
2832
int lzorle1x_1_compress(const unsigned char *src, size_t src_len,
2933
unsigned char *dst, size_t *dst_len, void *wrkmem);
3034

35+
/* Same as above but does not write more than dst_len to dst. */
36+
int lzorle1x_1_compress_safe(const unsigned char *src, size_t src_len,
37+
unsigned char *dst, size_t *dst_len, void *wrkmem);
38+
3139
/* safe decompression with overrun testing */
3240
int lzo1x_decompress_safe(const unsigned char *src, size_t src_len,
3341
unsigned char *dst, size_t *dst_len);

lib/lzo/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# SPDX-License-Identifier: GPL-2.0-only
2-
lzo_compress-objs := lzo1x_compress.o
2+
lzo_compress-objs := lzo1x_compress.o lzo1x_compress_safe.o
33
lzo_decompress-objs := lzo1x_decompress_safe.o
44

55
obj-$(CONFIG_LZO_COMPRESS) += lzo_compress.o

lib/lzo/lzo1x_compress.c

Lines changed: 77 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,32 @@
1818
#include <linux/lzo.h>
1919
#include "lzodefs.h"
2020

21-
static noinline size_t
22-
lzo1x_1_do_compress(const unsigned char *in, size_t in_len,
23-
unsigned char *out, size_t *out_len,
24-
size_t ti, void *wrkmem, signed char *state_offset,
25-
const unsigned char bitstream_version)
21+
#undef LZO_UNSAFE
22+
23+
#ifndef LZO_SAFE
24+
#define LZO_UNSAFE 1
25+
#define LZO_SAFE(name) name
26+
#define HAVE_OP(x) 1
27+
#endif
28+
29+
#define NEED_OP(x) if (!HAVE_OP(x)) goto output_overrun
30+
31+
static noinline int
32+
LZO_SAFE(lzo1x_1_do_compress)(const unsigned char *in, size_t in_len,
33+
unsigned char **out, unsigned char *op_end,
34+
size_t *tp, void *wrkmem,
35+
signed char *state_offset,
36+
const unsigned char bitstream_version)
2637
{
2738
const unsigned char *ip;
2839
unsigned char *op;
2940
const unsigned char * const in_end = in + in_len;
3041
const unsigned char * const ip_end = in + in_len - 20;
3142
const unsigned char *ii;
3243
lzo_dict_t * const dict = (lzo_dict_t *) wrkmem;
44+
size_t ti = *tp;
3345

34-
op = out;
46+
op = *out;
3547
ip = in;
3648
ii = ip;
3749
ip += ti < 4 ? 4 - ti : 0;
@@ -118,25 +130,32 @@ lzo1x_1_do_compress(const unsigned char *in, size_t in_len,
118130
if (t != 0) {
119131
if (t <= 3) {
120132
op[*state_offset] |= t;
133+
NEED_OP(4);
121134
COPY4(op, ii);
122135
op += t;
123136
} else if (t <= 16) {
137+
NEED_OP(17);
124138
*op++ = (t - 3);
125139
COPY8(op, ii);
126140
COPY8(op + 8, ii + 8);
127141
op += t;
128142
} else {
129143
if (t <= 18) {
144+
NEED_OP(1);
130145
*op++ = (t - 3);
131146
} else {
132147
size_t tt = t - 18;
148+
NEED_OP(1);
133149
*op++ = 0;
134150
while (unlikely(tt > 255)) {
135151
tt -= 255;
152+
NEED_OP(1);
136153
*op++ = 0;
137154
}
155+
NEED_OP(1);
138156
*op++ = tt;
139157
}
158+
NEED_OP(t);
140159
do {
141160
COPY8(op, ii);
142161
COPY8(op + 8, ii + 8);
@@ -153,6 +172,7 @@ lzo1x_1_do_compress(const unsigned char *in, size_t in_len,
153172
if (unlikely(run_length)) {
154173
ip += run_length;
155174
run_length -= MIN_ZERO_RUN_LENGTH;
175+
NEED_OP(4);
156176
put_unaligned_le32((run_length << 21) | 0xfffc18
157177
| (run_length & 0x7), op);
158178
op += 4;
@@ -245,25 +265,31 @@ lzo1x_1_do_compress(const unsigned char *in, size_t in_len,
245265
ip += m_len;
246266
if (m_len <= M2_MAX_LEN && m_off <= M2_MAX_OFFSET) {
247267
m_off -= 1;
268+
NEED_OP(2);
248269
*op++ = (((m_len - 1) << 5) | ((m_off & 7) << 2));
249270
*op++ = (m_off >> 3);
250271
} else if (m_off <= M3_MAX_OFFSET) {
251272
m_off -= 1;
273+
NEED_OP(1);
252274
if (m_len <= M3_MAX_LEN)
253275
*op++ = (M3_MARKER | (m_len - 2));
254276
else {
255277
m_len -= M3_MAX_LEN;
256278
*op++ = M3_MARKER | 0;
257279
while (unlikely(m_len > 255)) {
258280
m_len -= 255;
281+
NEED_OP(1);
259282
*op++ = 0;
260283
}
284+
NEED_OP(1);
261285
*op++ = (m_len);
262286
}
287+
NEED_OP(2);
263288
*op++ = (m_off << 2);
264289
*op++ = (m_off >> 6);
265290
} else {
266291
m_off -= 0x4000;
292+
NEED_OP(1);
267293
if (m_len <= M4_MAX_LEN)
268294
*op++ = (M4_MARKER | ((m_off >> 11) & 8)
269295
| (m_len - 2));
@@ -284,11 +310,14 @@ lzo1x_1_do_compress(const unsigned char *in, size_t in_len,
284310
m_len -= M4_MAX_LEN;
285311
*op++ = (M4_MARKER | ((m_off >> 11) & 8));
286312
while (unlikely(m_len > 255)) {
313+
NEED_OP(1);
287314
m_len -= 255;
288315
*op++ = 0;
289316
}
317+
NEED_OP(1);
290318
*op++ = (m_len);
291319
}
320+
NEED_OP(2);
292321
*op++ = (m_off << 2);
293322
*op++ = (m_off >> 6);
294323
}
@@ -297,14 +326,20 @@ lzo1x_1_do_compress(const unsigned char *in, size_t in_len,
297326
ii = ip;
298327
goto next;
299328
}
300-
*out_len = op - out;
301-
return in_end - (ii - ti);
329+
*out = op;
330+
*tp = in_end - (ii - ti);
331+
return LZO_E_OK;
332+
333+
output_overrun:
334+
return LZO_E_OUTPUT_OVERRUN;
302335
}
303336

304-
static int lzogeneric1x_1_compress(const unsigned char *in, size_t in_len,
305-
unsigned char *out, size_t *out_len,
306-
void *wrkmem, const unsigned char bitstream_version)
337+
static int LZO_SAFE(lzogeneric1x_1_compress)(
338+
const unsigned char *in, size_t in_len,
339+
unsigned char *out, size_t *out_len,
340+
void *wrkmem, const unsigned char bitstream_version)
307341
{
342+
unsigned char * const op_end = out + *out_len;
308343
const unsigned char *ip = in;
309344
unsigned char *op = out;
310345
unsigned char *data_start;
@@ -328,14 +363,18 @@ static int lzogeneric1x_1_compress(const unsigned char *in, size_t in_len,
328363
while (l > 20) {
329364
size_t ll = l <= (m4_max_offset + 1) ? l : (m4_max_offset + 1);
330365
uintptr_t ll_end = (uintptr_t) ip + ll;
366+
int err;
367+
331368
if ((ll_end + ((t + ll) >> 5)) <= ll_end)
332369
break;
333370
BUILD_BUG_ON(D_SIZE * sizeof(lzo_dict_t) > LZO1X_1_MEM_COMPRESS);
334371
memset(wrkmem, 0, D_SIZE * sizeof(lzo_dict_t));
335-
t = lzo1x_1_do_compress(ip, ll, op, out_len, t, wrkmem,
336-
&state_offset, bitstream_version);
372+
err = LZO_SAFE(lzo1x_1_do_compress)(
373+
ip, ll, &op, op_end, &t, wrkmem,
374+
&state_offset, bitstream_version);
375+
if (err != LZO_E_OK)
376+
return err;
337377
ip += ll;
338-
op += *out_len;
339378
l -= ll;
340379
}
341380
t += l;
@@ -344,20 +383,26 @@ static int lzogeneric1x_1_compress(const unsigned char *in, size_t in_len,
344383
const unsigned char *ii = in + in_len - t;
345384

346385
if (op == data_start && t <= 238) {
386+
NEED_OP(1);
347387
*op++ = (17 + t);
348388
} else if (t <= 3) {
349389
op[state_offset] |= t;
350390
} else if (t <= 18) {
391+
NEED_OP(1);
351392
*op++ = (t - 3);
352393
} else {
353394
size_t tt = t - 18;
395+
NEED_OP(1);
354396
*op++ = 0;
355397
while (tt > 255) {
356398
tt -= 255;
399+
NEED_OP(1);
357400
*op++ = 0;
358401
}
402+
NEED_OP(1);
359403
*op++ = tt;
360404
}
405+
NEED_OP(t);
361406
if (t >= 16) do {
362407
COPY8(op, ii);
363408
COPY8(op + 8, ii + 8);
@@ -370,31 +415,38 @@ static int lzogeneric1x_1_compress(const unsigned char *in, size_t in_len,
370415
} while (--t > 0);
371416
}
372417

418+
NEED_OP(3);
373419
*op++ = M4_MARKER | 1;
374420
*op++ = 0;
375421
*op++ = 0;
376422

377423
*out_len = op - out;
378424
return LZO_E_OK;
425+
426+
output_overrun:
427+
return LZO_E_OUTPUT_OVERRUN;
379428
}
380429

381-
int lzo1x_1_compress(const unsigned char *in, size_t in_len,
382-
unsigned char *out, size_t *out_len,
383-
void *wrkmem)
430+
int LZO_SAFE(lzo1x_1_compress)(const unsigned char *in, size_t in_len,
431+
unsigned char *out, size_t *out_len,
432+
void *wrkmem)
384433
{
385-
return lzogeneric1x_1_compress(in, in_len, out, out_len, wrkmem, 0);
434+
return LZO_SAFE(lzogeneric1x_1_compress)(
435+
in, in_len, out, out_len, wrkmem, 0);
386436
}
387437

388-
int lzorle1x_1_compress(const unsigned char *in, size_t in_len,
389-
unsigned char *out, size_t *out_len,
390-
void *wrkmem)
438+
int LZO_SAFE(lzorle1x_1_compress)(const unsigned char *in, size_t in_len,
439+
unsigned char *out, size_t *out_len,
440+
void *wrkmem)
391441
{
392-
return lzogeneric1x_1_compress(in, in_len, out, out_len,
393-
wrkmem, LZO_VERSION);
442+
return LZO_SAFE(lzogeneric1x_1_compress)(
443+
in, in_len, out, out_len, wrkmem, LZO_VERSION);
394444
}
395445

396-
EXPORT_SYMBOL_GPL(lzo1x_1_compress);
397-
EXPORT_SYMBOL_GPL(lzorle1x_1_compress);
446+
EXPORT_SYMBOL_GPL(LZO_SAFE(lzo1x_1_compress));
447+
EXPORT_SYMBOL_GPL(LZO_SAFE(lzorle1x_1_compress));
398448

449+
#ifndef LZO_UNSAFE
399450
MODULE_LICENSE("GPL");
400451
MODULE_DESCRIPTION("LZO1X-1 Compressor");
452+
#endif

lib/lzo/lzo1x_compress_safe.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/*
3+
* LZO1X Compressor from LZO
4+
*
5+
* Copyright (C) 1996-2012 Markus F.X.J. Oberhumer <[email protected]>
6+
*
7+
* The full LZO package can be found at:
8+
* http://www.oberhumer.com/opensource/lzo/
9+
*
10+
* Changed for Linux kernel use by:
11+
* Nitin Gupta <[email protected]>
12+
* Richard Purdie <[email protected]>
13+
*/
14+
15+
#define LZO_SAFE(name) name##_safe
16+
#define HAVE_OP(x) ((size_t)(op_end - op) >= (size_t)(x))
17+
18+
#include "lzo1x_compress.c"

0 commit comments

Comments
 (0)