Commit afca294289949b118a192b77be947379734ea620
Committed by
Simon Glass
1 parent
b75650d84d
Exists in
master
and in
53 other branches
lzma: correctly bounds-check output buffer
The output buffer size must be correctly passed to the lzma decoder or there is a risk of overflowing memory during decompression. Switching to the LZMA_FINISH_END mode means nothing is left in an unknown state once the buffer becomes full. Signed-off-by: Kees Cook <keescook@chromium.org> Acked-by: Simon Glass <sjg@chromium.org>
Showing 1 changed file with 6 additions and 2 deletions Inline Diff
lib/lzma/LzmaTools.c
1 | /* | 1 | /* |
2 | * Usefuls routines based on the LzmaTest.c file from LZMA SDK 4.65 | 2 | * Usefuls routines based on the LzmaTest.c file from LZMA SDK 4.65 |
3 | * | 3 | * |
4 | * Copyright (C) 2007-2009 Industrie Dial Face S.p.A. | 4 | * Copyright (C) 2007-2009 Industrie Dial Face S.p.A. |
5 | * Luigi 'Comio' Mantellini (luigi.mantellini@idf-hit.com) | 5 | * Luigi 'Comio' Mantellini (luigi.mantellini@idf-hit.com) |
6 | * | 6 | * |
7 | * Copyright (C) 1999-2005 Igor Pavlov | 7 | * Copyright (C) 1999-2005 Igor Pavlov |
8 | * | 8 | * |
9 | * SPDX-License-Identifier: GPL-2.0+ | 9 | * SPDX-License-Identifier: GPL-2.0+ |
10 | */ | 10 | */ |
11 | 11 | ||
12 | /* | 12 | /* |
13 | * LZMA_Alone stream format: | 13 | * LZMA_Alone stream format: |
14 | * | 14 | * |
15 | * uchar Properties[5] | 15 | * uchar Properties[5] |
16 | * uint64 Uncompressed size | 16 | * uint64 Uncompressed size |
17 | * uchar data[*] | 17 | * uchar data[*] |
18 | * | 18 | * |
19 | */ | 19 | */ |
20 | 20 | ||
21 | #include <config.h> | 21 | #include <config.h> |
22 | #include <common.h> | 22 | #include <common.h> |
23 | #include <watchdog.h> | 23 | #include <watchdog.h> |
24 | 24 | ||
25 | #ifdef CONFIG_LZMA | 25 | #ifdef CONFIG_LZMA |
26 | 26 | ||
27 | #define LZMA_PROPERTIES_OFFSET 0 | 27 | #define LZMA_PROPERTIES_OFFSET 0 |
28 | #define LZMA_SIZE_OFFSET LZMA_PROPS_SIZE | 28 | #define LZMA_SIZE_OFFSET LZMA_PROPS_SIZE |
29 | #define LZMA_DATA_OFFSET LZMA_SIZE_OFFSET+sizeof(uint64_t) | 29 | #define LZMA_DATA_OFFSET LZMA_SIZE_OFFSET+sizeof(uint64_t) |
30 | 30 | ||
31 | #include "LzmaTools.h" | 31 | #include "LzmaTools.h" |
32 | #include "LzmaDec.h" | 32 | #include "LzmaDec.h" |
33 | 33 | ||
34 | #include <linux/string.h> | 34 | #include <linux/string.h> |
35 | #include <malloc.h> | 35 | #include <malloc.h> |
36 | 36 | ||
37 | static void *SzAlloc(void *p, size_t size) { p = p; return malloc(size); } | 37 | static void *SzAlloc(void *p, size_t size) { p = p; return malloc(size); } |
38 | static void SzFree(void *p, void *address) { p = p; free(address); } | 38 | static void SzFree(void *p, void *address) { p = p; free(address); } |
39 | 39 | ||
40 | int lzmaBuffToBuffDecompress (unsigned char *outStream, SizeT *uncompressedSize, | 40 | int lzmaBuffToBuffDecompress (unsigned char *outStream, SizeT *uncompressedSize, |
41 | unsigned char *inStream, SizeT length) | 41 | unsigned char *inStream, SizeT length) |
42 | { | 42 | { |
43 | int res = SZ_ERROR_DATA; | 43 | int res = SZ_ERROR_DATA; |
44 | int i; | 44 | int i; |
45 | ISzAlloc g_Alloc; | 45 | ISzAlloc g_Alloc; |
46 | 46 | ||
47 | SizeT outSizeFull = 0xFFFFFFFF; /* 4GBytes limit */ | 47 | SizeT outSizeFull = 0xFFFFFFFF; /* 4GBytes limit */ |
48 | SizeT outProcessed; | 48 | SizeT outProcessed; |
49 | SizeT outSize; | 49 | SizeT outSize; |
50 | SizeT outSizeHigh; | 50 | SizeT outSizeHigh; |
51 | ELzmaStatus state; | 51 | ELzmaStatus state; |
52 | SizeT compressedSize = (SizeT)(length - LZMA_PROPS_SIZE); | 52 | SizeT compressedSize = (SizeT)(length - LZMA_PROPS_SIZE); |
53 | 53 | ||
54 | debug ("LZMA: Image address............... 0x%p\n", inStream); | 54 | debug ("LZMA: Image address............... 0x%p\n", inStream); |
55 | debug ("LZMA: Properties address.......... 0x%p\n", inStream + LZMA_PROPERTIES_OFFSET); | 55 | debug ("LZMA: Properties address.......... 0x%p\n", inStream + LZMA_PROPERTIES_OFFSET); |
56 | debug ("LZMA: Uncompressed size address... 0x%p\n", inStream + LZMA_SIZE_OFFSET); | 56 | debug ("LZMA: Uncompressed size address... 0x%p\n", inStream + LZMA_SIZE_OFFSET); |
57 | debug ("LZMA: Compressed data address..... 0x%p\n", inStream + LZMA_DATA_OFFSET); | 57 | debug ("LZMA: Compressed data address..... 0x%p\n", inStream + LZMA_DATA_OFFSET); |
58 | debug ("LZMA: Destination address......... 0x%p\n", outStream); | 58 | debug ("LZMA: Destination address......... 0x%p\n", outStream); |
59 | 59 | ||
60 | memset(&state, 0, sizeof(state)); | 60 | memset(&state, 0, sizeof(state)); |
61 | 61 | ||
62 | outSize = 0; | 62 | outSize = 0; |
63 | outSizeHigh = 0; | 63 | outSizeHigh = 0; |
64 | /* Read the uncompressed size */ | 64 | /* Read the uncompressed size */ |
65 | for (i = 0; i < 8; i++) { | 65 | for (i = 0; i < 8; i++) { |
66 | unsigned char b = inStream[LZMA_SIZE_OFFSET + i]; | 66 | unsigned char b = inStream[LZMA_SIZE_OFFSET + i]; |
67 | if (i < 4) { | 67 | if (i < 4) { |
68 | outSize += (UInt32)(b) << (i * 8); | 68 | outSize += (UInt32)(b) << (i * 8); |
69 | } else { | 69 | } else { |
70 | outSizeHigh += (UInt32)(b) << ((i - 4) * 8); | 70 | outSizeHigh += (UInt32)(b) << ((i - 4) * 8); |
71 | } | 71 | } |
72 | } | 72 | } |
73 | 73 | ||
74 | outSizeFull = (SizeT)outSize; | 74 | outSizeFull = (SizeT)outSize; |
75 | if (sizeof(SizeT) >= 8) { | 75 | if (sizeof(SizeT) >= 8) { |
76 | /* | 76 | /* |
77 | * SizeT is a 64 bit uint => We can manage files larger than 4GB! | 77 | * SizeT is a 64 bit uint => We can manage files larger than 4GB! |
78 | * | 78 | * |
79 | */ | 79 | */ |
80 | outSizeFull |= (((SizeT)outSizeHigh << 16) << 16); | 80 | outSizeFull |= (((SizeT)outSizeHigh << 16) << 16); |
81 | } else if (outSizeHigh != 0 || (UInt32)(SizeT)outSize != outSize) { | 81 | } else if (outSizeHigh != 0 || (UInt32)(SizeT)outSize != outSize) { |
82 | /* | 82 | /* |
83 | * SizeT is a 32 bit uint => We cannot manage files larger than | 83 | * SizeT is a 32 bit uint => We cannot manage files larger than |
84 | * 4GB! Assume however that all 0xf values is "unknown size" and | 84 | * 4GB! Assume however that all 0xf values is "unknown size" and |
85 | * not actually a file of 2^64 bits. | 85 | * not actually a file of 2^64 bits. |
86 | * | 86 | * |
87 | */ | 87 | */ |
88 | if (outSizeHigh != (SizeT)-1 || outSize != (SizeT)-1) { | 88 | if (outSizeHigh != (SizeT)-1 || outSize != (SizeT)-1) { |
89 | debug ("LZMA: 64bit support not enabled.\n"); | 89 | debug ("LZMA: 64bit support not enabled.\n"); |
90 | return SZ_ERROR_DATA; | 90 | return SZ_ERROR_DATA; |
91 | } | 91 | } |
92 | } | 92 | } |
93 | 93 | ||
94 | debug("LZMA: Uncompresed size............ 0x%zx\n", outSizeFull); | 94 | debug("LZMA: Uncompresed size............ 0x%zx\n", outSizeFull); |
95 | debug("LZMA: Compresed size.............. 0x%zx\n", compressedSize); | 95 | debug("LZMA: Compresed size.............. 0x%zx\n", compressedSize); |
96 | 96 | ||
97 | g_Alloc.Alloc = SzAlloc; | 97 | g_Alloc.Alloc = SzAlloc; |
98 | g_Alloc.Free = SzFree; | 98 | g_Alloc.Free = SzFree; |
99 | 99 | ||
100 | /* Short-circuit early if we know the buffer can't hold the results. */ | ||
101 | if (outSizeFull != (SizeT)-1 && *uncompressedSize < outSizeFull) | ||
102 | return SZ_ERROR_OUTPUT_EOF; | ||
103 | |||
100 | /* Decompress */ | 104 | /* Decompress */ |
101 | outProcessed = outSizeFull; | 105 | outProcessed = *uncompressedSize; |
102 | 106 | ||
103 | WATCHDOG_RESET(); | 107 | WATCHDOG_RESET(); |
104 | 108 | ||
105 | res = LzmaDecode( | 109 | res = LzmaDecode( |
106 | outStream, &outProcessed, | 110 | outStream, &outProcessed, |
107 | inStream + LZMA_DATA_OFFSET, &compressedSize, | 111 | inStream + LZMA_DATA_OFFSET, &compressedSize, |
108 | inStream, LZMA_PROPS_SIZE, LZMA_FINISH_ANY, &state, &g_Alloc); | 112 | inStream, LZMA_PROPS_SIZE, LZMA_FINISH_END, &state, &g_Alloc); |
109 | *uncompressedSize = outProcessed; | 113 | *uncompressedSize = outProcessed; |
110 | if (res != SZ_OK) { | 114 | if (res != SZ_OK) { |
111 | return res; | 115 | return res; |
112 | } | 116 | } |
113 | 117 | ||
114 | return res; | 118 | return res; |
115 | } | 119 | } |
116 | 120 | ||
117 | #endif | 121 | #endif |
118 | 122 |