Merge "Camera: Metadata: Fix a.ctrl.awbAvailableModes" into jb-mr2-dev
diff --git a/camera/include/system/camera_metadata.h b/camera/include/system/camera_metadata.h
index 5c64bae..a0e5cd1 100644
--- a/camera/include/system/camera_metadata.h
+++ b/camera/include/system/camera_metadata.h
@@ -165,6 +165,18 @@
         size_t data_capacity);
 
 /**
+ * Allocate a new camera_metadata structure of size src_size. Copy the data,
+ * ignoring alignment, and then attempt validation. If validation
+ * fails, free the memory and return NULL. Otherwise return the pointer.
+ *
+ * The resulting pointer can be freed with free_camera_metadata().
+ */
+ANDROID_API
+camera_metadata_t *allocate_copy_camera_metadata_checked(
+        const camera_metadata_t *src,
+        size_t src_size);
+
+/**
  * Place a camera metadata structure into an existing buffer. Returns NULL if
  * the buffer is too small for the requested number of reserved entries and
  * bytes of data. The entry_capacity is measured in entry counts, and
@@ -253,6 +265,23 @@
         const camera_metadata_t *src);
 
 /**
+ * Validate that a metadata is structurally sane. That is, its internal
+ * state is such that we won't get buffer overflows or run into other
+ * 'impossible' issues when calling the other API functions.
+ *
+ * This is useful in particular after copying the binary metadata blob
+ * from an untrusted source, since passing this check means the data is at least
+ * consistent.
+ *
+ * The expected_size argument is optional.
+ *
+ * Returns 0 on success. A non-0 value is returned on error.
+ */
+ANDROID_API
+int validate_camera_metadata_structure(const camera_metadata_t *metadata,
+                                       const size_t *expected_size);
+
+/**
  * Append camera metadata in src to an existing metadata structure in dst.  This
  * does not resize the destination structure, so if it is too small, a non-zero
  * value is returned. On success, 0 is returned. Appending onto a sorted
diff --git a/camera/src/camera_metadata.c b/camera/src/camera_metadata.c
index 1963b89..76fca9a 100644
--- a/camera/src/camera_metadata.c
+++ b/camera/src/camera_metadata.c
@@ -15,6 +15,8 @@
  */
 #define _GNU_SOURCE // for fdprintf
 #include <system/camera_metadata.h>
+
+#define LOG_TAG "camera_metadata"
 #include <cutils/log.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -34,7 +36,9 @@
 #define DATA_ALIGNMENT _Alignas(camera_metadata_data_t)
 
 #define ALIGN_TO(val, alignment) \
-    (((uint32_t)(val) + ((alignment) - 1)) & ~((alignment) - 1))
+    (((uintptr_t)(val) + ((alignment) - 1)) & ~((alignment) - 1))
+
+typedef size_t uptrdiff_t;
 
 /**
  * A single metadata entry, storing an array of values of a given type. If the
@@ -93,10 +97,10 @@
     uint32_t                 flags;
     size_t                   entry_count;
     size_t                   entry_capacity;
-    ptrdiff_t                entries_start; // Offset from camera_metadata
+    uptrdiff_t               entries_start; // Offset from camera_metadata
     size_t                   data_count;
     size_t                   data_capacity;
-    ptrdiff_t                data_start; // Offset from camera_metadata
+    uptrdiff_t               data_start; // Offset from camera_metadata
     void                    *user; // User set pointer, not copied with buffer
     uint8_t                  reserved[0];
 };
@@ -159,6 +163,26 @@
     return (uint8_t*)metadata + metadata->data_start;
 }
 
+camera_metadata_t *allocate_copy_camera_metadata_checked(
+        const camera_metadata_t *src,
+        size_t src_size) {
+
+    if (src == NULL) {
+        return NULL;
+    }
+
+    void *buffer = malloc(src_size);
+    memcpy(buffer, src, src_size);
+
+    camera_metadata_t *metadata = (camera_metadata_t*) buffer;
+    if (validate_camera_metadata_structure(metadata, &src_size) != OK) {
+        free(buffer);
+        return NULL;
+    }
+
+    return metadata;
+}
+
 camera_metadata_t *allocate_camera_metadata(size_t entry_capacity,
                                             size_t data_capacity) {
     if (entry_capacity == 0) return NULL;
@@ -192,13 +216,9 @@
     metadata->data_count = 0;
     metadata->data_capacity = data_capacity;
     metadata->size = memory_needed;
-    if (metadata->data_capacity != 0) {
-        size_t data_unaligned = (uint8_t*)(get_entries(metadata) +
-                metadata->entry_capacity) - (uint8_t*)metadata;
-        metadata->data_start = ALIGN_TO(data_unaligned, DATA_ALIGNMENT);
-    } else {
-        metadata->data_start = 0;
-    }
+    size_t data_unaligned = (uint8_t*)(get_entries(metadata) +
+            metadata->entry_capacity) - (uint8_t*)metadata;
+    metadata->data_start = ALIGN_TO(data_unaligned, DATA_ALIGNMENT);
     metadata->user = NULL;
 
     return metadata;
@@ -271,6 +291,155 @@
     return metadata;
 }
 
+int validate_camera_metadata_structure(const camera_metadata_t *metadata,
+                                       const size_t *expected_size) {
+
+    if (metadata == NULL) {
+        return ERROR;
+    }
+
+    // Check that the metadata pointer is well-aligned first.
+    {
+        struct {
+            const char *name;
+            size_t alignment;
+        } alignments[] = {
+            {
+                .name = "camera_metadata",
+                .alignment = _Alignas(struct camera_metadata)
+            },
+            {
+                .name = "camera_metadata_buffer_entry",
+                .alignment = _Alignas(struct camera_metadata_buffer_entry)
+            },
+            {
+                .name = "camera_metadata_data",
+                .alignment = _Alignas(union camera_metadata_data)
+            },
+        };
+
+        for (size_t i = 0; i < sizeof(alignments)/sizeof(alignments[0]); ++i) {
+            uintptr_t aligned_ptr = ALIGN_TO(metadata, alignments[i].alignment);
+
+            if ((uintptr_t)metadata != aligned_ptr) {
+                ALOGE("%s: Metadata pointer is not aligned (actual %p, "
+                      "expected %p) to type %s",
+                      __FUNCTION__, metadata,
+                      (void*)aligned_ptr, alignments[i].name);
+                return ERROR;
+            }
+        }
+    }
+
+    /**
+     * Check that the metadata contents are correct
+     */
+
+    if (expected_size != NULL && metadata->size > *expected_size) {
+        ALOGE("%s: Metadata size (%u) should be <= expected size (%u)",
+              __FUNCTION__, metadata->size, *expected_size);
+        return ERROR;
+    }
+
+    if (metadata->entry_count > metadata->entry_capacity) {
+        ALOGE("%s: Entry count (%u) should be <= entry capacity (%u)",
+              __FUNCTION__, metadata->entry_count, metadata->entry_capacity);
+        return ERROR;
+    }
+
+    uptrdiff_t entries_end = metadata->entries_start + metadata->entry_capacity;
+    if (entries_end < metadata->entries_start || // overflow check
+        entries_end > metadata->data_start) {
+
+        ALOGE("%s: Entry start + capacity (%u) should be <= data start (%u)",
+               __FUNCTION__,
+              (metadata->entries_start + metadata->entry_capacity),
+              metadata->data_start);
+        return ERROR;
+    }
+
+    uptrdiff_t data_end = metadata->data_start + metadata->data_capacity;
+    if (data_end < metadata->data_start || // overflow check
+        data_end > metadata->size) {
+
+        ALOGE("%s: Data start + capacity (%u) should be <= total size (%u)",
+               __FUNCTION__,
+              (metadata->data_start + metadata->data_capacity),
+              metadata->size);
+        return ERROR;
+    }
+
+    // Validate each entry
+    size_t entry_count = metadata->entry_count;
+    camera_metadata_buffer_entry_t *entries = get_entries(metadata);
+
+    for (size_t i = 0; i < entry_count; ++i) {
+
+        if ((uintptr_t)&entries[i] != ALIGN_TO(&entries[i], ENTRY_ALIGNMENT)) {
+            ALOGE("%s: Entry index %u had bad alignment (address %p),"
+                  " expected alignment %d",
+                  __FUNCTION__, i, &entries[i], ENTRY_ALIGNMENT);
+            return ERROR;
+        }
+
+        camera_metadata_buffer_entry_t entry = entries[i];
+
+        if (entry.type >= NUM_TYPES) {
+            ALOGE("%s: Entry index %u had a bad type %d",
+                  __FUNCTION__, i, entry.type);
+            return ERROR;
+        }
+
+        // TODO: fix vendor_tag_ops across processes so we don't need to special
+        //       case vendor-specific tags
+        uint32_t tag_section = entry.tag >> 16;
+        int tag_type = get_camera_metadata_tag_type(entry.tag);
+        if (tag_type != (int)entry.type && tag_section < VENDOR_SECTION) {
+            ALOGE("%s: Entry index %u had tag type %d, but the type was %d",
+                  __FUNCTION__, i, tag_type, entry.type);
+            return ERROR;
+        }
+
+        size_t data_size =
+                calculate_camera_metadata_entry_data_size(entry.type,
+                                                          entry.count);
+
+        if (data_size != 0) {
+            camera_metadata_data_t *data =
+                    (camera_metadata_data_t*) (get_data(metadata) +
+                                               entry.data.offset);
+
+            if ((uintptr_t)data != ALIGN_TO(data, DATA_ALIGNMENT)) {
+                ALOGE("%s: Entry index %u had bad data alignment (address %p),"
+                      " expected align %d, (tag name %s, data size %u)",
+                      __FUNCTION__, i, data, DATA_ALIGNMENT,
+                      get_camera_metadata_tag_name(entry.tag) ?: "unknown",
+                      data_size);
+                return ERROR;
+            }
+
+            size_t data_entry_end = entry.data.offset + data_size;
+            if (data_entry_end < entry.data.offset || // overflow check
+                data_entry_end > metadata->data_capacity) {
+
+                ALOGE("%s: Entry index %u data ends (%u) beyond the capacity "
+                      "%u", __FUNCTION__, i, data_entry_end,
+                      metadata->data_capacity);
+                return ERROR;
+            }
+
+        } else if (entry.count == 0) {
+            if (entry.data.offset != 0) {
+                ALOGE("%s: Entry index %u had 0 items, but offset was non-0 "
+                     "(%u)", __FUNCTION__, i, entry.data.offset);
+                return ERROR;
+            }
+        } // else data stored inline, so we look at value which can be anything.
+    }
+
+    return OK;
+}
+
 int append_camera_metadata(camera_metadata_t *dst,
         const camera_metadata_t *src) {
     if (dst == NULL || src == NULL ) return ERROR;
diff --git a/camera/tests/camera_metadata_tests.cpp b/camera/tests/camera_metadata_tests.cpp
index 4712121..8b4aa2b 100644
--- a/camera/tests/camera_metadata_tests.cpp
+++ b/camera/tests/camera_metadata_tests.cpp
@@ -38,6 +38,9 @@
     ({struct _AlignasStruct { char c; T field; };       \
         offsetof(struct _AlignasStruct, field); })
 
+#define FINISH_USING_CAMERA_METADATA(m)                         \
+    EXPECT_EQ(OK, validate_camera_metadata_structure(m, NULL)); \
+    free_camera_metadata(m);                                    \
 
 TEST(camera_metadata, allocate_normal) {
     camera_metadata_t *m = NULL;
@@ -52,7 +55,7 @@
     EXPECT_EQ((size_t)0, get_camera_metadata_data_count(m));
     EXPECT_EQ(data_capacity, get_camera_metadata_data_capacity(m));
 
-    free_camera_metadata(m);
+    FINISH_USING_CAMERA_METADATA(m);
 }
 
 TEST(camera_metadata, allocate_nodata) {
@@ -66,7 +69,7 @@
     EXPECT_EQ((size_t)0, get_camera_metadata_data_count(m));
     EXPECT_EQ((size_t)0, get_camera_metadata_data_capacity(m));
 
-    free_camera_metadata(m);
+    FINISH_USING_CAMERA_METADATA(m);
 }
 
 TEST(camera_metadata, allocate_nothing) {
@@ -101,6 +104,8 @@
     EXPECT_EQ((size_t)0, get_camera_metadata_data_count(m));
     EXPECT_EQ(data_capacity, get_camera_metadata_data_capacity(m));
 
+    EXPECT_EQ(OK, validate_camera_metadata_structure(m, &buf_size));
+
     free(buf);
 }
 
@@ -157,6 +162,8 @@
     EXPECT_EQ(data_capacity, get_camera_metadata_data_capacity(m));
     EXPECT_EQ(buf + buf_size - extra_space, (uint8_t*)m + get_camera_metadata_size(m));
 
+    EXPECT_EQ(OK, validate_camera_metadata_structure(m, &buf_size));
+
     free(buf);
 }
 
@@ -173,7 +180,7 @@
     EXPECT_EQ(calculate_camera_metadata_size(0,0),
             get_camera_metadata_compact_size(m) );
 
-    free_camera_metadata(m);
+    FINISH_USING_CAMERA_METADATA(m);
 }
 
 TEST(camera_metadata, add_get_normal) {
@@ -183,6 +190,8 @@
 
     m = allocate_camera_metadata(entry_capacity, data_capacity);
 
+    EXPECT_EQ(OK, validate_camera_metadata_structure(m, NULL));
+
     int result;
     size_t data_used = 0;
     size_t entries_used = 0;
@@ -198,6 +207,8 @@
             get_camera_metadata_tag_type(ANDROID_SENSOR_EXPOSURE_TIME), 1);
     entries_used++;
 
+    EXPECT_EQ(OK, validate_camera_metadata_structure(m, NULL));
+
     // INT32
 
     int32_t sensitivity = 800;
@@ -209,6 +220,8 @@
             get_camera_metadata_tag_type(ANDROID_SENSOR_SENSITIVITY), 1);
     entries_used++;
 
+    EXPECT_EQ(OK, validate_camera_metadata_structure(m, NULL));
+
     // FLOAT
 
     float focusDistance = 0.5f;
@@ -220,6 +233,8 @@
             get_camera_metadata_tag_type(ANDROID_LENS_FOCUS_DISTANCE), 1);
     entries_used++;
 
+    EXPECT_EQ(OK, validate_camera_metadata_structure(m, NULL));
+
     // Array of FLOAT
 
     float colorTransform[9] = {
@@ -235,6 +250,8 @@
            get_camera_metadata_tag_type(ANDROID_COLOR_CORRECTION_TRANSFORM), 9);
     entries_used++;
 
+    EXPECT_EQ(OK, validate_camera_metadata_structure(m, NULL));
+
     // Check added entries
 
     camera_metadata_entry entry;
@@ -286,7 +303,7 @@
         dump_camera_metadata(m, 0, 2);
     }
 
-    free_camera_metadata(m);
+    FINISH_USING_CAMERA_METADATA(m);
 }
 
 void add_test_metadata(camera_metadata_t *m, int entry_count) {
@@ -358,7 +375,7 @@
         dump_camera_metadata(m, 0, 2);
     }
 
-    free_camera_metadata(m);
+    FINISH_USING_CAMERA_METADATA(m);
 }
 
 TEST(camera_metadata, add_too_much_data) {
@@ -378,7 +395,7 @@
             &exposure_time, 1);
     EXPECT_EQ(ERROR, result);
 
-    free_camera_metadata(m);
+    FINISH_USING_CAMERA_METADATA(m);
 }
 
 TEST(camera_metadata, copy_metadata) {
@@ -428,9 +445,10 @@
         }
     }
 
+    EXPECT_EQ(OK, validate_camera_metadata_structure(m2, &buf_size));
     free(buf);
 
-    free_camera_metadata(m);
+    FINISH_USING_CAMERA_METADATA(m);
 }
 
 TEST(camera_metadata, copy_metadata_extraspace) {
@@ -487,9 +505,10 @@
         }
     }
 
+    EXPECT_EQ(OK, validate_camera_metadata_structure(m2, &buf_size));
     free(buf);
 
-    free_camera_metadata(m);
+    FINISH_USING_CAMERA_METADATA(m);
 }
 
 TEST(camera_metadata, copy_metadata_nospace) {
@@ -516,7 +535,7 @@
 
     free(buf);
 
-    free_camera_metadata(m);
+    FINISH_USING_CAMERA_METADATA(m);
 }
 
 TEST(camera_metadata, append_metadata) {
@@ -598,8 +617,8 @@
         }
     }
 
-    free_camera_metadata(m);
-    free_camera_metadata(m2);
+    FINISH_USING_CAMERA_METADATA(m);
+    FINISH_USING_CAMERA_METADATA(m2);
 }
 
 TEST(camera_metadata, append_metadata_nospace) {
@@ -624,8 +643,8 @@
     EXPECT_EQ((size_t)0, get_camera_metadata_entry_count(m2));
     EXPECT_EQ((size_t)0, get_camera_metadata_data_count(m2));
 
-    free_camera_metadata(m);
-    free_camera_metadata(m2);
+    FINISH_USING_CAMERA_METADATA(m);
+    FINISH_USING_CAMERA_METADATA(m2);
 }
 
 TEST(camera_metadata, append_metadata_onespace) {
@@ -706,8 +725,8 @@
         }
     }
 
-    free_camera_metadata(m);
-    free_camera_metadata(m2);
+    FINISH_USING_CAMERA_METADATA(m);
+    FINISH_USING_CAMERA_METADATA(m2);
 }
 
 TEST(camera_metadata, vendor_tags) {
@@ -723,11 +742,13 @@
             FAKEVENDOR_SENSOR_SUPERMODE,
             &superMode, 1);
     EXPECT_EQ(ERROR, result);
+    EXPECT_EQ(OK, validate_camera_metadata_structure(m, NULL));
 
     result = add_camera_metadata_entry(m,
             ANDROID_REQUEST_METADATA_MODE,
             &superMode, 1);
     EXPECT_EQ(OK, result);
+    EXPECT_EQ(OK, validate_camera_metadata_structure(m, NULL));
 
     EXPECT_NULL(get_camera_metadata_section_name(FAKEVENDOR_SENSOR_SUPERMODE));
     EXPECT_NULL(get_camera_metadata_tag_name(FAKEVENDOR_SENSOR_SUPERMODE));
@@ -739,16 +760,19 @@
             FAKEVENDOR_SENSOR_SUPERMODE,
             &superMode, 1);
     EXPECT_EQ(OK, result);
+    EXPECT_EQ(OK, validate_camera_metadata_structure(m, NULL));
 
     result = add_camera_metadata_entry(m,
             ANDROID_REQUEST_METADATA_MODE,
             &superMode, 1);
     EXPECT_EQ(OK, result);
+    EXPECT_EQ(OK, validate_camera_metadata_structure(m, NULL));
 
     result = add_camera_metadata_entry(m,
             FAKEVENDOR_SCALER_END,
             &superMode, 1);
     EXPECT_EQ(ERROR, result);
+    EXPECT_EQ(OK, validate_camera_metadata_structure(m, NULL));
 
     EXPECT_STREQ("com.fakevendor.sensor",
             get_camera_metadata_section_name(FAKEVENDOR_SENSOR_SUPERMODE));
@@ -763,22 +787,35 @@
     EXPECT_EQ(-1, get_camera_metadata_tag_type(FAKEVENDOR_SCALER_END));
 
     set_camera_metadata_vendor_tag_ops(NULL);
+    // TODO: fix vendor ops. Then the below 3 validations should fail.
+    EXPECT_EQ(OK, validate_camera_metadata_structure(m, NULL));
 
     result = add_camera_metadata_entry(m,
             FAKEVENDOR_SENSOR_SUPERMODE,
             &superMode, 1);
     EXPECT_EQ(ERROR, result);
+    EXPECT_EQ(OK, validate_camera_metadata_structure(m, NULL));
 
     result = add_camera_metadata_entry(m,
             ANDROID_REQUEST_METADATA_MODE,
             &superMode, 1);
     EXPECT_EQ(OK, result);
+    EXPECT_EQ(OK, validate_camera_metadata_structure(m, NULL));
 
     EXPECT_NULL(get_camera_metadata_section_name(FAKEVENDOR_SENSOR_SUPERMODE));
     EXPECT_NULL(get_camera_metadata_tag_name(FAKEVENDOR_SENSOR_SUPERMODE));
     EXPECT_EQ(-1, get_camera_metadata_tag_type(FAKEVENDOR_SENSOR_SUPERMODE));
 
-    free_camera_metadata(m);
+    // Remove all vendor entries so validation passes
+    {
+        camera_metadata_ro_entry_t entry;
+        EXPECT_EQ(OK, find_camera_metadata_ro_entry(m,
+                                                    FAKEVENDOR_SENSOR_SUPERMODE,
+                                                    &entry));
+        EXPECT_EQ(OK, delete_camera_metadata_entry(m, entry.index));
+    }
+
+    FINISH_USING_CAMERA_METADATA(m);
 }
 
 TEST(camera_metadata, add_all_tags) {
@@ -863,7 +900,7 @@
         dump_camera_metadata(m, 0, 2);
     }
 
-    free_camera_metadata(m);
+    FINISH_USING_CAMERA_METADATA(m);
 }
 
 TEST(camera_metadata, sort_metadata) {
@@ -977,7 +1014,7 @@
     EXPECT_EQ(focus_distance, *entry.data.f);
 
 
-    free_camera_metadata(m);
+    FINISH_USING_CAMERA_METADATA(m);
 }
 
 TEST(camera_metadata, delete_metadata) {
@@ -1633,8 +1670,9 @@
     result = get_camera_metadata_user_pointer(m2, &ptr);
     EXPECT_NULL(ptr);
 
+    EXPECT_EQ(OK, validate_camera_metadata_structure(m2, &buf_size));
     free(buf);
-    free_camera_metadata(m);
+    FINISH_USING_CAMERA_METADATA(m);
 }
 
 TEST(camera_metadata, memcpy) {
@@ -1648,9 +1686,10 @@
 
     add_test_metadata(m, 5);
 
-    uint8_t *dst = new uint8_t[get_camera_metadata_size(m)];
+    size_t m_size = get_camera_metadata_size(m);
+    uint8_t *dst = new uint8_t[m_size];
 
-    memcpy(dst, m, get_camera_metadata_size(m));
+    memcpy(dst, m, m_size);
 
     camera_metadata_t *m2 = reinterpret_cast<camera_metadata_t*>(dst);
 
@@ -1729,8 +1768,10 @@
     EXPECT_EQ(300, e2.data.i64[0]);
     EXPECT_EQ(200, e2.data.i64[1]);
 
+    EXPECT_EQ(OK, validate_camera_metadata_structure(m2, &m_size));
+
     delete dst;
-    free_camera_metadata(m);
+    FINISH_USING_CAMERA_METADATA(m);
 }
 
 TEST(camera_metadata, data_alignment) {
@@ -1818,7 +1859,7 @@
                     " data_count " << data_count <<
                     " expected alignment was: " << m_type_align[m_type];
 
-                free_camera_metadata(m);
+                FINISH_USING_CAMERA_METADATA(m);
             }
         }
     }