ClangBuiltLinux/linux

Fortify warning in net/xfrm/xfrm_user.c with arm64 CONFIG_KASAN=y after LLVM commit 90ba33099cbb1

Opened this issue · 0 comments

This is the same warning as #1985 but it only appears when KASAN is enabled, as reported by @arndb at https://lore.kernel.org/763214eb-20eb-4627-8d4b-2e7f29db829a@app.fastmail.com/.

$ echo 'CONFIG_FORTIFY_SOURCE=y
CONFIG_KASAN=y
CONFIG_XFRM_USER=y' >arch/arm64/configs/repro.config

$ make -skj"$(nproc)" ARCH=arm64 LLVM=1 {def,repro.}config net/xfrm/xfrm_user.o
In file included from net/xfrm/xfrm_user.c:14:
In file included from include/linux/compat.h:10:
In file included from include/linux/time.h:60:
In file included from include/linux/time32.h:13:
In file included from include/linux/timex.h:67:
In file included from arch/arm64/include/asm/timex.h:8:
In file included from arch/arm64/include/asm/arch_timer.h:12:
In file included from arch/arm64/include/asm/hwcap.h:9:
In file included from arch/arm64/include/asm/cpufeature.h:27:
In file included from include/linux/cpumask.h:13:
In file included from include/linux/bitmap.h:13:
In file included from include/linux/string.h:371:
include/linux/fortify-string.h:462:4: warning: call to '__write_overflow_field' declared with 'warning' attribute: detected write beyond size of field (1st parameter); maybe use struct_group()? [-Wattribute-warning]
  462 |                         __write_overflow_field(p_size_field, size);
      |                         ^
1 warning generated.

This is with 6.9-rc4, which contains my change to fix #1985.

cvise spits out:

enum { false } __fortify_panic() __attribute__((__noreturn__));
struct xfrm_id {
  int daddr;
  int spi;
  char proto;
};
struct xfrm_user_tmpl {
  struct xfrm_id id;
  short share;
  char optional;
  int calgos;
} copy_to_user_tmpl_vec[6];
struct xfrm_tmpl {
  char optional;
  int ealgos;
  int calgos;
};
struct {
  char xfrm_nr;
  struct xfrm_tmpl xfrm_vec[];
} *copy_to_user_tmpl_xp;
void __write_overflow_field()
    __attribute__((__warning__("detected write beyond size of field (1st "
                               "parameter); maybe use struct_group()?")));
void *__underlying_memcpy(void *, void *, long) __asm__("memcpy");
_Bool fortify_memset_chk(unsigned long size, unsigned long p_size,
                         unsigned long p_size_field) {
  if (__builtin_constant_p(p_size_field < size) && p_size_field < size)
    __write_overflow_field();
  if (p_size < size)
    __fortify_panic();
  return false;
}
int copy_to_user_tmpl() {
  unsigned long __trans_tmp_4, __trans_tmp_3;
  int i;
  if (copy_to_user_tmpl_xp)
    return 5;
  for (i = 0; copy_to_user_tmpl_xp->xfrm_nr; i++) {
    struct xfrm_user_tmpl *up = &copy_to_user_tmpl_vec[i];
    struct xfrm_tmpl kp = copy_to_user_tmpl_xp->xfrm_vec[i];
    unsigned long __fortify_size = sizeof(up);
    __trans_tmp_3 = __trans_tmp_4 = __builtin_dynamic_object_size(up, 1);
    fortify_memset_chk(__fortify_size, __trans_tmp_3, __trans_tmp_4);
    __fortify_size = sizeof(up->id);
    __underlying_memcpy(up, &kp, __fortify_size);
    up->share = up->optional = copy_to_user_tmpl_xp->xfrm_vec[i].ealgos;
    up->calgos = copy_to_user_tmpl_xp->xfrm_vec[i].calgos;
  }
  return 0;
}

GCC 13.2.0:

$ aarch64-linux-gcc -O2 -Wall -Wextra -Werror -Wfatal-errors -c -o /dev/null xfrm_user.i

$ aarch64-linux-gcc -O2 -Wall -Wextra -Werror -Wfatal-errors -c -o /dev/null xfrm_user.i -fsanitize=kernel-address

clang @ llvm/llvm-project@98509c7

$ clang -O2 -Wall -Wextra -Werror -Wfatal-errors -c -o /dev/null xfrm_user.i

$ clang -O2 -Wall -Wextra -Werror -Wfatal-errors -c -o /dev/null xfrm_user.i -fsanitize=kernel-address

clang @ llvm/llvm-project@90ba330

$ clang -O2 -Wall -Wextra -Werror -Wfatal-errors -c -o /dev/null xfrm_user.i

$ clang -O2 -Wall -Wextra -Werror -Wfatal-errors -c -o /dev/null xfrm_user.i -fsanitize=kernel-address
xfrm_user.i:29:5: fatal error: call to '__write_overflow_field' declared with 'warning' attribute: detected write beyond size of field (1st parameter); maybe use struct_group()? [-Wattribute-warning]
   29 |     __write_overflow_field();
      |     ^
1 error generated.