Change to using an alloc-realloc strategy

Allocate enough memory to guarantee we can store the OID, encode into
the buffer, then realloc and copy into a buffer of exactly the right
size.

Signed-off-by: David Horstmann <david.horstmann@arm.com>
diff --git a/library/oid.c b/library/oid.c
index 3c27bfc..139a707 100644
--- a/library/oid.c
+++ b/library/oid.c
@@ -951,111 +951,112 @@
     const char *str_ptr = oid_str;
     const char *str_bound = oid_str + size;
     unsigned int val = 0;
-    size_t encoded_len;
     unsigned int component1, component2;
 
-    /* First pass - parse the string to get the length of buffer required */
+    /* Count the number of dots to get a worst-case allocation size. */
+    size_t num_dots = 0;
+    for (size_t i = 0; (i < size) && (oid_str[i] != '\0'); i++) {
+        if (oid_str[i] == '.') {
+            num_dots++;
+        }
+    }
+    /* Allocate maximum possible required memory:
+     * There are (num_dots + 1) integer components, but the first 2 share the
+     * same subidentifier, so we only need num_dots subidentifiers maximum. */
+    if (num_dots == 0 || (num_dots > SIZE_MAX / sizeof(unsigned int))) {
+        return MBEDTLS_ERR_ASN1_INVALID_DATA;
+    }
+    size_t max_possible_bytes = num_dots * sizeof(unsigned int);
+    oid->p = mbedtls_calloc(max_possible_bytes, 1);
+    if (oid->p == NULL) {
+        return MBEDTLS_ERR_ASN1_ALLOC_FAILED;
+    }
+    unsigned char *out_ptr = oid->p;
+    unsigned char *out_bound = oid->p + max_possible_bytes;
 
     ret = oid_parse_number(&component1, &str_ptr, str_bound);
     if (ret != 0) {
-        return ret;
+        goto error;
     }
     if (component1 > 2) {
         /* First component can't be > 2 */
-        return MBEDTLS_ERR_ASN1_INVALID_DATA;
+        ret = MBEDTLS_ERR_ASN1_INVALID_DATA;
+        goto error;
     }
     if (str_ptr >= str_bound || *str_ptr != '.') {
-        return MBEDTLS_ERR_ASN1_INVALID_DATA;
+        ret = MBEDTLS_ERR_ASN1_INVALID_DATA;
+        goto error;
     }
     str_ptr++;
 
     ret = oid_parse_number(&component2, &str_ptr, str_bound);
     if (ret != 0) {
-        return ret;
+        goto error;
     }
     if ((component1 < 2) && (component2 > 39)) {
         /* Root nodes 0 and 1 may have up to 40 children, numbered 0-39 */
-        return MBEDTLS_ERR_ASN1_INVALID_DATA;
+        ret = MBEDTLS_ERR_ASN1_INVALID_DATA;
+        goto error;
     }
     if (str_ptr < str_bound && *str_ptr != '\0') {
         if (*str_ptr == '.') {
             str_ptr++;
         } else {
-            return MBEDTLS_ERR_ASN1_INVALID_DATA;
+            ret = MBEDTLS_ERR_ASN1_INVALID_DATA;
+            goto error;
         }
     }
 
     if ((UINT_MAX - component2) <= (component1 * 40)) {
-        return MBEDTLS_ERR_ASN1_INVALID_DATA;
+        ret = MBEDTLS_ERR_ASN1_INVALID_DATA;
+        goto error;
     }
-    encoded_len = oid_subidentifier_num_bytes((component1 * 40) + component2);
+    ret = oid_subidentifier_encode_into(&out_ptr, out_bound,
+                                        (component1 * 40) + component2);
+    if (ret != 0) {
+        goto error;
+    }
 
     while (str_ptr < str_bound && *str_ptr != '\0') {
-        oid_parse_number(&val, &str_ptr, str_bound);
+        ret = oid_parse_number(&val, &str_ptr, str_bound);
         if (ret != 0) {
-            return ret;
+            goto error;
         }
         if (str_ptr < str_bound && *str_ptr != '\0') {
             if (*str_ptr == '.') {
                 str_ptr++;
             } else {
-                return MBEDTLS_ERR_ASN1_INVALID_DATA;
+                ret = MBEDTLS_ERR_ASN1_INVALID_DATA;
+                goto error;
             }
         }
 
-        size_t num_bytes = oid_subidentifier_num_bytes(val);
-        if ((SIZE_MAX - encoded_len) <= num_bytes) {
-            return MBEDTLS_ERR_ASN1_INVALID_DATA;
-        }
-        encoded_len += num_bytes;
-    }
-
-    oid->p = mbedtls_calloc(encoded_len, 1);
-    if (oid->p == NULL) {
-        return MBEDTLS_ERR_ASN1_ALLOC_FAILED;
-    }
-    oid->len = encoded_len;
-
-    /* Second pass - now that we've allocated the buffer, go back to the
-     * start and encode */
-
-    str_ptr = oid_str;
-    unsigned char *out_ptr = oid->p;
-    unsigned char *out_bound = oid->p + oid->len;
-
-    /* No need to do validation this time, as we did it on the first pass */
-    oid_parse_number(&component1, &str_ptr, str_bound);
-    /* Skip past the '.' */
-    str_ptr++;
-    oid_parse_number(&component2, &str_ptr, str_bound);
-    /* Skip past the '.' */
-    str_ptr++;
-    ret = oid_subidentifier_encode_into(&out_ptr, out_bound,
-                                        (component1 * 40) + component2);
-    if (ret != 0) {
-        mbedtls_free(oid->p);
-        oid->p = NULL;
-        oid->len = 0;
-        return ret;
-    }
-    while (str_ptr < str_bound && *str_ptr != '\0') {
-        oid_parse_number(&val, &str_ptr, str_bound);
-        if (str_ptr < str_bound && *str_ptr == '.') {
-            /* Skip past the '.' */
-            str_ptr++;
-        }
-
         ret = oid_subidentifier_encode_into(&out_ptr, out_bound, val);
         if (ret != 0) {
-            mbedtls_free(oid->p);
-            oid->p = NULL;
-            oid->len = 0;
-            return ret;
+            goto error;
         }
     }
+
+    size_t encoded_len = out_ptr - oid->p;
+    unsigned char *minimum_mem = mbedtls_calloc(encoded_len, 1);
+    if (minimum_mem == NULL) {
+        ret = MBEDTLS_ERR_ASN1_ALLOC_FAILED;
+        goto error;
+    }
+    memcpy(minimum_mem, oid->p, encoded_len);
+    mbedtls_free(oid->p);
+    oid->p = minimum_mem;
+    oid->len = encoded_len;
+
     oid->tag = MBEDTLS_ASN1_OID;
 
     return 0;
+
+error:
+    mbedtls_free(oid->p);
+    oid->p = NULL;
+    oid->len = 0;
+    return ret;
 }
 
 #endif /* MBEDTLS_OID_C */