Tested with gcc 14.2 and the Linux kernel compiling for amd64. This is
at Linux next-20241127. This was already the case on gcc 13 (no idea
about earlier versions), I tested 14 to see if the problem is gone.
In the particular case I ran into a prediction concerning the return
value of __access_ok is not correctly acted upon.
#define access_ok(addr, size) likely(__access_ok(addr, size))
then in __access_ok:
return valid_user_address(sum) && sum >= (__force unsigned long)ptr;
The expected asm contains 2 branches both with a jump towards the end
of the function in the failing case. Instead jumps are emitted if
everything works out.
Interestingly the issue fixes itself if I split the if statement like so:
- return valid_user_address(sum) && sum >= (__force
unsigned long)ptr;
+ if (!valid_user_address(sum))
+ return false;
+ return sum >= (__force unsigned long)ptr;
Routine at hand (trimmed):
static inline __must_check unsigned long
_inline_copy_to_user(void __user *to, const void *from, unsigned long n)
{
if (access_ok(to, n)) {
instrument_copy_to_user(to, from, n);
n = raw_copy_to_user(to, from, n);
}
return n;
}
Bad asm:
Dump of assembler code for function _copy_to_user:
0xffffffff819713c0 <+0>: endbr64
0xffffffff819713c4 <+4>: mov %rdx,%rcx
0xffffffff819713c7 <+7>: mov %rdx,%rax
0xffffffff819713ca <+10>: xor %edx,%edx
0xffffffff819713cc <+12>: add %rdi,%rcx
0xffffffff819713cf <+15>: setb %dl
0xffffffff819713d2 <+18>: movabs $0x123456789abcdef,%r8
0xffffffff819713dc <+28>: test %rdx,%rdx
0xffffffff819713df <+31>: jne 0xffffffff819713e6 <_copy_to_user+38>
this jumps to the exit
0xffffffff819713e1 <+33>: cmp %rcx,%r8
0xffffffff819713e4 <+36>: jae 0xffffffff819713eb <_copy_to_user+43>
this jumps over exiting the func to do the actual work, even though
doing the compiler is hinted this is the expected state
0xffffffff819713e6 <+38>: jmp 0xffffffff8206b160 <__x86_return_thunk>
0xffffffff819713eb <+43>: nop
0xffffffff819713ec <+44>: nop
0xffffffff819713ed <+45>: nop
0xffffffff819713ee <+46>: mov %rax,%rcx
0xffffffff819713f1 <+49>: rep movsb %ds:(%rsi),%es:(%rdi)
0xffffffff819713f3 <+51>: nop
0xffffffff819713f4 <+52>: nop
0xffffffff819713f5 <+53>: nop
0xffffffff819713f6 <+54>: mov %rcx,%rax
0xffffffff819713f9 <+57>: nop
0xffffffff819713fa <+58>: nop
0xffffffff819713fb <+59>: nop
0xffffffff819713fc <+60>: jmp 0xffffffff8206b160 <__x86_return_thunk>
Good asm:
Dump of assembler code for function _copy_to_user:
0xffffffff819713f0 <+0>: endbr64
0xffffffff819713f4 <+4>: xor %r8d,%r8d
0xffffffff819713f7 <+7>: mov %rdx,%rax
0xffffffff819713fa <+10>: add %rdi,%rdx
0xffffffff819713fd <+13>: setb %r8b
0xffffffff81971401 <+17>: movabs $0x123456789abcdef,%rcx
0xffffffff8197140b <+27>: cmp %rdx,%rcx
0xffffffff8197140e <+30>: jb 0xffffffff81971426 <_copy_to_user+54>
this only jumps if the condition failed (where the prediction is that it wont)
0xffffffff81971410 <+32>: test %r8,%r8
0xffffffff81971413 <+35>: jne 0xffffffff81971426 <_copy_to_user+54>
ditto
0xffffffff81971415 <+37>: nop
0xffffffff81971416 <+38>: nop
0xffffffff81971417 <+39>: nop
0xffffffff81971418 <+40>: mov %rax,%rcx
0xffffffff8197141b <+43>: rep movsb %ds:(%rsi),%es:(%rdi)
0xffffffff8197141d <+45>: nop
0xffffffff8197141e <+46>: nop
0xffffffff8197141f <+47>: nop
0xffffffff81971420 <+48>: mov %rcx,%rax
0xffffffff81971423 <+51>: nop
0xffffffff81971424 <+52>: nop
0xffffffff81971425 <+53>: nop
0xffffffff81971426 <+54>: jmp 0xffffffff8206b1a0 <__x86_return_thunk>
--
Mateusz Guzik <mjguzik gmail.com>