Skip to content

Commit cc47f07

Browse files
committed
crypto: lzo - Fix compression buffer overrun
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]>
1 parent ef2a68f commit cc47f07

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;
@@ -116,25 +128,32 @@ lzo1x_1_do_compress(const unsigned char *in, size_t in_len,
116128
if (t != 0) {
117129
if (t <= 3) {
118130
op[*state_offset] |= t;
131+
NEED_OP(4);
119132
COPY4(op, ii);
120133
op += t;
121134
} else if (t <= 16) {
135+
NEED_OP(17);
122136
*op++ = (t - 3);
123137
COPY8(op, ii);
124138
COPY8(op + 8, ii + 8);
125139
op += t;
126140
} else {
127141
if (t <= 18) {
142+
NEED_OP(1);
128143
*op++ = (t - 3);
129144
} else {
130145
size_t tt = t - 18;
146+
NEED_OP(1);
131147
*op++ = 0;
132148
while (unlikely(tt > 255)) {
133149
tt -= 255;
150+
NEED_OP(1);
134151
*op++ = 0;
135152
}
153+
NEED_OP(1);
136154
*op++ = tt;
137155
}
156+
NEED_OP(t);
138157
do {
139158
COPY8(op, ii);
140159
COPY8(op + 8, ii + 8);
@@ -151,6 +170,7 @@ lzo1x_1_do_compress(const unsigned char *in, size_t in_len,
151170
if (unlikely(run_length)) {
152171
ip += run_length;
153172
run_length -= MIN_ZERO_RUN_LENGTH;
173+
NEED_OP(4);
154174
put_unaligned_le32((run_length << 21) | 0xfffc18
155175
| (run_length & 0x7), op);
156176
op += 4;
@@ -243,25 +263,31 @@ lzo1x_1_do_compress(const unsigned char *in, size_t in_len,
243263
ip += m_len;
244264
if (m_len <= M2_MAX_LEN && m_off <= M2_MAX_OFFSET) {
245265
m_off -= 1;
266+
NEED_OP(2);
246267
*op++ = (((m_len - 1) << 5) | ((m_off & 7) << 2));
247268
*op++ = (m_off >> 3);
248269
} else if (m_off <= M3_MAX_OFFSET) {
249270
m_off -= 1;
271+
NEED_OP(1);
250272
if (m_len <= M3_MAX_LEN)
251273
*op++ = (M3_MARKER | (m_len - 2));
252274
else {
253275
m_len -= M3_MAX_LEN;
254276
*op++ = M3_MARKER | 0;
255277
while (unlikely(m_len > 255)) {
256278
m_len -= 255;
279+
NEED_OP(1);
257280
*op++ = 0;
258281
}
282+
NEED_OP(1);
259283
*op++ = (m_len);
260284
}
285+
NEED_OP(2);
261286
*op++ = (m_off << 2);
262287
*op++ = (m_off >> 6);
263288
} else {
264289
m_off -= 0x4000;
290+
NEED_OP(1);
265291
if (m_len <= M4_MAX_LEN)
266292
*op++ = (M4_MARKER | ((m_off >> 11) & 8)
267293
| (m_len - 2));
@@ -282,11 +308,14 @@ lzo1x_1_do_compress(const unsigned char *in, size_t in_len,
282308
m_len -= M4_MAX_LEN;
283309
*op++ = (M4_MARKER | ((m_off >> 11) & 8));
284310
while (unlikely(m_len > 255)) {
311+
NEED_OP(1);
285312
m_len -= 255;
286313
*op++ = 0;
287314
}
315+
NEED_OP(1);
288316
*op++ = (m_len);
289317
}
318+
NEED_OP(2);
290319
*op++ = (m_off << 2);
291320
*op++ = (m_off >> 6);
292321
}
@@ -295,14 +324,20 @@ lzo1x_1_do_compress(const unsigned char *in, size_t in_len,
295324
ii = ip;
296325
goto next;
297326
}
298-
*out_len = op - out;
299-
return in_end - (ii - ti);
327+
*out = op;
328+
*tp = in_end - (ii - ti);
329+
return LZO_E_OK;
330+
331+
output_overrun:
332+
return LZO_E_OUTPUT_OVERRUN;
300333
}
301334

302-
static int lzogeneric1x_1_compress(const unsigned char *in, size_t in_len,
303-
unsigned char *out, size_t *out_len,
304-
void *wrkmem, const unsigned char bitstream_version)
335+
static int LZO_SAFE(lzogeneric1x_1_compress)(
336+
const unsigned char *in, size_t in_len,
337+
unsigned char *out, size_t *out_len,
338+
void *wrkmem, const unsigned char bitstream_version)
305339
{
340+
unsigned char * const op_end = out + *out_len;
306341
const unsigned char *ip = in;
307342
unsigned char *op = out;
308343
unsigned char *data_start;
@@ -326,14 +361,18 @@ static int lzogeneric1x_1_compress(const unsigned char *in, size_t in_len,
326361
while (l > 20) {
327362
size_t ll = min_t(size_t, l, m4_max_offset + 1);
328363
uintptr_t ll_end = (uintptr_t) ip + ll;
364+
int err;
365+
329366
if ((ll_end + ((t + ll) >> 5)) <= ll_end)
330367
break;
331368
BUILD_BUG_ON(D_SIZE * sizeof(lzo_dict_t) > LZO1X_1_MEM_COMPRESS);
332369
memset(wrkmem, 0, D_SIZE * sizeof(lzo_dict_t));
333-
t = lzo1x_1_do_compress(ip, ll, op, out_len, t, wrkmem,
334-
&state_offset, bitstream_version);
370+
err = LZO_SAFE(lzo1x_1_do_compress)(
371+
ip, ll, &op, op_end, &t, wrkmem,
372+
&state_offset, bitstream_version);
373+
if (err != LZO_E_OK)
374+
return err;
335375
ip += ll;
336-
op += *out_len;
337376
l -= ll;
338377
}
339378
t += l;
@@ -342,20 +381,26 @@ static int lzogeneric1x_1_compress(const unsigned char *in, size_t in_len,
342381
const unsigned char *ii = in + in_len - t;
343382

344383
if (op == data_start && t <= 238) {
384+
NEED_OP(1);
345385
*op++ = (17 + t);
346386
} else if (t <= 3) {
347387
op[state_offset] |= t;
348388
} else if (t <= 18) {
389+
NEED_OP(1);
349390
*op++ = (t - 3);
350391
} else {
351392
size_t tt = t - 18;
393+
NEED_OP(1);
352394
*op++ = 0;
353395
while (tt > 255) {
354396
tt -= 255;
397+
NEED_OP(1);
355398
*op++ = 0;
356399
}
400+
NEED_OP(1);
357401
*op++ = tt;
358402
}
403+
NEED_OP(t);
359404
if (t >= 16) do {
360405
COPY8(op, ii);
361406
COPY8(op + 8, ii + 8);
@@ -368,31 +413,38 @@ static int lzogeneric1x_1_compress(const unsigned char *in, size_t in_len,
368413
} while (--t > 0);
369414
}
370415

416+
NEED_OP(3);
371417
*op++ = M4_MARKER | 1;
372418
*op++ = 0;
373419
*op++ = 0;
374420

375421
*out_len = op - out;
376422
return LZO_E_OK;
423+
424+
output_overrun:
425+
return LZO_E_OUTPUT_OVERRUN;
377426
}
378427

379-
int lzo1x_1_compress(const unsigned char *in, size_t in_len,
380-
unsigned char *out, size_t *out_len,
381-
void *wrkmem)
428+
int LZO_SAFE(lzo1x_1_compress)(const unsigned char *in, size_t in_len,
429+
unsigned char *out, size_t *out_len,
430+
void *wrkmem)
382431
{
383-
return lzogeneric1x_1_compress(in, in_len, out, out_len, wrkmem, 0);
432+
return LZO_SAFE(lzogeneric1x_1_compress)(
433+
in, in_len, out, out_len, wrkmem, 0);
384434
}
385435

386-
int lzorle1x_1_compress(const unsigned char *in, size_t in_len,
387-
unsigned char *out, size_t *out_len,
388-
void *wrkmem)
436+
int LZO_SAFE(lzorle1x_1_compress)(const unsigned char *in, size_t in_len,
437+
unsigned char *out, size_t *out_len,
438+
void *wrkmem)
389439
{
390-
return lzogeneric1x_1_compress(in, in_len, out, out_len,
391-
wrkmem, LZO_VERSION);
440+
return LZO_SAFE(lzogeneric1x_1_compress)(
441+
in, in_len, out, out_len, wrkmem, LZO_VERSION);
392442
}
393443

394-
EXPORT_SYMBOL_GPL(lzo1x_1_compress);
395-
EXPORT_SYMBOL_GPL(lzorle1x_1_compress);
444+
EXPORT_SYMBOL_GPL(LZO_SAFE(lzo1x_1_compress));
445+
EXPORT_SYMBOL_GPL(LZO_SAFE(lzorle1x_1_compress));
396446

447+
#ifndef LZO_UNSAFE
397448
MODULE_LICENSE("GPL");
398449
MODULE_DESCRIPTION("LZO1X-1 Compressor");
450+
#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)