agm: service: Handle metadata parsing within size limit

Add size limit check before parsing GKV ,CKV and properties metadata
to avoid memory read overflow

Change-Id: Ia2b99c7641166ef042b22e5a5581d264e0aa8eea
diff --git a/service/src/metadata.c b/service/src/metadata.c
index c94f856..047f5e0 100644
--- a/service/src/metadata.c
+++ b/service/src/metadata.c
@@ -270,17 +270,17 @@
     return merged;
 }
 
-int metadata_copy(struct agm_meta_data_gsl *dest, uint32_t size __unused,
+int metadata_copy(struct agm_meta_data_gsl *dest, uint32_t size,
                                               uint8_t *metadata)
 {
 
     int ret = 0;
+    int min_req_len = 0;
 
     if (!metadata) {
         AGM_LOGI("NULL metadata passed, ignoring\n");
-        return ret;
+        goto done;
     }
-
     if ((NUM_GKV(metadata) > MAX_KVPAIR) || (NUM_CKV(metadata) > MAX_KVPAIR)) {
         AGM_LOGE("Num GKVs %d Num CKVs %d more than expected: %d", NUM_GKV(metadata),
                                                       NUM_CKV(metadata), MAX_KVPAIR);
@@ -288,39 +288,84 @@
         return ret;
     }
 
+    min_req_len += sizeof(uint32_t);
+    if (size < min_req_len) {
+        AGM_LOGE("size should be atleast %d size for GKV\n", sizeof(uint32_t));
+        ret = -EINVAL;
+        goto done;
+
+    }
+
     dest->gkv.num_kvs = NUM_GKV(metadata);
     dest->gkv.kv =  calloc(dest->gkv.num_kvs, sizeof(struct agm_key_value));
     if (!dest->gkv.kv) {
         AGM_LOGE("Memory allocation failed to copy GKV\n");
         ret = -ENOMEM;
-        return ret;
+        goto free_metadata;
+    }
+
+    min_req_len += (dest->gkv.num_kvs * sizeof(struct agm_key_value));
+    if (size < min_req_len) {
+        AGM_LOGE("Invalid GKV passed\n");
+        ret = -EINVAL;
+        goto free_metadata;
     }
     memcpy(dest->gkv.kv, PTR_TO_GKV(metadata), dest->gkv.num_kvs *
                                     sizeof(struct agm_key_value));
 
+    min_req_len += sizeof(uint32_t);
+    if (size < min_req_len) {
+        goto done;
+    }
     dest->ckv.num_kvs = NUM_CKV(metadata);
     dest->ckv.kv =  calloc(dest->ckv.num_kvs, sizeof(struct agm_key_value));
     if (!dest->ckv.kv) {
         AGM_LOGE("Memory allocation failed to copy CKV\n");
-        metadata_free(dest);
         ret = -ENOMEM;
-        return ret;
+        goto free_metadata;
+    }
+    min_req_len += (dest->ckv.num_kvs * sizeof(struct agm_key_value));
+    if (size < min_req_len) {
+        AGM_LOGE("Invalid CKV passed\n");
+        ret = -EINVAL;
+        goto free_metadata;
     }
     memcpy(dest->ckv.kv, PTR_TO_CKV(metadata), dest->ckv.num_kvs *
                                     sizeof(struct agm_key_value));
 
+    min_req_len += sizeof(uint32_t);
+    if (size < min_req_len) {
+        goto done;
+    }
     dest->sg_props.prop_id = PROP_ID(metadata);
+
+    min_req_len += sizeof(uint32_t);
+    if (size < min_req_len) {
+        AGM_LOGE("Invalid properties passed\n");
+        ret = -EINVAL;
+        goto free_metadata;
+    }
     dest->sg_props.num_values = NUM_PROPS(metadata);
     dest->sg_props.values =  calloc(dest->sg_props.num_values, sizeof(uint32_t));
     if (!dest->sg_props.values) {
         AGM_LOGE("Memory allocation failed to copy properties\n");
-        metadata_free(dest);
         ret = -ENOMEM;
-        return ret;
+        goto free_metadata;
+    }
+    min_req_len += (dest->sg_props.num_values * sizeof(uint32_t));
+    if (size < min_req_len) {
+        AGM_LOGE("Invalid properties passed\n");
+        ret = -EINVAL;
+        goto free_metadata;
     }
     memcpy(dest->sg_props.values, PTR_TO_PROPS(metadata),
            dest->sg_props.num_values * sizeof(uint32_t));
+    goto done;
 
+free_metadata:
+    metadata_free(dest);
+
+done:
     return ret;
 
 }