From de8f73b0ed435944c46429bd8679a2e7bf455ff2 Mon Sep 17 00:00:00 2001
From: "Christian W. Zuckschwerdt" <christian@zuckschwerdt.org>
Date: Thu, 17 Oct 2019 08:32:56 +0200
Subject: [PATCH] Add warnings on alloc failure, enforce alloc check style

---
 include/fatal.h          | 38 ++++++++++++++++++
 src/am_analyze.c         |  5 ++-
 src/confparse.c          |  6 ++-
 src/data.c               | 83 ++++++++++++++++++++++++++--------------
 src/decoder_util.c       | 77 ++++++++++++++++++++-----------------
 src/devices/fineoffset.c |  5 ++-
 src/devices/flex.c       | 34 +++++++++++-----
 src/list.c               |  4 +-
 src/output_mqtt.c        | 48 ++++++++++++-----------
 src/pulse_detect.c       |  7 +++-
 src/r_api.c              | 32 +++++++---------
 src/r_util.c             |  8 ++--
 src/rtl_433.c            | 10 ++---
 src/samp_grab.c          | 10 +++--
 src/sdr.c                | 33 ++++++++++------
 tests/style-check.c      | 18 ++++++++-
 16 files changed, 271 insertions(+), 147 deletions(-)
 create mode 100644 include/fatal.h

diff --git a/include/fatal.h b/include/fatal.h
new file mode 100644
index 00000000..bbfb3f9a
--- /dev/null
+++ b/include/fatal.h
@@ -0,0 +1,38 @@
+/** @file
+    Fatal abort and warning macros for allocs.
+
+    Copyright (C) 2019 Christian W. Zuckschwerdt <zany@triq.net>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+*/
+
+#ifndef INCLUDE_FATAL_H_
+#define INCLUDE_FATAL_H_
+
+#define STR(x) #x
+#define STRINGIFY(x) STR(x)
+#define FILE_LINE __FILE__ ":" STRINGIFY(__LINE__)
+#define FATAL(what) do { fprintf(stderr, "FATAL: " what " from " FILE_LINE "\n"); exit(1); } while (0)
+#define FATAL_MALLOC(what) FATAL("low memory? malloc() failed in " what)
+#define FATAL_CALLOC(what) FATAL("low memory? calloc() failed in " what)
+#define FATAL_REALLOC(what) FATAL("low memory? realloc() failed in " what)
+#define FATAL_STRDUP(what) FATAL("low memory? strdup() failed in " what)
+#define WARN(what) fprintf(stderr, "WARNING: " what " from " FILE_LINE "\n")
+#define WARN_MALLOC(what) WARN("low memory? malloc() failed in " what)
+#define WARN_CALLOC(what) WARN("low memory? calloc() failed in " what)
+#define WARN_REALLOC(what) WARN("low memory? realloc() failed in " what)
+#define WARN_STRDUP(what) WARN("low memory? strdup() failed in " what)
+
+/*
+    Use like this:
+
+    char *buf = malloc(size);
+    if (!buf)
+        FATAL_MALLOC("my_func()");
+
+*/
+
+#endif /* INCLUDE_FATAL_H_ */
diff --git a/src/am_analyze.c b/src/am_analyze.c
index 21353011..74aec0de 100644
--- a/src/am_analyze.c
+++ b/src/am_analyze.c
@@ -15,6 +15,7 @@
 
 #include "bitbuffer.h"
 #include "samp_grab.h"
+#include "fatal.h"
 
 #include "am_analyze.h"
 
@@ -25,7 +26,9 @@ am_analyze_t *am_analyze_create(void)
 {
     am_analyze_t *a;
     a = calloc(1, sizeof(am_analyze_t));
-    return a; // NOTE: might silently return NULL on alloc failure.
+    if (!a)
+        WARN_CALLOC("am_analyze_create()");
+    return a; // NOTE: returns NULL on alloc failure.
 }
 
 void am_analyze_free(am_analyze_t *a)
diff --git a/src/confparse.c b/src/confparse.c
index 535b07ae..d4d03760 100644
--- a/src/confparse.c
+++ b/src/confparse.c
@@ -20,6 +20,7 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include "confparse.h"
+#include "fatal.h"
 
 #ifdef _WIN32
 #include <io.h>
@@ -59,8 +60,9 @@ char *readconf(char const *path)
         return NULL;
     }
 
-    conf = (char *)malloc(file_size + 1);
-    if (conf == NULL) {
+    conf = malloc(file_size + 1);
+    if (!conf) {
+        WARN_MALLOC("readconf()");
         fprintf(stderr, "Failed to allocate memory for \"%s\"\n", path);
         fclose(fp);
         return NULL;
diff --git a/src/data.c b/src/data.c
index 759170d4..8b055be0 100644
--- a/src/data.c
+++ b/src/data.c
@@ -52,6 +52,7 @@
 
 #include "term_ctl.h"
 #include "abuf.h"
+#include "fatal.h"
 
 #include "data.h"
 
@@ -163,18 +164,24 @@ static bool import_values(void *dst, void *src, int num_values, data_type_t type
 data_array_t *data_array(int num_values, data_type_t type, void *values)
 {
     data_array_t *array = calloc(1, sizeof(data_array_t));
-    if (array) {
-        int element_size = dmt[type].array_element_size;
-        array->values = calloc(num_values, element_size);
-        if (!array->values)
-            goto alloc_error;
-        if (!import_values(array->values, values, num_values, type))
-            goto alloc_error;
-
-        array->num_values = num_values;
-        array->type = type;
+    if (!array) {
+        WARN_CALLOC("data_array()");
+        return NULL; // NOTE: returns NULL on alloc failure.
     }
-    return array; // NOTE: might silently return NULL on alloc failure.
+
+    int element_size = dmt[type].array_element_size;
+    array->values    = calloc(num_values, element_size);
+    if (!array->values) {
+        WARN_CALLOC("data_array()");
+        goto alloc_error;
+    }
+    if (!import_values(array->values, values, num_values, type))
+        goto alloc_error;
+
+    array->num_values = num_values;
+    array->type       = type;
+
+    return array;
 
 alloc_error:
     if (array)
@@ -198,8 +205,10 @@ static data_t *vdata_make(data_t *first, const char *key, const char *pretty_key
         switch (type) {
         case DATA_FORMAT:
             format = strdup(va_arg(ap, char *));
-            if (!format)
+            if (!format) {
+                WARN_STRDUP("vdata_make()");
                 goto alloc_error;
+            }
             type = va_arg(ap, data_type_t);
             continue;
             break;
@@ -211,16 +220,22 @@ static data_t *vdata_make(data_t *first, const char *key, const char *pretty_key
             break;
         case DATA_INT:
             value = malloc(sizeof(int));
-            if (value)
+            if (!value)
+                WARN_MALLOC("vdata_make()");
+            else // NOTE: skipped on alloc failure
                 *(int *)value = va_arg(ap, int);
             break;
         case DATA_DOUBLE:
             value = malloc(sizeof(double));
-            if (value)
+            if (!value)
+                WARN_MALLOC("vdata_make()");
+            else // NOTE: skipped on alloc failure
                 *(double *)value = va_arg(ap, double);
             break;
         case DATA_STRING:
             value = strdup(va_arg(ap, char *));
+            if (!value)
+                WARN_STRDUP("vdata_make()");
             break;
         case DATA_ARRAY:
             value = va_arg(ap, data_t *);
@@ -234,17 +249,23 @@ static data_t *vdata_make(data_t *first, const char *key, const char *pretty_key
             goto alloc_error;
 
         current = calloc(1, sizeof(*current));
-        if (!current)
+        if (!current) {
+            WARN_CALLOC("vdata_make()");
             goto alloc_error;
+        }
         if (prev)
             prev->next = current;
 
         current->key = strdup(key);
-        if (!current->key)
+        if (!current->key) {
+            WARN_STRDUP("vdata_make()");
             goto alloc_error;
+        }
         current->pretty_key = strdup(pretty_key ? pretty_key : key);
-        if (!current->pretty_key)
+        if (!current->pretty_key) {
+            WARN_STRDUP("vdata_make()");
             goto alloc_error;
+        }
         current->type = type;
         current->format = format;
         current->value = value;
@@ -484,8 +505,8 @@ struct data_output *data_output_json_create(FILE *file)
 {
     data_output_t *output = calloc(1, sizeof(data_output_t));
     if (!output) {
-        fprintf(stderr, "calloc() failed");
-        return NULL;
+        WARN_CALLOC("data_output_json_create()");
+        return NULL; // NOTE: returns NULL on alloc failure.
     }
 
     output->print_data   = print_json_data;
@@ -670,8 +691,8 @@ struct data_output *data_output_kv_create(FILE *file)
 {
     data_output_kv_t *kv = calloc(1, sizeof(data_output_kv_t));
     if (!kv) {
-        fprintf(stderr, "calloc() failed");
-        return NULL;
+        WARN_CALLOC("data_output_kv_create()");
+        return NULL; // NOTE: returns NULL on alloc failure.
     }
 
     kv->output.print_data   = print_kv_data;
@@ -766,8 +787,10 @@ static void data_output_csv_start(struct data_output *output, const char **field
     csv->separator = ",";
 
     allowed = calloc(num_fields, sizeof(const char *));
-    if (!allowed)
+    if (!allowed) {
+        WARN_CALLOC("data_output_csv_start()");
         goto alloc_error;
+    }
     memcpy(allowed, fields, sizeof(const char *) * num_fields);
 
     qsort(allowed, num_fields, sizeof(char *), compare_strings);
@@ -789,12 +812,16 @@ static void data_output_csv_start(struct data_output *output, const char **field
     num_unique_fields = i;
 
     csv->fields = calloc(num_unique_fields + 1, sizeof(const char *));
-    if (!csv->fields)
+    if (!csv->fields) {
+        WARN_CALLOC("data_output_csv_start()");
         goto alloc_error;
+    }
 
     use_count = calloc(num_unique_fields, sizeof(*use_count));
-    if (!use_count)
+    if (!use_count) {
+        WARN_CALLOC("data_output_csv_start()");
         goto alloc_error;
+    }
 
     for (i = 0; i < num_fields; ++i) {
         const char **field = bsearch(&fields[i], allowed, num_unique_fields, sizeof(const char *),
@@ -847,8 +874,8 @@ struct data_output *data_output_csv_create(FILE *file)
 {
     data_output_csv_t *csv = calloc(1, sizeof(data_output_csv_t));
     if (!csv) {
-        fprintf(stderr, "calloc() failed");
-        return NULL;
+        WARN_CALLOC("data_output_csv_create()");
+        return NULL; // NOTE: returns NULL on alloc failure.
     }
 
     csv->output.print_data   = print_csv_data;
@@ -1102,8 +1129,8 @@ struct data_output *data_output_syslog_create(const char *host, const char *port
 {
     data_output_syslog_t *syslog = calloc(1, sizeof(data_output_syslog_t));
     if (!syslog) {
-        fprintf(stderr, "calloc() failed");
-        return NULL;
+        WARN_CALLOC("data_output_syslog_create()");
+        return NULL; // NOTE: returns NULL on alloc failure.
     }
 #ifdef _WIN32
     WSADATA wsa;
diff --git a/src/decoder_util.c b/src/decoder_util.c
index 51dde8fa..a74013d9 100644
--- a/src/decoder_util.c
+++ b/src/decoder_util.c
@@ -14,16 +14,21 @@
 #include "data.h"
 #include "util.h"
 #include "decoder_util.h"
+#include "fatal.h"
 
 // create decoder functions
 
 r_device *create_device(r_device *dev_template)
 {
     r_device *r_dev = malloc(sizeof (*r_dev));
-    if (r_dev && dev_template)
+    if (!r_dev) {
+        WARN_MALLOC("create_device()");
+        return NULL; // NOTE: returns NULL on alloc failure.
+    }
+    if (dev_template)
         *r_dev = *dev_template; // copy
 
-    return r_dev; // NOTE: might silently return NULL on alloc failure.
+    return r_dev;
 }
 
 // variadic print functions
@@ -121,13 +126,39 @@ void decoder_output_message(r_device *decoder, char const *msg)
     decoder_output_data(decoder, data);
 }
 
-static char *bitrow_print_bits(bitrow_t const bitrow, unsigned bit_len)
+static char *bitrow_asprint_code(bitrow_t const bitrow, unsigned bit_len)
+{
+    char *row_code;
+    char row_bytes[BITBUF_COLS * 2 + 1];
+
+    row_bytes[0] = '\0';
+    // print byte-wide
+    for (unsigned col = 0; col < (unsigned)(bit_len + 7) / 8; ++col) {
+        sprintf(&row_bytes[2 * col], "%02x", bitrow[col]);
+    }
+    // remove last nibble if needed
+    row_bytes[2 * (bit_len + 3) / 8] = '\0';
+
+    // a simple bitrow representation
+    row_code = malloc(8 + bit_len / 4 + 1); // "{nnnn}..\0"
+    if (!row_code) {
+        WARN_MALLOC("decoder_output_bitbuffer()");
+        return NULL; // NOTE: returns NULL on alloc failure.
+    }
+    sprintf(row_code, "{%d}%s", bit_len, row_bytes);
+
+    return row_code;
+}
+
+static char *bitrow_asprint_bits(bitrow_t const bitrow, unsigned bit_len)
 {
     char *row_bits, *p;
 
     p = row_bits = malloc(bit_len + bit_len / 4 + 1); // "1..\0" (1 space per nibble)
-    if (!row_bits)
-        return NULL; // NOTE: might silently return NULL on alloc failure.
+    if (!row_bits) {
+        WARN_MALLOC("bitrow_asprint_bits()");
+        return NULL; // NOTE: returns NULL on alloc failure.
+    }
 
     // print bit-wide with a space every nibble
     for (unsigned i = 0; i < bit_len; ++i) {
@@ -151,25 +182,13 @@ void decoder_output_bitbuffer(r_device *decoder, bitbuffer_t const *bitbuffer, c
     data_t *data;
     char *row_codes[BITBUF_ROWS];
     char *row_bits[BITBUF_ROWS] = {0};
-    char row_bytes[BITBUF_COLS * 2 + 1];
     unsigned i;
 
     for (i = 0; i < bitbuffer->num_rows; i++) {
-        row_bytes[0] = '\0';
-        // print byte-wide
-        for (unsigned col = 0; col < (unsigned)(bitbuffer->bits_per_row[i] + 7) / 8; ++col) {
-            sprintf(&row_bytes[2 * col], "%02x", bitbuffer->bb[i][col]);
-        }
-        // remove last nibble if needed
-        row_bytes[2 * (bitbuffer->bits_per_row[i] + 3) / 8] = '\0';
-
-        // a simpler representation for csv output
-        row_codes[i] = malloc(8 + BITBUF_COLS * 2 + 1); // "{nnn}..\0"
-        if (row_codes[i]) // NOTE: might silently skip on alloc failure.
-            sprintf(row_codes[i], "{%d}%s", bitbuffer->bits_per_row[i], row_bytes);
+        row_codes[i] = bitrow_asprint_code(bitbuffer->bb[i], bitbuffer->bits_per_row[i]);
 
         if (decoder->verbose_bits) {
-            row_bits[i] = bitrow_print_bits(bitbuffer->bb[i], bitbuffer->bits_per_row[i]);
+            row_bits[i] = bitrow_asprint_bits(bitbuffer->bb[i], bitbuffer->bits_per_row[i]);
         }
     }
 
@@ -216,9 +235,7 @@ void decoder_output_bitbuffer_array(r_device *decoder, bitbuffer_t const *bitbuf
                 NULL);
 
         // a simpler representation for csv output
-        row_codes[i] = malloc(8 + BITBUF_COLS * 2 + 1); // "{nnn}..\0"
-        if (row_codes[i]) // NOTE: might silently skip on alloc failure.
-            sprintf(row_codes[i], "{%d}%s", bitbuffer->bits_per_row[i], row_bytes);
+        row_codes[i] = bitrow_asprint_code(bitbuffer->bb[i], bitbuffer->bits_per_row[i]);
     }
 
     data = data_make(
@@ -239,20 +256,8 @@ void decoder_output_bitrow(r_device *decoder, bitrow_t const bitrow, unsigned bi
     data_t *data;
     char *row_code;
     char *row_bits = NULL;
-    char row_bytes[BITBUF_COLS * 2 + 1];
 
-    row_bytes[0] = '\0';
-    // print byte-wide
-    for (unsigned col = 0; col < (bit_len + 7) / 8; ++col) {
-        sprintf(&row_bytes[2 * col], "%02x", bitrow[col]);
-    }
-    // remove last nibble if needed
-    row_bytes[2 * (bit_len + 3) / 8] = '\0';
-
-    // a simpler representation for csv output
-    row_code = malloc(8 + BITBUF_COLS * 2 + 1); // "{nnn}..\0"
-    if (row_code) // NOTE: might silently skip on alloc failure.
-        sprintf(row_code, "{%d}%s", bit_len, row_bytes);
+    row_code = bitrow_asprint_code(bitrow, bit_len);
 
     data = data_make(
             "msg", "", DATA_STRING, msg,
@@ -260,7 +265,7 @@ void decoder_output_bitrow(r_device *decoder, bitrow_t const bitrow, unsigned bi
             NULL);
 
     if (decoder->verbose_bits) {
-        row_bits = bitrow_print_bits(bitrow, bit_len);
+        row_bits = bitrow_asprint_bits(bitrow, bit_len);
         data_append(data,
                 "bits", "", DATA_STRING, row_bits,
                 NULL);
diff --git a/src/devices/fineoffset.c b/src/devices/fineoffset.c
index 5cfa878e..47e15488 100644
--- a/src/devices/fineoffset.c
+++ b/src/devices/fineoffset.c
@@ -11,6 +11,7 @@
 */
 
 #include "decoder.h"
+#include "fatal.h"
 #include <stdlib.h>
 
 r_device fineoffset_WH2;
@@ -19,14 +20,14 @@ static r_device *fineoffset_WH2_create(char *arg)
 {
     r_device *r_dev = create_device(&fineoffset_WH2);
     if (!r_dev) {
-        fprintf(stderr, "create_device() failed");
+        fprintf(stderr, "fineoffset_WH2_create() failed");
         return NULL; // NOTE: returns NULL on alloc failure.
     }
 
     if (arg && !strcmp(arg, "no-wh5")) {
         int *quirk = malloc(sizeof (*quirk));
         if (!quirk) {
-            fprintf(stderr, "malloc() failed");
+            WARN_MALLOC("fineoffset_WH2_create()");
             free(r_dev);
             return NULL; // NOTE: returns NULL on alloc failure.
         }
diff --git a/src/devices/flex.c b/src/devices/flex.c
index 84ce0d21..dd813e67 100644
--- a/src/devices/flex.c
+++ b/src/devices/flex.c
@@ -10,6 +10,7 @@
 
 #include "decoder.h"
 #include "optparse.h"
+#include "fatal.h"
 #include <stdlib.h>
 
 static inline int bit(const uint8_t *bytes, unsigned bit)
@@ -263,8 +264,10 @@ static int flex_callback(r_device *decoder, bitbuffer_t *bitbuffer)
         render_getters(row_data[i], bitbuffer->bb[i], params);
 
         // a simpler representation for csv output
-        row_codes[i] = malloc(8 + BITBUF_COLS * 2 + 1); // "{nnn}..\0"
-        if (row_codes[i]) // NOTE: might silently skip on alloc failure.
+        row_codes[i] = malloc(8 + bitbuffer->bits_per_row[i] / 4 + 1); // "{nnnn}..\0"
+        if (!row_codes[i])
+            WARN_MALLOC("flex_decode()");
+        else // NOTE: skipped on alloc failure.
             sprintf(row_codes[i], "{%d}%s", bitbuffer->bits_per_row[i], row_bytes);
     }
     /* clang-format off */
@@ -427,7 +430,9 @@ const char *parse_map(const char *arg, struct flex_get *getter)
         const char *e = c;
         while (*e != ' ' && *e != ']') e++;
         val = malloc(e - c + 1);
-        if (val) { // NOTE: might silently skip on alloc failure.
+        if (!val)
+            WARN_MALLOC("parse_map()");
+        else { // NOTE: skipped on alloc failure.
             memcpy(val, c, e - c);
             val[e - c] = '\0';
         }
@@ -458,8 +463,11 @@ static void parse_getter(const char *arg, struct flex_get *getter)
             getter->bit_count = parse_bits(arg, bitrow);
             getter->mask = extract_number(bitrow, 0, getter->bit_count);
         }
-        else
+        else {
             getter->name = strdup(arg);
+            if (!getter->name)
+                FATAL_STRDUP("parse_getter()");
+        }
         arg = p;
     }
     if (!getter->name) {
@@ -480,18 +488,23 @@ r_device *flex_create_device(char *spec)
     }
 
     struct flex_params *params = calloc(1, sizeof(*params));
-    if (!params)
-        return NULL; // NOTE: might silently return NULL on alloc failure.
+    if (!params) {
+        WARN_CALLOC("flex_create_device()");
+        return NULL; // NOTE: returns NULL on alloc failure.
+    }
     r_device *dev = calloc(1, sizeof(*dev));
     if (!dev) {
+        WARN_CALLOC("flex_create_device()");
         free(params);
-        return NULL; // NOTE: might silently return NULL on alloc failure.
+        return NULL; // NOTE: returns NULL on alloc failure.
     }
     dev->decode_ctx = params;
     char *c, *o;
     int get_count = 0;
 
     spec = strdup(spec);
+    if (!spec)
+        FATAL_STRDUP("flex_create_device()");
     // locate optional args and terminate mandatory args
     char *args = strchr(spec, ',');
     if (args) {
@@ -508,10 +521,13 @@ r_device *flex_create_device(char *spec)
     if (!strncasecmp(c, "name=", 5))
         c += 5;
     params->name  = strdup(c);
+    if (!params->name)
+        FATAL_STRDUP("flex_create_device()");
     int name_size = strlen(c) + 27;
     dev->name = malloc(name_size);
-    if (dev->name) // NOTE: might silently skip on alloc failure.
-        snprintf(dev->name, name_size, "General purpose decoder '%s'", c);
+    if (!dev->name)
+        FATAL_MALLOC("flex_create_device()");
+    snprintf(dev->name, name_size, "General purpose decoder '%s'", c);
 
     c = strtok(NULL, ":");
     if (c != NULL) {
diff --git a/src/list.c b/src/list.c
index 6ab1d231..03701112 100644
--- a/src/list.c
+++ b/src/list.c
@@ -10,6 +10,7 @@
 */
 
 #include "list.h"
+#include "fatal.h"
 #include <stdlib.h>
 #include <stdio.h>
 
@@ -18,8 +19,7 @@ void list_ensure_size(list_t *list, size_t min_size)
     if (!list->elems || list->size < min_size) {
         list->elems = realloc(list->elems, min_size * sizeof(*list->elems));
         if (!list->elems) {
-            fprintf(stderr, "realloc() failed");
-            exit(1); // NOTE: abort on alloc failure.
+            FATAL_REALLOC("list_ensure_size()");
         }
         list->size  = min_size;
 
diff --git a/src/output_mqtt.c b/src/output_mqtt.c
index 4e36a423..d5a4ab04 100644
--- a/src/output_mqtt.c
+++ b/src/output_mqtt.c
@@ -13,6 +13,7 @@
 #include "output_mqtt.h"
 #include "optparse.h"
 #include "util.h"
+#include "fatal.h"
 
 #include <stdlib.h>
 #include <stdio.h>
@@ -95,16 +96,13 @@ static void mqtt_client_event(struct mg_connection *nc, int ev, void *ev_data)
 static struct mg_mgr *mqtt_client_init(char const *host, char const *port, char const *user, char const *pass, char const *client_id, int retain)
 {
     struct mg_mgr *mgr = calloc(1, sizeof(*mgr));
-    if (!mgr) {
-        fprintf(stderr, "calloc() failed in %s() %s:%d\n", __func__, __FILE__, __LINE__);
-        exit(1);
-    }
+    if (!mgr)
+        FATAL_CALLOC("mqtt_client_init()");
 
     mqtt_client_t *ctx = calloc(1, sizeof(*ctx));
-    if (!ctx) {
-        fprintf(stderr, "calloc() failed in %s() %s:%d\n", __func__, __FILE__, __LINE__);
-        exit(1);
-    }
+    if (!ctx)
+        FATAL_CALLOC("mqtt_client_init()");
+
     ctx->opts.user_name = user;
     ctx->opts.password  = pass;
     ctx->publish_flags  = MG_MQTT_QOS(0) | (retain ? MG_MQTT_RETAIN : 0);
@@ -331,8 +329,8 @@ static void print_mqtt_data(data_output_t *output, data_t *data, char *format)
                 size_t message_size = 20000; // state message need a large buffer
                 char *message       = malloc(message_size);
                 if (!message) {
-                    fprintf(stderr, "malloc() failed\n");
-                    return; // NOTE: skip on alloc failure.
+                    WARN_MALLOC("print_mqtt_data()");
+                    return; // NOTE: skip output on alloc failure.
                 }
                 data_print_jsons(data, message, message_size);
                 expand_topic(mqtt->topic, mqtt->states, data, mqtt->hostname);
@@ -440,24 +438,30 @@ static void data_output_mqtt_free(data_output_t *output)
 
 static char *mqtt_topic_default(char const *topic, char const *base, char const *suffix)
 {
-    if (topic)
-        return strdup(topic);
+    char const *p;
+    if (topic) {
+        p = topic;
+    }
+    else if (!base) {
+        p = suffix;
+    }
+    else {
+        char path[256];
+        snprintf(path, sizeof(path), "%s/%s", base, suffix);
+        p = path;
+    }
 
-    if (!base)
-        return strdup(suffix);
-
-    char path[256];
-    snprintf(path, sizeof(path), "%s/%s", base, suffix);
-    return strdup(path);
+    char *ret = strdup(p);
+    if (!ret)
+        WARN_STRDUP("mqtt_topic_default()");
+    return ret;
 }
 
 struct data_output *data_output_mqtt_create(char const *host, char const *port, char *opts, char const *dev_hint)
 {
     data_output_mqtt_t *mqtt = calloc(1, sizeof(data_output_mqtt_t));
-    if (!mqtt) {
-        fprintf(stderr, "calloc() failed in %s() %s:%d\n", __func__, __FILE__, __LINE__);
-        exit(1);
-    }
+    if (!mqtt)
+        FATAL_CALLOC("data_output_mqtt_create()");
 
     gethostname(mqtt->hostname, sizeof(mqtt->hostname) - 1);
     mqtt->hostname[sizeof(mqtt->hostname) - 1] = '\0';
diff --git a/src/pulse_detect.c b/src/pulse_detect.c
index 431f0445..10acd12e 100644
--- a/src/pulse_detect.c
+++ b/src/pulse_detect.c
@@ -13,6 +13,7 @@
 #include "pulse_demod.h"
 #include "util.h"
 #include "decoder.h"
+#include "fatal.h"
 #include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -56,7 +57,6 @@ void pulse_data_dump_raw(uint8_t *buf, unsigned len, uint64_t buf_offset, pulse_
     }
 }
 
-__attribute__((always_inline))
 static inline void chk_ret(int ret)
 {
     if (ret < 0) {
@@ -364,7 +364,10 @@ struct pulse_detect {
 
 pulse_detect_t *pulse_detect_create()
 {
-    return calloc(1, sizeof(pulse_detect_t));
+    pulse_detect_t *pulse_detect = calloc(1, sizeof(pulse_detect_t));
+    if (!pulse_detect)
+        WARN_CALLOC("pulse_detect_create()");
+    return pulse_detect;
 }
 
 void pulse_detect_free(pulse_detect_t *pulse_detect)
diff --git a/src/r_api.c b/src/r_api.c
index 4622c08a..c16f032e 100644
--- a/src/r_api.c
+++ b/src/r_api.c
@@ -27,6 +27,7 @@
 #include "optparse.h"
 #include "output_mqtt.h"
 #include "compat_time.h"
+#include "fatal.h"
 
 #ifdef _WIN32
 #include <io.h>
@@ -81,10 +82,8 @@ void r_init_cfg(r_cfg_t *cfg)
     list_ensure_size(&cfg->output_handler, 16);
 
     cfg->demod = calloc(1, sizeof(*cfg->demod));
-    if (!cfg->demod) {
-        fprintf(stderr, "Could not create demod!\n");
-        exit(1);
-    }
+    if (!cfg->demod)
+        FATAL_CALLOC("r_init_cfg()");
 
     cfg->demod->level_limit = DEFAULT_LEVEL_LIMIT;
 
@@ -95,10 +94,8 @@ void r_init_cfg(r_cfg_t *cfg)
 r_cfg_t *r_create_cfg(void)
 {
     r_cfg_t *cfg = calloc(1, sizeof(*cfg));
-    if (!cfg) {
-        fprintf(stderr, "Could not create cfg!\n");
-        exit(1);
-    }
+    if (!cfg)
+        FATAL_CALLOC("r_create_cfg()");
 
     r_init_cfg(cfg);
 
@@ -167,10 +164,8 @@ void register_protocol(r_cfg_t *cfg, r_device *r_dev, char *arg)
             fprintf(stderr, "Protocol [%d] \"%s\" does not take arguments \"%s\"!\n", r_dev->protocol_num, r_dev->name, arg);
         }
         p  = malloc(sizeof(*p));
-        if (!p) {
-            fprintf(stderr, "Could not register protocol\n");
-            exit(1);
-        }
+        if (!p)
+            FATAL_CALLOC("register_protocol()");
         *p = *r_dev; // copy
     }
 
@@ -485,12 +480,15 @@ void data_acquired_handler(r_device *r_dev, data_t *data)
             if ((d->type == DATA_STRING) && !strcmp(d->key, "battery")) {
                 free(d->key);
                 d->key = strdup("battery_ok");
+                if (!d->key)
+                    FATAL_STRDUP("data_acquired_handler()");
                 int ok = d->value && !strcmp(d->value, "OK");
                 free(d->value);
                 d->type = DATA_INT;
                 d->value = malloc(sizeof(int));
-                if (d->value) // NOTE: might silently skip on alloc failure.
-                    *(int *)d->value = ok;
+                if (!d->value)
+                    FATAL_MALLOC("data_acquired_handler()");
+                *(int *)d->value = ok;
                 break;
             }
         }
@@ -868,10 +866,8 @@ void add_null_output(r_cfg_t *cfg, char *param)
 void add_dumper(r_cfg_t *cfg, char const *spec, int overwrite)
 {
     file_info_t *dumper = calloc(1, sizeof(*dumper));
-    if (!dumper) {
-        fprintf(stderr, "Could not add dumper\n");
-        exit(1);
-    }
+    if (!dumper)
+        FATAL_CALLOC("add_dumper()");
     list_push(&cfg->demod->dumper, dumper);
 
     parse_file_info(spec, dumper);
diff --git a/src/r_util.c b/src/r_util.c
index d5948318..06ce440b 100644
--- a/src/r_util.c
+++ b/src/r_util.c
@@ -10,6 +10,7 @@
 */
 
 #include "r_util.h"
+#include "fatal.h"
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
@@ -169,9 +170,10 @@ char *str_replace(char *orig, char *rep, char *with)
     }
 
     tmp = result = malloc(strlen(orig) + (len_with - len_rep) * count + 1);
-
-    if (!result)
-        return NULL;
+    if (!result) {
+        WARN_MALLOC("str_replace()");
+        return NULL; // NOTE: returns NULL on alloc failure.
+    }
 
     // first time through the loop, all the variables are set correctly
     // from here on,
diff --git a/src/rtl_433.c b/src/rtl_433.c
index b9a79b82..572b26a8 100644
--- a/src/rtl_433.c
+++ b/src/rtl_433.c
@@ -46,6 +46,7 @@
 #include "confparse.h"
 #include "term_ctl.h"
 #include "compat_paths.h"
+#include "fatal.h"
 
 #ifdef _WIN32
 #include <io.h>
@@ -1211,12 +1212,11 @@ int main(int argc, char **argv) {
     // Special case for in files
     if (cfg.in_files.len) {
         unsigned char *test_mode_buf = malloc(DEFAULT_BUF_LENGTH * sizeof(unsigned char));
+        if (!test_mode_buf)
+            FATAL_MALLOC("test_mode_buf");
         float *test_mode_float_buf = malloc(DEFAULT_BUF_LENGTH / sizeof(int16_t) * sizeof(float));
-        if (!test_mode_buf || !test_mode_float_buf)
-        {
-            fprintf(stderr, "Couldn't allocate read buffers!\n");
-            exit(1);
-        }
+        if (!test_mode_float_buf)
+            FATAL_MALLOC("test_mode_float_buf");
 
         if (cfg.duration > 0) {
             time(&cfg.stop_time);
diff --git a/src/samp_grab.c b/src/samp_grab.c
index 407d1269..bece01f3 100644
--- a/src/samp_grab.c
+++ b/src/samp_grab.c
@@ -25,23 +25,25 @@
 #endif
 
 #include "samp_grab.h"
+#include "fatal.h"
 
 samp_grab_t *samp_grab_create(unsigned size)
 {
     samp_grab_t *g;
     g = calloc(1, sizeof(*g));
     if (!g) {
-        return NULL; // NOTE: silently return NULL on alloc failure.
+        WARN_CALLOC("samp_grab_create()");
+        return NULL; // NOTE: returns NULL on alloc failure.
     }
 
-    g->sg_buf  = malloc(size);
     g->sg_size = size;
-
     g->sg_counter = 1;
 
+    g->sg_buf = malloc(size);
     if (!g->sg_buf) {
+        WARN_MALLOC("samp_grab_create()");
         free(g);
-        return NULL; // NOTE: silently return NULL on alloc failure.
+        return NULL; // NOTE: returns NULL on alloc failure.
     }
 
     return g;
diff --git a/src/sdr.c b/src/sdr.c
index a86e0621..9be21ea9 100644
--- a/src/sdr.c
+++ b/src/sdr.c
@@ -19,6 +19,7 @@
 #include "sdr.h"
 #include "r_util.h"
 #include "optparse.h"
+#include "fatal.h"
 #ifdef RTLSDR
 #include "rtl-sdr.h"
 #include <libusb.h> /* libusb_error_name(), libusb_strerror() */
@@ -152,8 +153,10 @@ static int rtltcp_open(sdr_dev_t **out_dev, int *sample_size, char *dev_query, i
     fprintf(stderr, "rtl_tcp connected to %s:%s (Tuner: %s)\n", host, port, tuner_name);
 
     sdr_dev_t *dev = calloc(1, sizeof(sdr_dev_t));
-    if (!dev)
-        return -1; // NOTE: silently return on alloc failure.
+    if (!dev) {
+        WARN_CALLOC("rtltcp_open()");
+        return -1; // NOTE: returns error on alloc failure.
+    }
 
     dev->rtl_tcp = sock;
     dev->sample_size = sizeof(uint8_t); // CU8
@@ -185,8 +188,10 @@ static int rtltcp_read_loop(sdr_dev_t *dev, sdr_read_cb_t cb, void *ctx, uint32_
     if (dev->buffer_size != buf_len) {
         free(dev->buffer);
         dev->buffer = malloc(buf_len);
-        if (!dev->buffer)
-            return -1; // NOTE: silently return on alloc failure.
+        if (!dev->buffer) {
+            WARN_MALLOC("rtltcp_read_loop()");
+            return -1; // NOTE: returns error on alloc failure.
+        }
         dev->buffer_size = buf_len;
     }
     uint8_t *buffer = dev->buffer;
@@ -294,9 +299,10 @@ static int sdr_open_rtl(sdr_dev_t **out_dev, int *sample_size, char *dev_query,
     char vendor[256] = "n/a", product[256] = "n/a", serial[256] = "n/a";
     int r = -1;
     sdr_dev_t *dev = calloc(1, sizeof(sdr_dev_t));
-    if (!dev)
-        return -1; // NOTE: silently return on alloc failure.
-
+    if (!dev) {
+        WARN_CALLOC("sdr_open_rtl()");
+        return -1; // NOTE: returns error on alloc failure.
+    }
     for (uint32_t i = dev_query ? dev_index : 0;
             //cast quiets -Wsign-compare; if dev_index were < 0, would have returned -1 above
             i < (dev_query ? (unsigned)dev_index + 1 : device_count);
@@ -605,8 +611,10 @@ static int sdr_open_soapy(sdr_dev_t **out_dev, int *sample_size, char *dev_query
         SoapySDR_setLogLevel(SOAPY_SDR_DEBUG);
 
     sdr_dev_t *dev = calloc(1, sizeof(sdr_dev_t));
-    if (!dev)
-        return -1; // NOTE: silently return on alloc failure.
+    if (!dev) {
+        WARN_CALLOC("sdr_open_soapy()");
+        return -1; // NOTE: returns error on alloc failure.
+    }
 
     dev->soapy_dev = SoapySDRDevice_makeStrArgs(dev_query);
     if (!dev->soapy_dev) {
@@ -661,9 +669,10 @@ static int soapysdr_read_loop(sdr_dev_t *dev, sdr_read_cb_t cb, void *ctx, uint3
     if (dev->buffer_size != buf_len) {
         free(dev->buffer);
         dev->buffer = malloc(buf_len);
-        if (!dev->buffer)
-            return -1; // NOTE: silently return on alloc failure.
-
+        if (!dev->buffer) {
+            WARN_CALLOC("soapysdr_read_loop()");
+            return -1; // NOTE: returns error on alloc failure.
+        }
         dev->buffer_size = buf_len;
     }
     int16_t *buffer = dev->buffer;
diff --git a/tests/style-check.c b/tests/style-check.c
index a7798495..ac0da04c 100644
--- a/tests/style-check.c
+++ b/tests/style-check.c
@@ -35,6 +35,7 @@ static int style_check(char *path)
     int long_errors = 0;
     int crlf_errors = 0;
     int tabs_errors = 0;
+    int memc_errors = 0;
 
     int leading_tabs = 0;
     int leading_spcs = 0;
@@ -42,6 +43,8 @@ static int style_check(char *path)
     int use_stdout = 0;
     int use_printf = 0;
 
+    int need_cond = 0;
+
     char str[MAX_LEN];
     while (fgets(str, MAX_LEN, fp)) {
         int len = strlen(str);
@@ -72,6 +75,17 @@ static int style_check(char *path)
                 use_printf++;
             }
         }
+        if (need_cond && !strstr(str, "if (!")) {
+            // we had an alloc but no check on the following line
+            memc_errors++;
+        }
+        need_cond = 0;
+        if (strstr(str, "alloc(") && !strstr(str, "alloc()")) {
+            need_cond++;
+        }
+        if (strstr(str, "strdup(") && !strstr(str, "strdup()")) {
+            need_cond++;
+        }
     }
     if (leading_tabs && leading_spcs) {
         tabs_errors = leading_tabs > leading_spcs ? leading_spcs : leading_tabs;
@@ -85,6 +99,8 @@ static int style_check(char *path)
         printf("File \"%s\" has %d CRLF errors.\n", path, crlf_errors);
     if (tabs_errors)
         printf("File \"%s\" has %d MIXED tab/spaces errors.\n", path, tabs_errors);
+    if (memc_errors)
+        printf("File \"%s\" has %d ALLOC check errors.\n", path, memc_errors);
     if (leading_tabs)
         printf("File \"%s\" has %d TAB indented lines.\n", path, leading_tabs);
     if (strict && use_stdout)
@@ -92,7 +108,7 @@ static int style_check(char *path)
     if (strict && use_printf)
         printf("File \"%s\" has %d PRINTF lines.\n", path, use_printf);
 
-    return read_errors + long_errors + crlf_errors + tabs_errors + leading_tabs + (strict ? use_stdout + use_printf : 0);
+    return read_errors + long_errors + crlf_errors + tabs_errors + leading_tabs + (strict ? use_stdout + use_printf : 0) + memc_errors;
 }
 
 int main(int argc, char *argv[])