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);
}
}
}