Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

+106 -28
+1 -1
crypto/lzo-rle.c
··· 55 55 size_t tmp_len = *dlen; /* size_t(ulong) <-> uint on 64 bit */ 56 56 int err; 57 57 58 - err = lzorle1x_1_compress(src, slen, dst, &tmp_len, ctx); 58 + err = lzorle1x_1_compress_safe(src, slen, dst, &tmp_len, ctx); 59 59 60 60 if (err != LZO_E_OK) 61 61 return -EINVAL;
+1 -1
crypto/lzo.c
··· 55 55 size_t tmp_len = *dlen; /* size_t(ulong) <-> uint on 64 bit */ 56 56 int err; 57 57 58 - err = lzo1x_1_compress(src, slen, dst, &tmp_len, ctx); 58 + err = lzo1x_1_compress_safe(src, slen, dst, &tmp_len, ctx); 59 59 60 60 if (err != LZO_E_OK) 61 61 return -EINVAL;
+8
include/linux/lzo.h
··· 24 24 int lzo1x_1_compress(const unsigned char *src, size_t src_len, 25 25 unsigned char *dst, size_t *dst_len, void *wrkmem); 26 26 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 + 27 31 /* This requires 'wrkmem' of size LZO1X_1_MEM_COMPRESS */ 28 32 int lzorle1x_1_compress(const unsigned char *src, size_t src_len, 29 33 unsigned char *dst, size_t *dst_len, void *wrkmem); 34 + 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); 30 38 31 39 /* safe decompression with overrun testing */ 32 40 int lzo1x_decompress_safe(const unsigned char *src, size_t src_len,
+1 -1
lib/lzo/Makefile
··· 1 1 # 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 3 3 lzo_decompress-objs := lzo1x_decompress_safe.o 4 4 5 5 obj-$(CONFIG_LZO_COMPRESS) += lzo_compress.o
+77 -25
lib/lzo/lzo1x_compress.c
··· 18 18 #include <linux/lzo.h> 19 19 #include "lzodefs.h" 20 20 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) 26 37 { 27 38 const unsigned char *ip; 28 39 unsigned char *op; ··· 41 30 const unsigned char * const ip_end = in + in_len - 20; 42 31 const unsigned char *ii; 43 32 lzo_dict_t * const dict = (lzo_dict_t *) wrkmem; 33 + size_t ti = *tp; 44 34 45 - op = out; 35 + op = *out; 46 36 ip = in; 47 37 ii = ip; 48 38 ip += ti < 4 ? 4 - ti : 0; ··· 128 116 if (t != 0) { 129 117 if (t <= 3) { 130 118 op[*state_offset] |= t; 119 + NEED_OP(4); 131 120 COPY4(op, ii); 132 121 op += t; 133 122 } else if (t <= 16) { 123 + NEED_OP(17); 134 124 *op++ = (t - 3); 135 125 COPY8(op, ii); 136 126 COPY8(op + 8, ii + 8); 137 127 op += t; 138 128 } else { 139 129 if (t <= 18) { 130 + NEED_OP(1); 140 131 *op++ = (t - 3); 141 132 } else { 142 133 size_t tt = t - 18; 134 + NEED_OP(1); 143 135 *op++ = 0; 144 136 while (unlikely(tt > 255)) { 145 137 tt -= 255; 138 + NEED_OP(1); 146 139 *op++ = 0; 147 140 } 141 + NEED_OP(1); 148 142 *op++ = tt; 149 143 } 144 + NEED_OP(t); 150 145 do { 151 146 COPY8(op, ii); 152 147 COPY8(op + 8, ii + 8); ··· 170 151 if (unlikely(run_length)) { 171 152 ip += run_length; 172 153 run_length -= MIN_ZERO_RUN_LENGTH; 154 + NEED_OP(4); 173 155 put_unaligned_le32((run_length << 21) | 0xfffc18 174 156 | (run_length & 0x7), op); 175 157 op += 4; ··· 263 243 ip += m_len; 264 244 if (m_len <= M2_MAX_LEN && m_off <= M2_MAX_OFFSET) { 265 245 m_off -= 1; 246 + NEED_OP(2); 266 247 *op++ = (((m_len - 1) << 5) | ((m_off & 7) << 2)); 267 248 *op++ = (m_off >> 3); 268 249 } else if (m_off <= M3_MAX_OFFSET) { 269 250 m_off -= 1; 251 + NEED_OP(1); 270 252 if (m_len <= M3_MAX_LEN) 271 253 *op++ = (M3_MARKER | (m_len - 2)); 272 254 else { ··· 276 254 *op++ = M3_MARKER | 0; 277 255 while (unlikely(m_len > 255)) { 278 256 m_len -= 255; 257 + NEED_OP(1); 279 258 *op++ = 0; 280 259 } 260 + NEED_OP(1); 281 261 *op++ = (m_len); 282 262 } 263 + NEED_OP(2); 283 264 *op++ = (m_off << 2); 284 265 *op++ = (m_off >> 6); 285 266 } else { 286 267 m_off -= 0x4000; 268 + NEED_OP(1); 287 269 if (m_len <= M4_MAX_LEN) 288 270 *op++ = (M4_MARKER | ((m_off >> 11) & 8) 289 271 | (m_len - 2)); ··· 308 282 m_len -= M4_MAX_LEN; 309 283 *op++ = (M4_MARKER | ((m_off >> 11) & 8)); 310 284 while (unlikely(m_len > 255)) { 285 + NEED_OP(1); 311 286 m_len -= 255; 312 287 *op++ = 0; 313 288 } 289 + NEED_OP(1); 314 290 *op++ = (m_len); 315 291 } 292 + NEED_OP(2); 316 293 *op++ = (m_off << 2); 317 294 *op++ = (m_off >> 6); 318 295 } ··· 324 295 ii = ip; 325 296 goto next; 326 297 } 327 - *out_len = op - out; 328 - return in_end - (ii - ti); 298 + *out = op; 299 + *tp = in_end - (ii - ti); 300 + return LZO_E_OK; 301 + 302 + output_overrun: 303 + return LZO_E_OUTPUT_OVERRUN; 329 304 } 330 305 331 - static int lzogeneric1x_1_compress(const unsigned char *in, size_t in_len, 332 - unsigned char *out, size_t *out_len, 333 - void *wrkmem, const unsigned char bitstream_version) 306 + static int LZO_SAFE(lzogeneric1x_1_compress)( 307 + const unsigned char *in, size_t in_len, 308 + unsigned char *out, size_t *out_len, 309 + void *wrkmem, const unsigned char bitstream_version) 334 310 { 311 + unsigned char * const op_end = out + *out_len; 335 312 const unsigned char *ip = in; 336 313 unsigned char *op = out; 337 314 unsigned char *data_start; ··· 361 326 while (l > 20) { 362 327 size_t ll = min_t(size_t, l, m4_max_offset + 1); 363 328 uintptr_t ll_end = (uintptr_t) ip + ll; 329 + int err; 330 + 364 331 if ((ll_end + ((t + ll) >> 5)) <= ll_end) 365 332 break; 366 333 BUILD_BUG_ON(D_SIZE * sizeof(lzo_dict_t) > LZO1X_1_MEM_COMPRESS); 367 334 memset(wrkmem, 0, D_SIZE * sizeof(lzo_dict_t)); 368 - t = lzo1x_1_do_compress(ip, ll, op, out_len, t, wrkmem, 369 - &state_offset, bitstream_version); 335 + err = LZO_SAFE(lzo1x_1_do_compress)( 336 + ip, ll, &op, op_end, &t, wrkmem, 337 + &state_offset, bitstream_version); 338 + if (err != LZO_E_OK) 339 + return err; 370 340 ip += ll; 371 - op += *out_len; 372 341 l -= ll; 373 342 } 374 343 t += l; ··· 381 342 const unsigned char *ii = in + in_len - t; 382 343 383 344 if (op == data_start && t <= 238) { 345 + NEED_OP(1); 384 346 *op++ = (17 + t); 385 347 } else if (t <= 3) { 386 348 op[state_offset] |= t; 387 349 } else if (t <= 18) { 350 + NEED_OP(1); 388 351 *op++ = (t - 3); 389 352 } else { 390 353 size_t tt = t - 18; 354 + NEED_OP(1); 391 355 *op++ = 0; 392 356 while (tt > 255) { 393 357 tt -= 255; 358 + NEED_OP(1); 394 359 *op++ = 0; 395 360 } 361 + NEED_OP(1); 396 362 *op++ = tt; 397 363 } 364 + NEED_OP(t); 398 365 if (t >= 16) do { 399 366 COPY8(op, ii); 400 367 COPY8(op + 8, ii + 8); ··· 413 368 } while (--t > 0); 414 369 } 415 370 371 + NEED_OP(3); 416 372 *op++ = M4_MARKER | 1; 417 373 *op++ = 0; 418 374 *op++ = 0; 419 375 420 376 *out_len = op - out; 421 377 return LZO_E_OK; 378 + 379 + output_overrun: 380 + return LZO_E_OUTPUT_OVERRUN; 422 381 } 423 382 424 - int lzo1x_1_compress(const unsigned char *in, size_t in_len, 425 - unsigned char *out, size_t *out_len, 426 - void *wrkmem) 383 + int LZO_SAFE(lzo1x_1_compress)(const unsigned char *in, size_t in_len, 384 + unsigned char *out, size_t *out_len, 385 + void *wrkmem) 427 386 { 428 - return lzogeneric1x_1_compress(in, in_len, out, out_len, wrkmem, 0); 387 + return LZO_SAFE(lzogeneric1x_1_compress)( 388 + in, in_len, out, out_len, wrkmem, 0); 429 389 } 430 390 431 - int lzorle1x_1_compress(const unsigned char *in, size_t in_len, 432 - unsigned char *out, size_t *out_len, 433 - void *wrkmem) 391 + int LZO_SAFE(lzorle1x_1_compress)(const unsigned char *in, size_t in_len, 392 + unsigned char *out, size_t *out_len, 393 + void *wrkmem) 434 394 { 435 - return lzogeneric1x_1_compress(in, in_len, out, out_len, 436 - wrkmem, LZO_VERSION); 395 + return LZO_SAFE(lzogeneric1x_1_compress)( 396 + in, in_len, out, out_len, wrkmem, LZO_VERSION); 437 397 } 438 398 439 - EXPORT_SYMBOL_GPL(lzo1x_1_compress); 440 - EXPORT_SYMBOL_GPL(lzorle1x_1_compress); 399 + EXPORT_SYMBOL_GPL(LZO_SAFE(lzo1x_1_compress)); 400 + EXPORT_SYMBOL_GPL(LZO_SAFE(lzorle1x_1_compress)); 441 401 402 + #ifndef LZO_UNSAFE 442 403 MODULE_LICENSE("GPL"); 443 404 MODULE_DESCRIPTION("LZO1X-1 Compressor"); 405 + #endif
+18
lib/lzo/lzo1x_compress_safe.c
··· 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 <markus@oberhumer.com> 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 <nitingupta910@gmail.com> 12 + * Richard Purdie <rpurdie@openedhand.com> 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"