aom_mem: fix overflow in alignment calculation

the calculation of the aligned allocation size could rollover if the
requested size is near SIZE_MAX

+ add a missing overflow check for aom_calloc()

Bug: aomedia:3195
Bug: aomedia:3223
Change-Id: I385de3344dadf9af56f0673b1e1a69b254e73c92
(cherry picked from commit 0929aa0af301888445539626bbc94c09ed0e28b7)
diff --git a/aom_mem/aom_mem.c b/aom_mem/aom_mem.c
index e977b01..f13ee2f 100644
--- a/aom_mem/aom_mem.c
+++ b/aom_mem/aom_mem.c
@@ -10,25 +10,31 @@
  */
 
 #include "aom_mem.h"
-#include <stdio.h>
+#include <assert.h>
 #include <stdlib.h>
 #include <string.h>
 #include "include/aom_mem_intrnl.h"
 #include "aom/aom_integer.h"
 
-#if defined(AOM_MAX_ALLOCABLE_MEMORY)
-// Returns 0 in case of overflow of nmemb * size.
-static int check_size_argument_overflow(uint64_t nmemb, uint64_t size) {
-  const uint64_t total_size = nmemb * size;
-  if (nmemb == 0) return 1;
-  if (size > AOM_MAX_ALLOCABLE_MEMORY / nmemb) return 0;
-  if (total_size != (size_t)total_size) return 0;
-  return 1;
+static size_t GetAllocationPaddingSize(size_t align) {
+  assert(align > 0);
+  assert(align < SIZE_MAX - ADDRESS_STORAGE_SIZE);
+  return align - 1 + ADDRESS_STORAGE_SIZE;
 }
-#endif
 
-static size_t GetAlignedMallocSize(size_t size, size_t align) {
-  return size + align - 1 + ADDRESS_STORAGE_SIZE;
+// Returns 0 in case of overflow of nmemb * size.
+static int check_size_argument_overflow(size_t nmemb, size_t size,
+                                        size_t align) {
+  if (nmemb == 0) return 1;
+  const size_t alloc_padding = GetAllocationPaddingSize(align);
+#if defined(AOM_MAX_ALLOCABLE_MEMORY)
+  assert(AOM_MAX_ALLOCABLE_MEMORY >= alloc_padding);
+  assert(AOM_MAX_ALLOCABLE_MEMORY <= SIZE_MAX);
+  if (size > (AOM_MAX_ALLOCABLE_MEMORY - alloc_padding) / nmemb) return 0;
+#else
+  if (size > (SIZE_MAX - alloc_padding) / nmemb) return 0;
+#endif
+  return 1;
 }
 
 static size_t *GetMallocAddressLocation(void *const mem) {
@@ -48,10 +54,8 @@
 
 void *aom_memalign(size_t align, size_t size) {
   void *x = NULL;
-  const size_t aligned_size = GetAlignedMallocSize(size, align);
-#if defined(AOM_MAX_ALLOCABLE_MEMORY)
-  if (!check_size_argument_overflow(1, aligned_size)) return NULL;
-#endif
+  if (!check_size_argument_overflow(1, size, align)) return NULL;
+  const size_t aligned_size = size + GetAllocationPaddingSize(align);
   void *const addr = malloc(aligned_size);
   if (addr) {
     x = aom_align_addr((unsigned char *)addr + ADDRESS_STORAGE_SIZE, align);
@@ -63,6 +67,7 @@
 void *aom_malloc(size_t size) { return aom_memalign(DEFAULT_ALIGNMENT, size); }
 
 void *aom_calloc(size_t num, size_t size) {
+  if (!check_size_argument_overflow(num, size, DEFAULT_ALIGNMENT)) return NULL;
   const size_t total_size = num * size;
   void *const x = aom_malloc(total_size);
   if (x) memset(x, 0, total_size);
diff --git a/test/aom_mem_test.cc b/test/aom_mem_test.cc
new file mode 100644
index 0000000..849ba64
--- /dev/null
+++ b/test/aom_mem_test.cc
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2021, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+
+#include "aom_mem/aom_mem.h"
+
+#include <cstdio>
+#include <cstddef>
+
+#include "third_party/googletest/src/googletest/include/gtest/gtest.h"
+
+TEST(AomMemTest, Overflow) {
+  // Allocations are aligned > 1 so SIZE_MAX should always fail.
+  ASSERT_EQ(aom_malloc(SIZE_MAX), nullptr);
+  ASSERT_EQ(aom_calloc(1, SIZE_MAX), nullptr);
+  ASSERT_EQ(aom_calloc(32, SIZE_MAX / 32), nullptr);
+  ASSERT_EQ(aom_calloc(SIZE_MAX, SIZE_MAX), nullptr);
+  ASSERT_EQ(aom_memalign(1, SIZE_MAX), nullptr);
+  ASSERT_EQ(aom_memalign(64, SIZE_MAX), nullptr);
+  ASSERT_EQ(aom_memalign(64, SIZE_MAX - 64), nullptr);
+  ASSERT_EQ(aom_memalign(64, SIZE_MAX - 64 - sizeof(size_t) + 2), nullptr);
+}
+
+TEST(AomMemTest, NullParams) {
+  ASSERT_EQ(aom_memset16(nullptr, 0, 0), nullptr);
+  aom_free(nullptr);
+}
diff --git a/test/test.cmake b/test/test.cmake
index 31ae14b..183aff2 100644
--- a/test/test.cmake
+++ b/test/test.cmake
@@ -128,6 +128,7 @@
 
 if(NOT BUILD_SHARED_LIBS)
   list(APPEND AOM_UNIT_TEST_COMMON_SOURCES
+              "${AOM_ROOT}/test/aom_mem_test.cc"
               "${AOM_ROOT}/test/av1_common_int_test.cc"
               "${AOM_ROOT}/test/cdef_test.cc"
               "${AOM_ROOT}/test/cfl_test.cc"