Skip to content

Commit fb7fa58

Browse files
Larhzutorvalds
authored andcommitted
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 <[email protected]> Cc: "H. Peter Anvin" <[email protected]> Cc: Alain Knaff <[email protected]> Cc: Albin Tonnerre <[email protected]> Cc: Phillip Lougher <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 5a3f81a commit fb7fa58

File tree

1 file changed

+50
-10
lines changed

1 file changed

+50
-10
lines changed

lib/decompress_unlzo.c

+50-10
Original file line numberDiff line numberDiff line change
@@ -139,8 +139,8 @@ STATIC inline int INIT unlzo(u8 *input, int in_len,
139139
goto exit_1;
140140
} else if (input) {
141141
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");
144144
goto exit_1;
145145
} else {
146146
in_buf = malloc(lzo1x_worst_compress(LZO_BLOCK_SIZE));
@@ -154,21 +154,40 @@ STATIC inline int INIT unlzo(u8 *input, int in_len,
154154
if (posp)
155155
*posp = 0;
156156

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+
}
159167

160-
if (!parse_header(input, &skip, in_len)) {
168+
if (!parse_header(in_buf, &skip, in_len)) {
161169
error("invalid header");
162170
goto exit_2;
163171
}
164172
in_buf += skip;
165173
in_len -= skip;
166174

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+
167181
if (posp)
168182
*posp = skip;
169183

170184
for (;;) {
171185
/* 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+
}
172191
if (in_len < 4) {
173192
error("file corrupted");
174193
goto exit_2;
@@ -190,6 +209,11 @@ STATIC inline int INIT unlzo(u8 *input, int in_len,
190209
}
191210

192211
/* 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+
}
193217
if (in_len < 8) {
194218
error("file corrupted");
195219
goto exit_2;
@@ -198,12 +222,21 @@ STATIC inline int INIT unlzo(u8 *input, int in_len,
198222
in_buf += 8;
199223
in_len -= 8;
200224

201-
if (src_len <= 0 || src_len > dst_len || src_len > in_len) {
225+
if (src_len <= 0 || src_len > dst_len) {
202226
error("file corrupted");
203227
goto exit_2;
204228
}
205229

206230
/* 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+
}
207240
tmp = dst_len;
208241

209242
/* When the input data is not compressed at all,
@@ -227,12 +260,19 @@ STATIC inline int INIT unlzo(u8 *input, int in_len,
227260
out_buf += dst_len;
228261
if (posp)
229262
*posp += src_len + 12;
263+
264+
in_buf += src_len;
265+
in_len -= src_len;
230266
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];
231275
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;
236276
}
237277
}
238278

0 commit comments

Comments
 (0)