diff --git a/cores/esp8266/heap.cpp b/cores/esp8266/heap.cpp
index 896002d2b9..63ff49579f 100644
--- a/cores/esp8266/heap.cpp
+++ b/cores/esp8266/heap.cpp
@@ -5,6 +5,7 @@
 
 #include <stdlib.h>
 #include "umm_malloc/umm_malloc.h"
+extern "C" size_t umm_umul_sat(const size_t a, const size_t b);;
 
 // Need FORCE_ALWAYS_INLINE to put HeapSelect class constructor/deconstructor in IRAM
 #define FORCE_ALWAYS_INLINE_HEAP_SELECT
@@ -153,7 +154,7 @@ void* _calloc_r(struct _reent* unused, size_t count, size_t size)
 {
     (void) unused;
     void *ret = calloc(count, size);
-    PTR_CHECK__LOG_LAST_FAIL(ret, count * size);
+    PTR_CHECK__LOG_LAST_FAIL(ret, umm_umul_sat(count, size));
     return ret;
 }
 
@@ -247,8 +248,11 @@ void* IRAM_ATTR calloc(size_t count, size_t size)
     INTEGRITY_CHECK__ABORT();
     POISON_CHECK__ABORT();
     void* ret = UMM_CALLOC(count, size);
-    PTR_CHECK__LOG_LAST_FAIL(ret, count * size);
-    OOM_CHECK__PRINT_OOM(ret, size);
+    #if defined(DEBUG_ESP_OOM)
+    size_t total_size = umm_umul_sat(count, size);// For logging purposes
+    #endif
+    PTR_CHECK__LOG_LAST_FAIL(ret, total_size);
+    OOM_CHECK__PRINT_OOM(ret, total_size);
     return ret;
 }
 
@@ -287,8 +291,11 @@ void* IRAM_ATTR heap_pvPortCalloc(size_t count, size_t size, const char* file, i
     INTEGRITY_CHECK__PANIC_FL(file, line);
     POISON_CHECK__PANIC_FL(file, line);
     void* ret = UMM_CALLOC(count, size);
-    PTR_CHECK__LOG_LAST_FAIL_FL(ret, count * size, file, line);
-    OOM_CHECK__PRINT_LOC(ret, size, file, line);
+    #if defined(DEBUG_ESP_OOM)
+    size_t total_size = umm_umul_sat(count, size);
+    #endif
+    PTR_CHECK__LOG_LAST_FAIL_FL(ret, total_size, file, line);
+    OOM_CHECK__PRINT_LOC(ret, total_size, file, line);
     return ret;
 }
 
diff --git a/cores/esp8266/umm_malloc/umm_local.c b/cores/esp8266/umm_malloc/umm_local.c
index 901fdfc63e..24ddf8ce06 100644
--- a/cores/esp8266/umm_malloc/umm_local.c
+++ b/cores/esp8266/umm_malloc/umm_local.c
@@ -137,7 +137,7 @@ void *umm_poison_realloc_fl(void *ptr, size_t size, const char *file, int line)
 
     ptr = get_unpoisoned_check_neighbors(ptr, file, line);
 
-    size += poison_size(size);
+    add_poison_size(&size);
     ret = umm_realloc(ptr, size);
 
     ret = get_poisoned(ret, size);
@@ -296,4 +296,32 @@ size_t ICACHE_FLASH_ATTR umm_get_free_null_count(void) {
 }
 #endif // UMM_STATS_FULL
 
+#if defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE)
+/*
+ * Saturated unsigned add
+ * Poison added to allocation size requires overflow protection.
+ */
+static size_t umm_uadd_sat(const size_t a, const size_t b) {
+    size_t r = a + b;
+    if (r < a) {
+        return SIZE_MAX;
+    }
+    return r;
+}
+#endif
+
+/*
+ * Use platform-specific functions to protect against unsigned overflow/wrap by
+ * implementing saturated unsigned multiply.
+ * The function umm_calloc requires a saturated multiply function.
+ */
+size_t umm_umul_sat(const size_t a, const size_t b) {
+    size_t r;
+    if (__builtin_mul_overflow(a, b, &r)) {
+        return SIZE_MAX;
+    }
+    return r;
+}
+
+
 #endif // BUILD_UMM_MALLOC_C
diff --git a/cores/esp8266/umm_malloc/umm_local.h b/cores/esp8266/umm_malloc/umm_local.h
index a649780441..c5dcffd73c 100644
--- a/cores/esp8266/umm_malloc/umm_local.h
+++ b/cores/esp8266/umm_malloc/umm_local.h
@@ -15,6 +15,14 @@
 #define memset ets_memset
 
 
+/*
+ * Saturated unsigned add and unsigned multiply
+ */
+size_t umm_umul_sat(const size_t a, const size_t b);  // share with heap.cpp
+#if defined(UMM_POISON_CHECK) || defined(UMM_POISON_CHECK_LITE)
+static size_t umm_uadd_sat(const size_t a, const size_t b);
+#endif
+
 /*
  * This redefines DBGLOG_FORCE defined in dbglog/dbglog.h
  * Just for printing from umm_info() which is assumed to always be called from
diff --git a/cores/esp8266/umm_malloc/umm_malloc.cpp b/cores/esp8266/umm_malloc/umm_malloc.cpp
index 694478cd2b..d3f4c92c66 100644
--- a/cores/esp8266/umm_malloc/umm_malloc.cpp
+++ b/cores/esp8266/umm_malloc/umm_malloc.cpp
@@ -1214,10 +1214,14 @@ void *umm_realloc(void *ptr, size_t size) {
 void *umm_calloc(size_t num, size_t item_size) {
     void *ret;
 
-    ret = umm_malloc((size_t)(item_size * num));
+    // Use saturated multiply.
+    // Rely on umm_malloc to supply the fail response as needed.
+    size_t size = umm_umul_sat(num, item_size);
+
+    ret = umm_malloc(size);
 
     if (ret) {
-        memset(ret, 0x00, (size_t)(item_size * num));
+        memset(ret, 0x00, size);
     }
 
     return ret;
diff --git a/cores/esp8266/umm_malloc/umm_poison.c b/cores/esp8266/umm_malloc/umm_poison.c
index 1d5e7651de..a7a3fa2179 100644
--- a/cores/esp8266/umm_malloc/umm_poison.c
+++ b/cores/esp8266/umm_malloc/umm_poison.c
@@ -8,15 +8,19 @@
 #include <stddef.h>
 #include <stdbool.h>
 
+#define UMM_POISON_BLOCK_SIZE (UMM_POISON_SIZE_BEFORE + sizeof(UMM_POISONED_BLOCK_LEN_TYPE) + UMM_POISON_SIZE_AFTER)
+
 /*
- * Yields a size of the poison for the block of size `s`.
+ * Yields the total size of a poison block of size `s`.
  * If `s` is 0, returns 0.
+ * If result overflows/wraps, return saturation value.
  */
-static size_t poison_size(size_t s) {
-    return s ? (UMM_POISON_SIZE_BEFORE +
-        sizeof(UMM_POISONED_BLOCK_LEN_TYPE) +
-        UMM_POISON_SIZE_AFTER)
-             : 0;
+static void add_poison_size(size_t* s) {
+    if (*s == 0) {
+        return;
+    }
+
+    *s = umm_uadd_sat(*s, UMM_POISON_BLOCK_SIZE);
 }
 
 /*
@@ -158,7 +162,7 @@ static void *get_unpoisoned(void *vptr) {
 void *umm_poison_malloc(size_t size) {
     void *ret;
 
-    size += poison_size(size);
+    add_poison_size(&size);
 
     ret = umm_malloc(size);
 
@@ -171,9 +175,12 @@ void *umm_poison_malloc(size_t size) {
 
 void *umm_poison_calloc(size_t num, size_t item_size) {
     void *ret;
-    size_t size = item_size * num;
 
-    size += poison_size(size);
+    // Use saturated multiply.
+    // Rely on umm_malloc to supply the fail response as needed.
+    size_t size = umm_umul_sat(num, item_size);
+
+    add_poison_size(&size);
 
     ret = umm_malloc(size);
 
@@ -193,7 +200,7 @@ void *umm_poison_realloc(void *ptr, size_t size) {
 
     ptr = get_unpoisoned(ptr);
 
-    size += poison_size(size);
+    add_poison_size(&size);
     ret = umm_realloc(ptr, size);
 
     ret = get_poisoned(ret, size);