Commit fb7fa589fd3ecc212fabd7867a4ecc3b175260c1
Committed by
Linus Torvalds
1 parent
5a3f81a702
Exists in
master
and in
7 other branches
Decompressors: fix callback-to-callback mode in decompress_unlzo.c
Callback-to-callback decompression mode is used for initrd (not initramfs). The LZO wrapper is broken for this use case for two reasons: - The argument validation is needlessly too strict by requiring that "posp" is non-NULL when "fill" is non-NULL. - The buffer handling code didn't work at all for this use case. I tested with LZO-compressed kernel, initramfs, initrd, and corrupt (truncated) initramfs and initrd images. Signed-off-by: Lasse Collin <lasse.collin@tukaani.org> Cc: "H. Peter Anvin" <hpa@zytor.com> Cc: Alain Knaff <alain@knaff.lu> Cc: Albin Tonnerre <albin.tonnerre@free-electrons.com> Cc: Phillip Lougher <phillip@lougher.demon.co.uk> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Showing 1 changed file with 50 additions and 10 deletions Side-by-side Diff
lib/decompress_unlzo.c
... | ... | @@ -139,8 +139,8 @@ |
139 | 139 | goto exit_1; |
140 | 140 | } else if (input) { |
141 | 141 | in_buf = input; |
142 | - } else if (!fill || !posp) { | |
143 | - error("NULL input pointer and missing position pointer or fill function"); | |
142 | + } else if (!fill) { | |
143 | + error("NULL input pointer and missing fill function"); | |
144 | 144 | goto exit_1; |
145 | 145 | } else { |
146 | 146 | in_buf = malloc(lzo1x_worst_compress(LZO_BLOCK_SIZE)); |
147 | 147 | |
148 | 148 | |
149 | 149 | |
... | ... | @@ -154,21 +154,40 @@ |
154 | 154 | if (posp) |
155 | 155 | *posp = 0; |
156 | 156 | |
157 | - if (fill) | |
158 | - fill(in_buf, lzo1x_worst_compress(LZO_BLOCK_SIZE)); | |
157 | + if (fill) { | |
158 | + /* | |
159 | + * Start from in_buf + HEADER_SIZE_MAX to make it possible | |
160 | + * to use memcpy() to copy the unused data to the beginning | |
161 | + * of the buffer. This way memmove() isn't needed which | |
162 | + * is missing from pre-boot environments of most archs. | |
163 | + */ | |
164 | + in_buf += HEADER_SIZE_MAX; | |
165 | + in_len = fill(in_buf, HEADER_SIZE_MAX); | |
166 | + } | |
159 | 167 | |
160 | - if (!parse_header(input, &skip, in_len)) { | |
168 | + if (!parse_header(in_buf, &skip, in_len)) { | |
161 | 169 | error("invalid header"); |
162 | 170 | goto exit_2; |
163 | 171 | } |
164 | 172 | in_buf += skip; |
165 | 173 | in_len -= skip; |
166 | 174 | |
175 | + if (fill) { | |
176 | + /* Move the unused data to the beginning of the buffer. */ | |
177 | + memcpy(in_buf_save, in_buf, in_len); | |
178 | + in_buf = in_buf_save; | |
179 | + } | |
180 | + | |
167 | 181 | if (posp) |
168 | 182 | *posp = skip; |
169 | 183 | |
170 | 184 | for (;;) { |
171 | 185 | /* read uncompressed block size */ |
186 | + if (fill && in_len < 4) { | |
187 | + skip = fill(in_buf + in_len, 4 - in_len); | |
188 | + if (skip > 0) | |
189 | + in_len += skip; | |
190 | + } | |
172 | 191 | if (in_len < 4) { |
173 | 192 | error("file corrupted"); |
174 | 193 | goto exit_2; |
... | ... | @@ -190,6 +209,11 @@ |
190 | 209 | } |
191 | 210 | |
192 | 211 | /* read compressed block size, and skip block checksum info */ |
212 | + if (fill && in_len < 8) { | |
213 | + skip = fill(in_buf + in_len, 8 - in_len); | |
214 | + if (skip > 0) | |
215 | + in_len += skip; | |
216 | + } | |
193 | 217 | if (in_len < 8) { |
194 | 218 | error("file corrupted"); |
195 | 219 | goto exit_2; |
196 | 220 | |
... | ... | @@ -198,12 +222,21 @@ |
198 | 222 | in_buf += 8; |
199 | 223 | in_len -= 8; |
200 | 224 | |
201 | - if (src_len <= 0 || src_len > dst_len || src_len > in_len) { | |
225 | + if (src_len <= 0 || src_len > dst_len) { | |
202 | 226 | error("file corrupted"); |
203 | 227 | goto exit_2; |
204 | 228 | } |
205 | 229 | |
206 | 230 | /* decompress */ |
231 | + if (fill && in_len < src_len) { | |
232 | + skip = fill(in_buf + in_len, src_len - in_len); | |
233 | + if (skip > 0) | |
234 | + in_len += skip; | |
235 | + } | |
236 | + if (in_len < src_len) { | |
237 | + error("file corrupted"); | |
238 | + goto exit_2; | |
239 | + } | |
207 | 240 | tmp = dst_len; |
208 | 241 | |
209 | 242 | /* When the input data is not compressed at all, |
210 | 243 | |
211 | 244 | |
... | ... | @@ -227,12 +260,19 @@ |
227 | 260 | out_buf += dst_len; |
228 | 261 | if (posp) |
229 | 262 | *posp += src_len + 12; |
263 | + | |
264 | + in_buf += src_len; | |
265 | + in_len -= src_len; | |
230 | 266 | if (fill) { |
267 | + /* | |
268 | + * If there happens to still be unused data left in | |
269 | + * in_buf, move it to the beginning of the buffer. | |
270 | + * Use a loop to avoid memmove() dependency. | |
271 | + */ | |
272 | + if (in_len > 0) | |
273 | + for (skip = 0; skip < in_len; ++skip) | |
274 | + in_buf_save[skip] = in_buf[skip]; | |
231 | 275 | in_buf = in_buf_save; |
232 | - fill(in_buf, lzo1x_worst_compress(LZO_BLOCK_SIZE)); | |
233 | - } else { | |
234 | - in_buf += src_len; | |
235 | - in_len -= src_len; | |
236 | 276 | } |
237 | 277 | } |
238 | 278 |