Bug 1965360

Summary: kernel: get_timespec64 does not ignore padding in compat syscalls
Product: Red Hat Enterprise Linux 8 Reporter: Florian Weimer <fweimer>
Component: kernelAssignee: Waiman Long <llong>
kernel sub component: Kernel-Core QA Contact: Qiao Zhao <qzhao>
Status: CLOSED ERRATA Docs Contact:
Severity: urgent    
Priority: urgent CC: brdeoliv, cye, honli, llong, rdma-dev-team, smeisner, tagoh, tkopecek, vondruch
Version: 8.4Keywords: Triaged, ZStream
Target Milestone: beta   
Target Release: 8.5   
Hardware: x86_64   
OS: Linux   
Whiteboard:
Fixed In Version: kernel-4.18.0-313.el8 Doc Type: No Doc Update
Doc Text:
Story Points: ---
Clone Of:
: Red Hat2003569 (view as bug list) Environment:
Last Closed: 2021-11-09 19:20:33 UTC Type: Bug
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Bug Depends On:    
Bug Blocks: 1963934, Red Hat2003569    

Description Florian Weimer 2021-05-27 14:25:00 UTC
I'm looking at kernel-4.18.0-305.el8.x86_64.

get_timespec64 in kernel/time/time.c does not clear the padding, I think:

        /* Zero out the padding for 32 bit systems or in compat mode */
        if (IS_ENABLED(CONFIG_64BIT_TIME) && (!IS_ENABLED(CONFIG_64BIT) || in_compat_syscall()))
                kts.tv_nsec &= 0xFFFFFFFFUL;

In our kernels IS_ENABLED(CONFIG_64BIT_TIME) is false, so this code is not compiled in, I think:

Dump of assembler code for function get_timespec64:
855     {
   0xffffffff8116a2e0 <+0>:     callq  0xffffffff81a01920 <__fentry__>
   0xffffffff8116a2e5 <+5>:     push   %rbx
   0xffffffff8116a2e6 <+6>:     mov    $0x10,%edx
   0xffffffff8116a2eb <+11>:    mov    %rdi,%rbx
   0xffffffff8116a2ee <+14>:    sub    $0x18,%rsp
   0xffffffff8116a2f2 <+18>:    mov    %gs:0x28,%rax
   0xffffffff8116a2fb <+27>:    mov    %rax,0x10(%rsp)
   0xffffffff8116a300 <+32>:    xor    %eax,%eax

856             struct __kernel_timespec kts;
857             int ret;
858
859             ret = copy_from_user(&kts, uts, sizeof(kts));
860             if (ret)
   0xffffffff8116a30a <+42>:    test   %eax,%eax
   0xffffffff8116a30c <+44>:    jne    0xffffffff8116a334 <get_timespec64+84>

861                     return -EFAULT;
862
863             ts->tv_sec = kts.tv_sec;
   0xffffffff8116a30e <+46>:    mov    (%rsp),%rdx
   0xffffffff8116a312 <+50>:    mov    %rdx,(%rbx)

864
865             /* Zero out the padding for 32 bit systems or in compat mode */
866             if (IS_ENABLED(CONFIG_64BIT_TIME) && (!IS_ENABLED(CONFIG_64BIT) || in_compat_syscall()))

867                     kts.tv_nsec &= 0xFFFFFFFFUL;
868
869             ts->tv_nsec = kts.tv_nsec;
   0xffffffff8116a315 <+53>:    mov    0x8(%rsp),%rdx
   0xffffffff8116a31a <+58>:    mov    %rdx,0x8(%rbx)

870
871             return 0;
   0xffffffff8116a31e <+62>:    mov    0x10(%rsp),%rcx
   0xffffffff8116a323 <+67>:    xor    %gs:0x28,%rcx
   0xffffffff8116a32c <+76>:    jne    0xffffffff8116a33b <get_timespec64+91>
   0xffffffff8116a32e <+78>:    add    $0x18,%rsp
   0xffffffff8116a332 <+82>:    pop    %rbx
   0xffffffff8116a333 <+83>:    retq   
   0xffffffff8116a334 <+84>:    mov    $0xfffffff2,%eax
   0xffffffff8116a339 <+89>:    jmp    0xffffffff8116a31e <get_timespec64+62>
   0xffffffff8116a33b <+91>:    callq  0xffffffff810e09b0 <__stack_chk_fail>

End of assembler dump.

As a result, our kernel does not have the right userspace ABI for system calls such as utimensat_time64. Upstream decided to ignore padding, and current glibc (e.g., glibc-2.33-13.el9.i686) depends on this.

Comment 1 Florian Weimer 2021-05-27 14:42:09 UTC
Reproducer that can be built with gcc -m32 on Red Hat Enterprise Linux 8:

#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <sys/syscall.h>
#include <unistd.h>

int
main (void)
{
  errno = 0;
  uint64_t times[4] = { 1, 2 | (3ULL << 32), 4, 5 | (6ULL << 32) };
  long ret = syscall (412, AT_FDCWD, "/proc/self/exe", times, 0);
  printf ("%ld, %m\n", ret);
}

“/opt/rh/gcc-toolset-10/root/usr/bin/strace ./a.out” shows:

utimensat_time64(AT_FDCWD, "/proc/self/exe", [{tv_sec=1, tv_nsec=12884901890}, {tv_sec=4, tv_nsec=25769803781}], 0) = -1 EINVAL (Invalid argument)

strace does not mask the padding, either, so the tv_nsec values are expected, but the EINVAL result is not. The system call should succeed.

Comment 3 Florian Weimer 2021-05-27 14:46:29 UTC
*** Bug 1965078 has been marked as a duplicate of this bug. ***

Comment 4 Waiman Long 2021-05-27 16:41:08 UTC
(In reply to Florian Weimer from comment #0)
> I'm looking at kernel-4.18.0-305.el8.x86_64.
> 
> get_timespec64 in kernel/time/time.c does not clear the padding, I think:
> 
>         /* Zero out the padding for 32 bit systems or in compat mode */
>         if (IS_ENABLED(CONFIG_64BIT_TIME) && (!IS_ENABLED(CONFIG_64BIT) ||
> in_compat_syscall()))
>                 kts.tv_nsec &= 0xFFFFFFFFUL;
> 
> In our kernels IS_ENABLED(CONFIG_64BIT_TIME) is false, so this code is not
> compiled in, I think:

Another side effect of the backport of time related patches to support time namespace. I will remove CONFIG_64BIT_TIME from the kernel source.

Thanks for looking into this problem.

-Longman

Comment 26 errata-xmlrpc 2021-11-09 19:20:33 UTC
Since the problem described in this bug report should be
resolved in a recent advisory, it has been closed with a
resolution of ERRATA.

For information on the advisory (Moderate: kernel security, bug fix, and enhancement update), and where to find the updated
files, follow the link below.

If the solution does not work for you, open a new bug report.

https://access.redhat.com/errata/RHSA-2021:4356