Merge branch develop-3.10
[firefly-linux-kernel-4.4.55.git] / lib / lzo / lzo1x_decompress_safe.c
index 569985d522d518a8992929d5924b6a5062ff9e93..a1c387f6afba24c3a027456b48b44c603c3c3b96 100644 (file)
 #define NEED_OP(x)      if (!HAVE_OP(x)) goto output_overrun
 #define TEST_LB(m_pos)  if ((m_pos) < out) goto lookbehind_overrun
 
+/* This MAX_255_COUNT is the maximum number of times we can add 255 to a base
+ * count without overflowing an integer. The multiply will overflow when
+ * multiplying 255 by more than MAXINT/255. The sum will overflow earlier
+ * depending on the base count. Since the base count is taken from a u8
+ * and a few bits, it is safe to assume that it will always be lower than
+ * or equal to 2*255, thus we can always prevent any overflow by accepting
+ * two less 255 steps. See Documentation/lzo.txt for more information.
+ */
+#define MAX_255_COUNT      ((((size_t)~0) / 255) - 2)
+
 int lzo1x_decompress_safe(const unsigned char *in, size_t in_len,
                          unsigned char *out, size_t *out_len)
 {
@@ -55,12 +65,19 @@ int lzo1x_decompress_safe(const unsigned char *in, size_t in_len,
                if (t < 16) {
                        if (likely(state == 0)) {
                                if (unlikely(t == 0)) {
+                                       size_t offset;
+                                       const unsigned char *ip_last = ip;
+
                                        while (unlikely(*ip == 0)) {
-                                               t += 255;
                                                ip++;
                                                NEED_IP(1);
                                        }
-                                       t += 15 + *ip++;
+                                       offset = ip - ip_last;
+                                       if (unlikely(offset > MAX_255_COUNT))
+                                               return LZO_E_ERROR;
+
+                                       offset = (offset << 8) - offset;
+                                       t += offset + 15 + *ip++;
                                }
                                t += 3;
 copy_literal_run:
@@ -116,12 +133,19 @@ copy_literal_run:
                } else if (t >= 32) {
                        t = (t & 31) + (3 - 1);
                        if (unlikely(t == 2)) {
+                               size_t offset;
+                               const unsigned char *ip_last = ip;
+
                                while (unlikely(*ip == 0)) {
-                                       t += 255;
                                        ip++;
                                        NEED_IP(1);
                                }
-                               t += 31 + *ip++;
+                               offset = ip - ip_last;
+                               if (unlikely(offset > MAX_255_COUNT))
+                                       return LZO_E_ERROR;
+
+                               offset = (offset << 8) - offset;
+                               t += offset + 31 + *ip++;
                                NEED_IP(2);
                        }
                        m_pos = op - 1;
@@ -134,12 +158,19 @@ copy_literal_run:
                        m_pos -= (t & 8) << 11;
                        t = (t & 7) + (3 - 1);
                        if (unlikely(t == 2)) {
+                               size_t offset;
+                               const unsigned char *ip_last = ip;
+
                                while (unlikely(*ip == 0)) {
-                                       t += 255;
                                        ip++;
                                        NEED_IP(1);
                                }
-                               t += 7 + *ip++;
+                               offset = ip - ip_last;
+                               if (unlikely(offset > MAX_255_COUNT))
+                                       return LZO_E_ERROR;
+
+                               offset = (offset << 8) - offset;
+                               t += offset + 7 + *ip++;
                                NEED_IP(2);
                        }
                        next = get_unaligned_le16(ip);