Add error and range checks to parse_range

Only trusted input is passed to parse_range, but check for invalid
input to catch possible problems in transfer lists.

Bug: 21033983
Bug: 21034030
Bug: 21034172
Bug: 21034406
Change-Id: Ia17537a2d23d5f701522fbc42ed38924e1ee3366
diff --git a/updater/blockimg.c b/updater/blockimg.c
index 532e7b8..a98cdcc 100644
--- a/updater/blockimg.c
+++ b/updater/blockimg.c
@@ -60,30 +60,91 @@
     int pos[0];
 } RangeSet;
 
+#define RANGESET_MAX_POINTS \
+    ((int)((INT_MAX / sizeof(int)) - sizeof(RangeSet)))
+
 static RangeSet* parse_range(char* text) {
     char* save;
-    int num;
-    num = strtol(strtok_r(text, ",", &save), NULL, 0);
+    char* token;
+    int i, num;
+    long int val;
+    RangeSet* out = NULL;
+    size_t bufsize;
 
-    RangeSet* out = malloc(sizeof(RangeSet) + num * sizeof(int));
-    if (out == NULL) {
-        fprintf(stderr, "failed to allocate range of %zu bytes\n",
-                sizeof(RangeSet) + num * sizeof(int));
-        exit(1);
+    if (!text) {
+        goto err;
     }
+
+    token = strtok_r(text, ",", &save);
+
+    if (!token) {
+        goto err;
+    }
+
+    val = strtol(token, NULL, 0);
+
+    if (val < 2 || val > RANGESET_MAX_POINTS) {
+        goto err;
+    } else if (val % 2) {
+        goto err; // must be even
+    }
+
+    num = (int) val;
+    bufsize = sizeof(RangeSet) + num * sizeof(int);
+
+    out = malloc(bufsize);
+
+    if (!out) {
+        fprintf(stderr, "failed to allocate range of %zu bytes\n", bufsize);
+        goto err;
+    }
+
     out->count = num / 2;
     out->size = 0;
-    int i;
+
     for (i = 0; i < num; ++i) {
-        out->pos[i] = strtol(strtok_r(NULL, ",", &save), NULL, 0);
-        if (i%2) {
+        token = strtok_r(NULL, ",", &save);
+
+        if (!token) {
+            goto err;
+        }
+
+        val = strtol(token, NULL, 0);
+
+        if (val < 0 || val > INT_MAX) {
+            goto err;
+        }
+
+        out->pos[i] = (int) val;
+
+        if (i % 2) {
+            if (out->pos[i - 1] >= out->pos[i]) {
+                goto err; // empty or negative range
+            }
+
+            if (out->size > INT_MAX - out->pos[i]) {
+                goto err; // overflow
+            }
+
             out->size += out->pos[i];
         } else {
+            if (out->size < 0) {
+                goto err;
+            }
+
             out->size -= out->pos[i];
         }
     }
 
+    if (out->size <= 0) {
+        goto err;
+    }
+
     return out;
+
+err:
+    fprintf(stderr, "failed to parse range '%s'\n", text ? text : "NULL");
+    exit(1);
 }
 
 static int range_overlaps(RangeSet* r1, RangeSet* r2) {