Neverflow: C macros that guard against buffer overflows
Here is a different take on it. We can use #define to inform the header about the properties of certain symbols.<p>Here is my oob.c program. I will show the output, and then the content of &quot;oob.h&quot;.<p><pre><code>  #include &lt;stdlib.h&gt;
  #include &lt;stdio.h&gt;
  #include &quot;oob.h&quot;

  int oob_fail(const char *file, int line)
  {
    fprintf(stderr, &quot;%s:%d:out of bounds array access\n&quot;, file, line);
    abort();
  }

  &#x2F;*
   * Declare properties of array type x
   *&#x2F;
  #define ARRAY_ELTYPE_x int    &#x2F;* element type is int *&#x2F;
  #define ARRAY_SIZE_x 7        &#x2F;* number of elements is 7 *&#x2F;

  &#x2F;*
   * Ensure array type x is fully declared at file scope
   *&#x2F;
  ARRAY_FULLTYPE(x);

  &#x2F;*
   * Inform the OOB module that the identifiers p and a are
   * used as variables related to type x: either pointers
   * to it or values.
   *&#x2F;
  #define ARRAY_TYPEOF_p x
  #define ARRAY_TYPEOF_a x

  int get_elem(ARRAY_TYPE(x) *p, int i)
  {
     return APREF(p, i);
  }

  int main(void)
  {
     ARRAY_TYPE(x) a = ARRAY_INIT(1, 2, 3);

     for (size_t i = 0; i &lt;= ARRAY_SIZEOF(a); i++)
        printf(&quot;a[%zd] == %d\n&quot;, i, get_elem(&amp;a, i));

     return 0;
  }
</code></pre>
Output:<p><pre><code>  $ .&#x2F;oob
  a[0] == 1
  a[1] == 2
  a[2] == 3
  a[3] == 0
  a[4] == 0
  a[5] == 0
  a[6] == 0
  oob.c:31:out of bounds array access
  Aborted (core dumped)
</code></pre>
The content of &quot;oob.h&quot;<p><pre><code>  #ifndef OOB_H_435E_FDE9
  #define OOB_H_435E_FDE9

  int oob_fail(const char *file, int line);

  #define OOB_PREFIX oob_ident_
  #define OOB_XCAT(X, Y) X ## Y
  #define OOB_CAT(X, Y) OOB_XCAT(X, Y)

  #define ARRAY_ELTYPE(T) OOB_CAT(ARRAY_ELTYPE_, T)
  #define ARRAY_SIZE(T) OOB_CAT(ARRAY_SIZE_, T)
  #define ARRAY_TAG(T) OOB_CAT(ARRAY_TAG_, T)

  #define ARRAY_FULLTYPE(T)                                                     \
    struct ARRAY_TAG(T) {                                                       \
      ARRAY_ELTYPE(T) a[ARRAY_SIZE(T)];                                         \
    }

  #define ARRAY_TYPE(T) struct ARRAY_TAG(T)

  #define ARRAY_TYPEOF(V) OOB_CAT(ARRAY_TYPEOF_, V)
  #define ARRAY_SIZEOF(V) ARRAY_SIZE(ARRAY_TYPEOF(V))

  #define ARRAY_INIT(...) { { __VA_ARGS__ } }

  #define AREF(ARRAY, I)                                                        \
    (((size_t) (I) &gt;= ARRAY_SIZEOF(ARRAY))                                      \
     ? oob_fail(__FILE__, __LINE__), (ARRAY).a[0]                               \
     : (ARRAY).a[I])

  #define APREF(PARRAY, I)                                                      \
    (((size_t) (I) &gt;= ARRAY_SIZEOF(PARRAY))                                     \
     ? oob_fail(__FILE__, __LINE__), (PARRAY)-&gt;a[0]                             \
     : (PARRAY)-&gt;a[I])

  #endif
</code></pre>
Preprocessor invoked on oob.c (snipped down to the relevant part after the run-time support function oob_fail):<p><pre><code>  struct ARRAY_TAG_x { int a[7]; };


  int get_elem(struct ARRAY_TAG_x *p, int i)
  {
     return (((size_t) (i) &gt;= 7) ? oob_fail(&quot;oob.c&quot;, 31), (p)-&gt;a[0] : (p)-&gt;a[i]);
  }

  int main(void)
  {
     struct ARRAY_TAG_x a = { { 1, 2, 3 } };

     for (size_t i = 0; i &lt;= 7; i++)
        printf(&quot;a[%zd] == %d\n&quot;, i, get_elem(&amp;a, i));

     return 0;
  }
</code></pre>
It&#x27;s clean enough to be readable (except, of course, code dense with AREF or APREF calls will be a mess). Uses arrays wrapped in structs, so you can pass arrays by value.<p>You have to make a list of your variables that are involved and write some #define lines for them.<p>Same for the array types.