fortify: Capture __bos() results in const temp vars
authorKees Cook <keescook@chromium.org>
Fri, 28 Oct 2022 22:32:07 +0000 (15:32 -0700)
committerKees Cook <keescook@chromium.org>
Fri, 28 Oct 2022 23:07:01 +0000 (16:07 -0700)
In two recent run-time memcpy() bound checking bug reports (NFS[1] and
JFS[2]), the _detection_ was working correctly (in the sense that the
requested copy size was larger than the destination field size), but
the _warning text_ was showing the destination field size as SIZE_MAX
("unknown size"). This should be impossible, since the detection function
will explicitly give up if the destination field size is unknown. For
example, the JFS warning was:

  memcpy: detected field-spanning write (size 132) of single field "ip->i_link" at fs/jfs/namei.c:950 (size 18446744073709551615)

Other cases of this warning (e.g.[3]) have reported correctly,
and the reproducer only happens under GCC (at least 10.2 and 12.1),
so this currently appears to be a GCC bug. Explicitly capturing the
__builtin_object_size() results in const temporary variables fixes the
report. For example, the JFS reproducer now correctly reports the field
size (128):

  memcpy: detected field-spanning write (size 132) of single field "ip->i_link" at fs/jfs/namei.c:950 (size 128)

Examination of the .text delta (which is otherwise identical), shows
the literal value used in the report changing:

-     mov    $0xffffffffffffffff,%rcx
+     mov    $0x80,%ecx

[1] https://lore.kernel.org/lkml/Y0zEzZwhOxTDcBTB@codemonkey.org.uk/
[2] https://syzkaller.appspot.com/bug?id=23d613df5259b977dac1696bec77f61a85890e3d
[3] https://lore.kernel.org/all/202210110948.26b43120-yujie.liu@intel.com/

Cc: "Dr. David Alan Gilbert" <linux@treblig.org>
Cc: llvm@lists.linux.dev
Cc: linux-hardening@vger.kernel.org
Signed-off-by: Kees Cook <keescook@chromium.org>
include/linux/fortify-string.h

index 4029fe3..0f00a55 100644 (file)
@@ -441,13 +441,18 @@ __FORTIFY_INLINE bool fortify_memcpy_chk(__kernel_size_t size,
 
 #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);                         \
-       WARN_ONCE(fortify_memcpy_chk(__fortify_size, p_size, q_size,    \
-                                    p_size_field, q_size_field, #op),  \
+       const size_t __fortify_size = (size_t)(size);                   \
+       const size_t __p_size = (p_size);                               \
+       const size_t __q_size = (q_size);                               \
+       const size_t __p_size_field = (p_size_field);                   \
+       const size_t __q_size_field = (q_size_field);                   \
+       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);                                        \
+                 __p_size_field);                                      \
        __underlying_##op(p, q, __fortify_size);                        \
 })