#ifndef _LINUX_FORTIFY_STRING_H_
 #define _LINUX_FORTIFY_STRING_H_
 
+#include <linux/bug.h>
 #include <linux/const.h>
 #include <linux/limits.h>
 
  * V = vulnerable to run-time overflow (will need refactoring to solve)
  *
  */
-__FORTIFY_INLINE void fortify_memcpy_chk(__kernel_size_t size,
+__FORTIFY_INLINE bool fortify_memcpy_chk(__kernel_size_t size,
                                         const size_t p_size,
                                         const size_t q_size,
                                         const size_t p_size_field,
        if ((p_size != SIZE_MAX && p_size < size) ||
            (q_size != SIZE_MAX && q_size < size))
                fortify_panic(func);
+
+       /*
+        * Warn when writing beyond destination field size.
+        *
+        * We must ignore p_size_field == 0 for existing 0-element
+        * fake flexible arrays, until they are all converted to
+        * proper flexible arrays.
+        *
+        * The implementation of __builtin_object_size() behaves
+        * like sizeof() when not directly referencing a flexible
+        * array member, which means there will be many bounds checks
+        * that will appear at run-time, without a way for them to be
+        * detected at compile-time (as can be done when the destination
+        * is specifically the flexible array member).
+        * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101832
+        */
+       if (p_size_field != 0 && p_size_field != SIZE_MAX &&
+           p_size != p_size_field && p_size_field < size)
+               return true;
+
+       return false;
 }
 
 #define __fortify_memcpy_chk(p, q, size, p_size, q_size,               \
                             p_size_field, q_size_field, op) ({         \
        size_t __fortify_size = (size_t)(size);                         \
-       fortify_memcpy_chk(__fortify_size, p_size, q_size,              \
-                          p_size_field, q_size_field, #op);            \
+       WARN_ONCE(fortify_memcpy_chk(__fortify_size, p_size, q_size,    \
+                                    p_size_field, q_size_field, #op),  \
+                 #op ": detected field-spanning write (size %zu) of single %s (size %zu)\n", \
+                 __fortify_size,                                       \
+                 "field \"" #p "\" at " __FILE__ ":" __stringify(__LINE__), \
+                 p_size_field);                                        \
        __underlying_##op(p, q, __fortify_size);                        \
 })
 
+/*
+ * Notes about compile-time buffer size detection:
+ *
+ * With these types...
+ *
+ *     struct middle {
+ *             u16 a;
+ *             u8 middle_buf[16];
+ *             int b;
+ *     };
+ *     struct end {
+ *             u16 a;
+ *             u8 end_buf[16];
+ *     };
+ *     struct flex {
+ *             int a;
+ *             u8 flex_buf[];
+ *     };
+ *
+ *     void func(TYPE *ptr) { ... }
+ *
+ * Cases where destination size cannot be currently detected:
+ * - the size of ptr's object (seemingly by design, gcc & clang fail):
+ *     __builtin_object_size(ptr, 1) == SIZE_MAX
+ * - the size of flexible arrays in ptr's obj (by design, dynamic size):
+ *     __builtin_object_size(ptr->flex_buf, 1) == SIZE_MAX
+ * - the size of ANY array at the end of ptr's obj (gcc and clang bug):
+ *     __builtin_object_size(ptr->end_buf, 1) == SIZE_MAX
+ *     https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101836
+ *
+ * Cases where destination size is currently detected:
+ * - the size of non-array members within ptr's object:
+ *     __builtin_object_size(ptr->a, 1) == 2
+ * - the size of non-flexible-array in the middle of ptr's obj:
+ *     __builtin_object_size(ptr->middle_buf, 1) == 16
+ *
+ */
+
 /*
  * __builtin_object_size() must be captured here to avoid evaluating argument
  * side-effects further into the macro layers.