Commit fb7fa589fd3ecc212fabd7867a4ecc3b175260c1

Authored by Lasse Collin
Committed by Linus Torvalds
1 parent 5a3f81a702

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