]> nv-tegra.nvidia Code Review - linux-3.10.git/blobdiff - lib/decompress_unlzo.c
misc: tegra-profiler: fix stopping the session
[linux-3.10.git] / lib / decompress_unlzo.c
index df3e98f945a6e2ee7945c6fb16483f499e5cee0c..960183d4258f2bffda4dfa421525d04cdd40a949 100644 (file)
@@ -31,9 +31,8 @@
  */
 
 #ifdef STATIC
-#include "lzo/lzo1x_decompress.c"
+#include "lzo/lzo1x_decompress_safe.c"
 #else
-#include <linux/slab.h>
 #include <linux/decompress/unlzo.h>
 #endif
 
@@ -49,14 +48,25 @@ static const unsigned char lzop_magic[] = {
 
 #define LZO_BLOCK_SIZE        (256*1024l)
 #define HEADER_HAS_FILTER      0x00000800L
+#define HEADER_SIZE_MIN       (9 + 7     + 4 + 8     + 1       + 4)
+#define HEADER_SIZE_MAX       (9 + 7 + 1 + 8 + 8 + 4 + 1 + 255 + 4)
 
-STATIC inline int INIT parse_header(u8 *input, u8 *skip)
+STATIC inline int INIT parse_header(u8 *input, int *skip, int in_len)
 {
        int l;
        u8 *parse = input;
+       u8 *end = input + in_len;
        u8 level = 0;
        u16 version;
 
+       /*
+        * Check that there's enough input to possibly have a valid header.
+        * Then it is possible to parse several fields until the minimum
+        * size may have been used.
+        */
+       if (in_len < HEADER_SIZE_MIN)
+               return 0;
+
        /* read magic: 9 first bits */
        for (l = 0; l < 9; l++) {
                if (*parse++ != lzop_magic[l])
@@ -74,6 +84,15 @@ STATIC inline int INIT parse_header(u8 *input, u8 *skip)
        else
                parse += 4; /* flags */
 
+       /*
+        * At least mode, mtime_low, filename length, and checksum must
+        * be left to be parsed. If also mtime_high is present, it's OK
+        * because the next input buffer check is after reading the
+        * filename length.
+        */
+       if (end - parse < 8 + 1 + 4)
+               return 0;
+
        /* skip mode and mtime_low */
        parse += 8;
        if (version >= 0x0940)
@@ -81,6 +100,8 @@ STATIC inline int INIT parse_header(u8 *input, u8 *skip)
 
        l = *parse++;
        /* don't care about the file name, and skip checksum */
+       if (end - parse < l + 4)
+               return 0;
        parse += l + 4;
 
        *skip = parse - input;
@@ -93,7 +114,8 @@ STATIC inline int INIT unlzo(u8 *input, int in_len,
                                u8 *output, int *posp,
                                void (*error) (char *x))
 {
-       u8 skip = 0, r = 0;
+       u8 r = 0;
+       int skip = 0;
        u32 src_len, dst_len;
        size_t tmp;
        u8 *in_buf, *in_buf_save, *out_buf;
@@ -117,8 +139,8 @@ STATIC inline int INIT unlzo(u8 *input, int in_len,
                goto exit_1;
        } else if (input) {
                in_buf = input;
-       } else if (!fill || !posp) {
-               error("NULL input pointer and missing position pointer or fill function");
+       } else if (!fill) {
+               error("NULL input pointer and missing fill function");
                goto exit_1;
        } else {
                in_buf = malloc(lzo1x_worst_compress(LZO_BLOCK_SIZE));
@@ -132,22 +154,47 @@ STATIC inline int INIT unlzo(u8 *input, int in_len,
        if (posp)
                *posp = 0;
 
-       if (fill)
-               fill(in_buf, lzo1x_worst_compress(LZO_BLOCK_SIZE));
+       if (fill) {
+               /*
+                * Start from in_buf + HEADER_SIZE_MAX to make it possible
+                * to use memcpy() to copy the unused data to the beginning
+                * of the buffer. This way memmove() isn't needed which
+                * is missing from pre-boot environments of most archs.
+                */
+               in_buf += HEADER_SIZE_MAX;
+               in_len = fill(in_buf, HEADER_SIZE_MAX);
+       }
 
-       if (!parse_header(input, &skip)) {
+       if (!parse_header(in_buf, &skip, in_len)) {
                error("invalid header");
                goto exit_2;
        }
        in_buf += skip;
+       in_len -= skip;
+
+       if (fill) {
+               /* Move the unused data to the beginning of the buffer. */
+               memcpy(in_buf_save, in_buf, in_len);
+               in_buf = in_buf_save;
+       }
 
        if (posp)
                *posp = skip;
 
        for (;;) {
                /* read uncompressed block size */
+               if (fill && in_len < 4) {
+                       skip = fill(in_buf + in_len, 4 - in_len);
+                       if (skip > 0)
+                               in_len += skip;
+               }
+               if (in_len < 4) {
+                       error("file corrupted");
+                       goto exit_2;
+               }
                dst_len = get_unaligned_be32(in_buf);
                in_buf += 4;
+               in_len -= 4;
 
                /* exit if last block */
                if (dst_len == 0) {
@@ -162,8 +209,18 @@ STATIC inline int INIT unlzo(u8 *input, int in_len,
                }
 
                /* read compressed block size, and skip block checksum info */
+               if (fill && in_len < 8) {
+                       skip = fill(in_buf + in_len, 8 - in_len);
+                       if (skip > 0)
+                               in_len += skip;
+               }
+               if (in_len < 8) {
+                       error("file corrupted");
+                       goto exit_2;
+               }
                src_len = get_unaligned_be32(in_buf);
                in_buf += 8;
+               in_len -= 8;
 
                if (src_len <= 0 || src_len > dst_len) {
                        error("file corrupted");
@@ -171,6 +228,15 @@ STATIC inline int INIT unlzo(u8 *input, int in_len,
                }
 
                /* decompress */
+               if (fill && in_len < src_len) {
+                       skip = fill(in_buf + in_len, src_len - in_len);
+                       if (skip > 0)
+                               in_len += skip;
+               }
+               if (in_len < src_len) {
+                       error("file corrupted");
+                       goto exit_2;
+               }
                tmp = dst_len;
 
                /* When the input data is not compressed at all,
@@ -188,23 +254,32 @@ STATIC inline int INIT unlzo(u8 *input, int in_len,
                        }
                }
 
-               if (flush)
-                       flush(out_buf, dst_len);
+               if (flush && flush(out_buf, dst_len) != dst_len)
+                       goto exit_2;
                if (output)
                        out_buf += dst_len;
                if (posp)
                        *posp += src_len + 12;
+
+               in_buf += src_len;
+               in_len -= src_len;
                if (fill) {
+                       /*
+                        * If there happens to still be unused data left in
+                        * in_buf, move it to the beginning of the buffer.
+                        * Use a loop to avoid memmove() dependency.
+                        */
+                       if (in_len > 0)
+                               for (skip = 0; skip < in_len; ++skip)
+                                       in_buf_save[skip] = in_buf[skip];
                        in_buf = in_buf_save;
-                       fill(in_buf, lzo1x_worst_compress(LZO_BLOCK_SIZE));
-               } else
-                       in_buf += src_len;
+               }
        }
 
        ret = 0;
 exit_2:
        if (!input)
-               free(in_buf);
+               free(in_buf_save);
 exit_1:
        if (!output)
                free(out_buf);