mirror of
https://github.com/torvalds/linux.git
synced 2025-04-09 14:45:27 +00:00
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 <herbert@gondor.apana.org.au> Reviewed-by: David Sterba <dsterba@suse.com> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
This commit is contained in:
parent
ef2a68f815
commit
cc47f07234
@ -55,7 +55,7 @@ static int __lzorle_compress(const u8 *src, unsigned int slen,
|
||||
size_t tmp_len = *dlen; /* size_t(ulong) <-> uint on 64 bit */
|
||||
int err;
|
||||
|
||||
err = lzorle1x_1_compress(src, slen, dst, &tmp_len, ctx);
|
||||
err = lzorle1x_1_compress_safe(src, slen, dst, &tmp_len, ctx);
|
||||
|
||||
if (err != LZO_E_OK)
|
||||
return -EINVAL;
|
||||
|
@ -55,7 +55,7 @@ static int __lzo_compress(const u8 *src, unsigned int slen,
|
||||
size_t tmp_len = *dlen; /* size_t(ulong) <-> uint on 64 bit */
|
||||
int err;
|
||||
|
||||
err = lzo1x_1_compress(src, slen, dst, &tmp_len, ctx);
|
||||
err = lzo1x_1_compress_safe(src, slen, dst, &tmp_len, ctx);
|
||||
|
||||
if (err != LZO_E_OK)
|
||||
return -EINVAL;
|
||||
|
@ -24,10 +24,18 @@
|
||||
int lzo1x_1_compress(const unsigned char *src, size_t src_len,
|
||||
unsigned char *dst, size_t *dst_len, void *wrkmem);
|
||||
|
||||
/* Same as above but does not write more than dst_len to dst. */
|
||||
int lzo1x_1_compress_safe(const unsigned char *src, size_t src_len,
|
||||
unsigned char *dst, size_t *dst_len, void *wrkmem);
|
||||
|
||||
/* This requires 'wrkmem' of size LZO1X_1_MEM_COMPRESS */
|
||||
int lzorle1x_1_compress(const unsigned char *src, size_t src_len,
|
||||
unsigned char *dst, size_t *dst_len, void *wrkmem);
|
||||
|
||||
/* Same as above but does not write more than dst_len to dst. */
|
||||
int lzorle1x_1_compress_safe(const unsigned char *src, size_t src_len,
|
||||
unsigned char *dst, size_t *dst_len, void *wrkmem);
|
||||
|
||||
/* safe decompression with overrun testing */
|
||||
int lzo1x_decompress_safe(const unsigned char *src, size_t src_len,
|
||||
unsigned char *dst, size_t *dst_len);
|
||||
|
@ -1,5 +1,5 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
lzo_compress-objs := lzo1x_compress.o
|
||||
lzo_compress-objs := lzo1x_compress.o lzo1x_compress_safe.o
|
||||
lzo_decompress-objs := lzo1x_decompress_safe.o
|
||||
|
||||
obj-$(CONFIG_LZO_COMPRESS) += lzo_compress.o
|
||||
|
@ -18,11 +18,22 @@
|
||||
#include <linux/lzo.h>
|
||||
#include "lzodefs.h"
|
||||
|
||||
static noinline size_t
|
||||
lzo1x_1_do_compress(const unsigned char *in, size_t in_len,
|
||||
unsigned char *out, size_t *out_len,
|
||||
size_t ti, void *wrkmem, signed char *state_offset,
|
||||
const unsigned char bitstream_version)
|
||||
#undef LZO_UNSAFE
|
||||
|
||||
#ifndef LZO_SAFE
|
||||
#define LZO_UNSAFE 1
|
||||
#define LZO_SAFE(name) name
|
||||
#define HAVE_OP(x) 1
|
||||
#endif
|
||||
|
||||
#define NEED_OP(x) if (!HAVE_OP(x)) goto output_overrun
|
||||
|
||||
static noinline int
|
||||
LZO_SAFE(lzo1x_1_do_compress)(const unsigned char *in, size_t in_len,
|
||||
unsigned char **out, unsigned char *op_end,
|
||||
size_t *tp, void *wrkmem,
|
||||
signed char *state_offset,
|
||||
const unsigned char bitstream_version)
|
||||
{
|
||||
const unsigned char *ip;
|
||||
unsigned char *op;
|
||||
@ -30,8 +41,9 @@ lzo1x_1_do_compress(const unsigned char *in, size_t in_len,
|
||||
const unsigned char * const ip_end = in + in_len - 20;
|
||||
const unsigned char *ii;
|
||||
lzo_dict_t * const dict = (lzo_dict_t *) wrkmem;
|
||||
size_t ti = *tp;
|
||||
|
||||
op = out;
|
||||
op = *out;
|
||||
ip = in;
|
||||
ii = ip;
|
||||
ip += ti < 4 ? 4 - ti : 0;
|
||||
@ -116,25 +128,32 @@ next:
|
||||
if (t != 0) {
|
||||
if (t <= 3) {
|
||||
op[*state_offset] |= t;
|
||||
NEED_OP(4);
|
||||
COPY4(op, ii);
|
||||
op += t;
|
||||
} else if (t <= 16) {
|
||||
NEED_OP(17);
|
||||
*op++ = (t - 3);
|
||||
COPY8(op, ii);
|
||||
COPY8(op + 8, ii + 8);
|
||||
op += t;
|
||||
} else {
|
||||
if (t <= 18) {
|
||||
NEED_OP(1);
|
||||
*op++ = (t - 3);
|
||||
} else {
|
||||
size_t tt = t - 18;
|
||||
NEED_OP(1);
|
||||
*op++ = 0;
|
||||
while (unlikely(tt > 255)) {
|
||||
tt -= 255;
|
||||
NEED_OP(1);
|
||||
*op++ = 0;
|
||||
}
|
||||
NEED_OP(1);
|
||||
*op++ = tt;
|
||||
}
|
||||
NEED_OP(t);
|
||||
do {
|
||||
COPY8(op, ii);
|
||||
COPY8(op + 8, ii + 8);
|
||||
@ -151,6 +170,7 @@ next:
|
||||
if (unlikely(run_length)) {
|
||||
ip += run_length;
|
||||
run_length -= MIN_ZERO_RUN_LENGTH;
|
||||
NEED_OP(4);
|
||||
put_unaligned_le32((run_length << 21) | 0xfffc18
|
||||
| (run_length & 0x7), op);
|
||||
op += 4;
|
||||
@ -243,10 +263,12 @@ m_len_done:
|
||||
ip += m_len;
|
||||
if (m_len <= M2_MAX_LEN && m_off <= M2_MAX_OFFSET) {
|
||||
m_off -= 1;
|
||||
NEED_OP(2);
|
||||
*op++ = (((m_len - 1) << 5) | ((m_off & 7) << 2));
|
||||
*op++ = (m_off >> 3);
|
||||
} else if (m_off <= M3_MAX_OFFSET) {
|
||||
m_off -= 1;
|
||||
NEED_OP(1);
|
||||
if (m_len <= M3_MAX_LEN)
|
||||
*op++ = (M3_MARKER | (m_len - 2));
|
||||
else {
|
||||
@ -254,14 +276,18 @@ m_len_done:
|
||||
*op++ = M3_MARKER | 0;
|
||||
while (unlikely(m_len > 255)) {
|
||||
m_len -= 255;
|
||||
NEED_OP(1);
|
||||
*op++ = 0;
|
||||
}
|
||||
NEED_OP(1);
|
||||
*op++ = (m_len);
|
||||
}
|
||||
NEED_OP(2);
|
||||
*op++ = (m_off << 2);
|
||||
*op++ = (m_off >> 6);
|
||||
} else {
|
||||
m_off -= 0x4000;
|
||||
NEED_OP(1);
|
||||
if (m_len <= M4_MAX_LEN)
|
||||
*op++ = (M4_MARKER | ((m_off >> 11) & 8)
|
||||
| (m_len - 2));
|
||||
@ -282,11 +308,14 @@ m_len_done:
|
||||
m_len -= M4_MAX_LEN;
|
||||
*op++ = (M4_MARKER | ((m_off >> 11) & 8));
|
||||
while (unlikely(m_len > 255)) {
|
||||
NEED_OP(1);
|
||||
m_len -= 255;
|
||||
*op++ = 0;
|
||||
}
|
||||
NEED_OP(1);
|
||||
*op++ = (m_len);
|
||||
}
|
||||
NEED_OP(2);
|
||||
*op++ = (m_off << 2);
|
||||
*op++ = (m_off >> 6);
|
||||
}
|
||||
@ -295,14 +324,20 @@ finished_writing_instruction:
|
||||
ii = ip;
|
||||
goto next;
|
||||
}
|
||||
*out_len = op - out;
|
||||
return in_end - (ii - ti);
|
||||
*out = op;
|
||||
*tp = in_end - (ii - ti);
|
||||
return LZO_E_OK;
|
||||
|
||||
output_overrun:
|
||||
return LZO_E_OUTPUT_OVERRUN;
|
||||
}
|
||||
|
||||
static int lzogeneric1x_1_compress(const unsigned char *in, size_t in_len,
|
||||
unsigned char *out, size_t *out_len,
|
||||
void *wrkmem, const unsigned char bitstream_version)
|
||||
static int LZO_SAFE(lzogeneric1x_1_compress)(
|
||||
const unsigned char *in, size_t in_len,
|
||||
unsigned char *out, size_t *out_len,
|
||||
void *wrkmem, const unsigned char bitstream_version)
|
||||
{
|
||||
unsigned char * const op_end = out + *out_len;
|
||||
const unsigned char *ip = in;
|
||||
unsigned char *op = out;
|
||||
unsigned char *data_start;
|
||||
@ -326,14 +361,18 @@ static int lzogeneric1x_1_compress(const unsigned char *in, size_t in_len,
|
||||
while (l > 20) {
|
||||
size_t ll = min_t(size_t, l, m4_max_offset + 1);
|
||||
uintptr_t ll_end = (uintptr_t) ip + ll;
|
||||
int err;
|
||||
|
||||
if ((ll_end + ((t + ll) >> 5)) <= ll_end)
|
||||
break;
|
||||
BUILD_BUG_ON(D_SIZE * sizeof(lzo_dict_t) > LZO1X_1_MEM_COMPRESS);
|
||||
memset(wrkmem, 0, D_SIZE * sizeof(lzo_dict_t));
|
||||
t = lzo1x_1_do_compress(ip, ll, op, out_len, t, wrkmem,
|
||||
&state_offset, bitstream_version);
|
||||
err = LZO_SAFE(lzo1x_1_do_compress)(
|
||||
ip, ll, &op, op_end, &t, wrkmem,
|
||||
&state_offset, bitstream_version);
|
||||
if (err != LZO_E_OK)
|
||||
return err;
|
||||
ip += ll;
|
||||
op += *out_len;
|
||||
l -= ll;
|
||||
}
|
||||
t += l;
|
||||
@ -342,20 +381,26 @@ static int lzogeneric1x_1_compress(const unsigned char *in, size_t in_len,
|
||||
const unsigned char *ii = in + in_len - t;
|
||||
|
||||
if (op == data_start && t <= 238) {
|
||||
NEED_OP(1);
|
||||
*op++ = (17 + t);
|
||||
} else if (t <= 3) {
|
||||
op[state_offset] |= t;
|
||||
} else if (t <= 18) {
|
||||
NEED_OP(1);
|
||||
*op++ = (t - 3);
|
||||
} else {
|
||||
size_t tt = t - 18;
|
||||
NEED_OP(1);
|
||||
*op++ = 0;
|
||||
while (tt > 255) {
|
||||
tt -= 255;
|
||||
NEED_OP(1);
|
||||
*op++ = 0;
|
||||
}
|
||||
NEED_OP(1);
|
||||
*op++ = tt;
|
||||
}
|
||||
NEED_OP(t);
|
||||
if (t >= 16) do {
|
||||
COPY8(op, ii);
|
||||
COPY8(op + 8, ii + 8);
|
||||
@ -368,31 +413,38 @@ static int lzogeneric1x_1_compress(const unsigned char *in, size_t in_len,
|
||||
} while (--t > 0);
|
||||
}
|
||||
|
||||
NEED_OP(3);
|
||||
*op++ = M4_MARKER | 1;
|
||||
*op++ = 0;
|
||||
*op++ = 0;
|
||||
|
||||
*out_len = op - out;
|
||||
return LZO_E_OK;
|
||||
|
||||
output_overrun:
|
||||
return LZO_E_OUTPUT_OVERRUN;
|
||||
}
|
||||
|
||||
int lzo1x_1_compress(const unsigned char *in, size_t in_len,
|
||||
unsigned char *out, size_t *out_len,
|
||||
void *wrkmem)
|
||||
int LZO_SAFE(lzo1x_1_compress)(const unsigned char *in, size_t in_len,
|
||||
unsigned char *out, size_t *out_len,
|
||||
void *wrkmem)
|
||||
{
|
||||
return lzogeneric1x_1_compress(in, in_len, out, out_len, wrkmem, 0);
|
||||
return LZO_SAFE(lzogeneric1x_1_compress)(
|
||||
in, in_len, out, out_len, wrkmem, 0);
|
||||
}
|
||||
|
||||
int lzorle1x_1_compress(const unsigned char *in, size_t in_len,
|
||||
unsigned char *out, size_t *out_len,
|
||||
void *wrkmem)
|
||||
int LZO_SAFE(lzorle1x_1_compress)(const unsigned char *in, size_t in_len,
|
||||
unsigned char *out, size_t *out_len,
|
||||
void *wrkmem)
|
||||
{
|
||||
return lzogeneric1x_1_compress(in, in_len, out, out_len,
|
||||
wrkmem, LZO_VERSION);
|
||||
return LZO_SAFE(lzogeneric1x_1_compress)(
|
||||
in, in_len, out, out_len, wrkmem, LZO_VERSION);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(lzo1x_1_compress);
|
||||
EXPORT_SYMBOL_GPL(lzorle1x_1_compress);
|
||||
EXPORT_SYMBOL_GPL(LZO_SAFE(lzo1x_1_compress));
|
||||
EXPORT_SYMBOL_GPL(LZO_SAFE(lzorle1x_1_compress));
|
||||
|
||||
#ifndef LZO_UNSAFE
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("LZO1X-1 Compressor");
|
||||
#endif
|
||||
|
18
lib/lzo/lzo1x_compress_safe.c
Normal file
18
lib/lzo/lzo1x_compress_safe.c
Normal file
@ -0,0 +1,18 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* LZO1X Compressor from LZO
|
||||
*
|
||||
* Copyright (C) 1996-2012 Markus F.X.J. Oberhumer <markus@oberhumer.com>
|
||||
*
|
||||
* The full LZO package can be found at:
|
||||
* http://www.oberhumer.com/opensource/lzo/
|
||||
*
|
||||
* Changed for Linux kernel use by:
|
||||
* Nitin Gupta <nitingupta910@gmail.com>
|
||||
* Richard Purdie <rpurdie@openedhand.com>
|
||||
*/
|
||||
|
||||
#define LZO_SAFE(name) name##_safe
|
||||
#define HAVE_OP(x) ((size_t)(op_end - op) >= (size_t)(x))
|
||||
|
||||
#include "lzo1x_compress.c"
|
Loading…
x
Reference in New Issue
Block a user