• Nathan Chancellor's avatar
    Bluetooth: Fix type of len in {l2cap,sco}_sock_getsockopt_old() · 9bf4e919
    Nathan Chancellor authored
    After an innocuous optimization change in LLVM main (19.0.0), x86_64
    allmodconfig (which enables CONFIG_KCSAN / -fsanitize=thread) fails to
    build due to the checks in check_copy_size():
    
      In file included from net/bluetooth/sco.c:27:
      In file included from include/linux/module.h:13:
      In file included from include/linux/stat.h:19:
      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/x86/include/asm/timex.h:6:
      In file included from arch/x86/include/asm/tsc.h:10:
      In file included from arch/x86/include/asm/msr.h:15:
      In file included from include/linux/percpu.h:7:
      In file included from include/linux/smp.h:118:
      include/linux/thread_info.h:244:4: error: call to '__bad_copy_from'
      declared with 'error' attribute: copy source size is too small
        244 |                         __bad_copy_from();
            |                         ^
    
    The same exact error occurs in l2cap_sock.c. The copy_to_user()
    statements that are failing come from l2cap_sock_getsockopt_old() and
    sco_sock_getsockopt_old(). This does not occur with GCC with or without
    KCSAN or Clang without KCSAN enabled.
    
    len is defined as an 'int' because it is assigned from
    '__user int *optlen'. However, it is clamped against the result of
    sizeof(), which has a type of 'size_t' ('unsigned long' for 64-bit
    platforms). This is done with min_t() because min() requires compatible
    types, which results in both len and the result of sizeof() being casted
    to 'unsigned int', meaning len changes signs and the result of sizeof()
    is truncated. From there, len is passed to copy_to_user(), which has a
    third parameter type of 'unsigned long', so it is widened and changes
    signs again. This excessive casting in combination with the KCSAN
    instrumentation causes LLVM to fail to eliminate the __bad_copy_from()
    call, failing the build.
    
    The official recommendation from LLVM developers is to consistently use
    long types for all size variables to avoid the unnecessary casting in
    the first place. Change the type of len to size_t in both
    l2cap_sock_getsockopt_old() and sco_sock_getsockopt_old(). This clears
    up the error while allowing min_t() to be replaced with min(), resulting
    in simpler code with no casts and fewer implicit conversions. While len
    is a different type than optlen now, it should result in no functional
    change because the result of sizeof() will clamp all values of optlen in
    the same manner as before.
    
    Cc: stable@vger.kernel.org
    Closes: https://github.com/ClangBuiltLinux/linux/issues/2007
    Link: https://github.com/llvm/llvm-project/issues/85647Signed-off-by: default avatarNathan Chancellor <nathan@kernel.org>
    Reviewed-by: default avatarJustin Stitt <justinstitt@google.com>
    Signed-off-by: default avatarLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
    9bf4e919
sco.c 29.2 KB