Bug 45737 - fakeroot: неопределённое поведение в обёртке syscall
Summary: fakeroot: неопределённое поведение в обёртке syscall
Status: CLOSED NOTABUG
Alias: None
Product: Sisyphus
Classification: Development
Component: fakeroot (show other bugs)
Version: unstable
Hardware: all Linux
: P5 normal
Assignee: placeholder@altlinux.org
QA Contact: qa-sisyphus
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2023-04-03 10:59 MSK by Alexey Sheplyakov
Modified: 2023-06-02 18:26 MSK (History)
9 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Alexey Sheplyakov 2023-04-03 10:59:53 MSK
2635 long int syscall(long int nr, ...)
2636 {
2637   switch (nr) {
2638 #ifdef SYS_setgroups32
2639     case SYS_setgroups32:
2640 #endif
2641     case SYS_setgroups:
2642       if (fakeroot_disabled)
2643         goto forward;
2644       else
2645         return 0;
2646     default:
2647       break;
2648   }
2649 forward:
2650   va_list args;
2651   va_start(args, nr);
2652   long a1 = va_arg(args, long);
2653   long a2 = va_arg(args, long);
2654   long a3 = va_arg(args, long);
2655   long a4 = va_arg(args, long);
2656   long a5 = va_arg(args, long);
2657   long a6 = va_arg(args, long);
2658   va_end(args);
2659   return next_syscall(nr, a1, a2, a3, a4, a5, a6);
2660 }

The va_arg() macro expands to an expression that has the type and value of the next argument in the call. The argument ap is the va_list ap initialized by va_start(). Each call to va_arg() modifies ap so that the next call returns the next argument. The argument type is a type name specified so that the type of a pointer to an object that has the specified type can be obtained simply by adding a * to type.

The first use of the va_arg() macro after that of the va_start() macro returns the argument after last. Successive invocations return the values of the remaining arguments.

If there is no next argument, or if type is not compatible with the type of the actual next argument (as promoted according to the default argument promotions), random errors will occur. 

(цитата из https://linux.die.net/man/3/va_start)

Из-за этого нельзя просто так взять и достать 6 аргументов. Нужно либо
1) Сделать обёртку, которая принимает аргумент va_list (наподобие vprintf)
2) Понять, сколько их было передано на самом деле исходя из номера системного вызова (аналогично тому, как printf определяет количество аргументов из форматной строки)
3) Перехватывать собственно системный вызов подобно fakeroot-ng (или более "современным" способом с seccomp filter, примерно так: https://github.com/alfonsosanchezbeato/ptrace-redirect/blob/master/redir_filter.c)
4) Придумать что-то другое
Comment 1 Dmitry V. Levin 2023-04-03 12:23:59 MSK
(In reply to Alexey Sheplyakov from comment #0)
> Из-за этого нельзя просто так взять и достать 6 аргументов.

Что значит нельзя? Если достать больше аргументов, чем было передано, то будет обычный trash in - garbage out, что вполне подходит для этой обёртки.

> 1) Сделать обёртку, которая принимает аргумент va_list (наподобие vprintf)
> 2) Понять, сколько их было передано на самом деле исходя из номера
> системного вызова (аналогично тому, как printf определяет количество
> аргументов из форматной строки)
> 3) Перехватывать собственно системный вызов подобно fakeroot-ng (или более
> "современным" способом с seccomp filter, примерно так:
> https://github.com/alfonsosanchezbeato/ptrace-redirect/blob/master/
> redir_filter.c)
> 4) Придумать что-то другое

Зачем так усложнять в данном конкретном случае?
Comment 2 Alexey Sheplyakov 2023-04-05 00:30:40 MSK
(Ответ для Dmitry V. Levin на комментарий #1)
> (In reply to Alexey Sheplyakov from comment #0)
> > Из-за этого нельзя просто так взять и достать 6 аргументов.
> 
> Что значит нельзя? Если достать больше аргументов, чем было передано, то
> будет обычный trash in - garbage out, что вполне подходит для этой обёртки.

Undefined behavior не подходит никогда.

> > 1) Сделать обёртку, которая принимает аргумент va_list (наподобие vprintf)

Ничего сложного в этом нет

> Зачем так усложнять в данном конкретном случае?

Зачем делать плохо, если можно сделать хорошо, и это несложно?
Comment 3 Dmitry V. Levin 2023-04-05 00:55:58 MSK
(In reply to Alexey Sheplyakov from comment #2)
> (Ответ для Dmitry V. Levin на комментарий #1)
> > (In reply to Alexey Sheplyakov from comment #0)
> > > Из-за этого нельзя просто так взять и достать 6 аргументов.
> > 
> > Что значит нельзя? Если достать больше аргументов, чем было передано, то
> > будет обычный trash in - garbage out, что вполне подходит для этой обёртки.
> 
> Undefined behavior не подходит никогда.

Это UB только теоретически.

> > > 1) Сделать обёртку, которая принимает аргумент va_list (наподобие vprintf)
> 
> Ничего сложного в этом нет

Сигнатура syscall() фиксирована и поменять её нельзя.
Честно говоря, я тут не вижу несложного решения.

> > Зачем так усложнять в данном конкретном случае?
> 
> Зачем делать плохо, если можно сделать хорошо, и это несложно?

Мы же не академический институт, зачем нам тут решать чисто теоретические задачи?
Comment 4 Ivan Zakharyaschev 2023-04-06 03:08:14 MSK
Думаю, что ничего страшного не происходит от того, что берётся больше аргументов, чем было на самом деле передано (и чем ожидает на самом деле конкретный системный вызов), ведь даже в реализациях syscall.S в glibc тупо загружают для системного вызова максимум аргументов вне зависимости от конкретного вызова. Т.е. эту задачу не пытаются решить. (Только для ia64 по-хитрому сдвигают номера регистров, как я понял, но хоть архитектура и очень похожа на e2k, соглашение о вызове variadic function на e2k другое и ведёт к тому, что аргументы в памяти, а не в регистрах, и так сделать нельзя.)

# tail -n20 glibc-2.29-alt2.E2K.26.012.1/sysdeps/unix/sysv/linux/*/syscall.S      
==> glibc-2.29-alt2.E2K.26.012.1/sysdeps/unix/sysv/linux/aarch64/syscall.S <==

   For kernel entry we need to move the system call nr to x8 then
   load the remaining arguments to register. */

ENTRY (syscall)
        uxtw    x8, w0
        mov     x0, x1
        mov     x1, x2
        mov     x2, x3
        mov     x3, x4
        mov     x4, x5
        mov     x5, x6
        mov     x6, x7
        svc     0x0
        cmn     x0, #4095
        b.cs    1f
        RET
1:
        b       SYSCALL_ERROR
PSEUDO_END (syscall)

==> glibc-2.29-alt2.E2K.26.012.1/sysdeps/unix/sysv/linux/alpha/syscall.S <==
        .prologue 1
#else
        .prologue 0
#endif

        mov     a0, v0          /* Syscall number -> v0 */
        mov     a1, a0          /* arg1-arg5 -> a0-a4 */
        mov     a2, a1
        mov     a3, a2
        mov     a4, a3
        mov     a5, a4
        ldq     a5,0(sp)        /* arg6 -> a5 */

        call_pal PAL_callsys    /* Invoke system call */
        bne     a3, SYSCALL_ERROR_LABEL
        ret

PSEUDO_END(__syscall)

weak_alias (__syscall, syscall)

==> glibc-2.29-alt2.E2K.26.012.1/sysdeps/unix/sysv/linux/arm/syscall.S <==
        cfi_rel_offset (r5, 4)
        cfi_rel_offset (r6, 8)
        cfi_rel_offset (r7, 12)
        mov     r7, r0
        mov     r0, r1
        mov     r1, r2
        mov     r2, r3
        ldmfd   ip, {r3, r4, r5, r6}
        swi     0x0
        pop     {r4, r5, r6, r7}
        cfi_adjust_cfa_offset (-16)
        cfi_restore (r4)
        cfi_restore (r5)
        cfi_restore (r6)
        cfi_restore (r7)
        cmn     r0, #4096
        it      cc
        RETINSTR(cc, lr)
        b       PLTJMP(syscall_error)
PSEUDO_END (syscall)

==> glibc-2.29-alt2.E2K.26.012.1/sysdeps/unix/sysv/linux/e2k/syscall.S <==
        setwd   wsz = 0x9
        setbn   rsz = 0x3, rbs = 0x5, rcur = 0x0
        getsp   0x0, %r7

        __SYSCALL_ARG_MEM (%r7, 0x0, %b[0])
        __SYSCALL_ARG_MEM (%r7, 0x8, %b[1])
        __SYSCALL_ARG_MEM (%r7, 0x10, %b[2])
        __SYSCALL_ARG_MEM (%r7, 0x18, %b[3])
        __SYSCALL_ARG_MEM (%r7, 0x20, %b[4])
        __SYSCALL_ARG_MEM (%r7, 0x28, %b[5])
        __SYSCALL_ARG_MEM (%r7, 0x30, %b[6])

        sdisp   %ctpr1, __SYSCALL_TRAPNUM
        call    %ctpr1, wbs = 0x5

        __SYSCALL_OUTPUT

        ret

PSEUDO_END (syscall)

==> glibc-2.29-alt2.E2K.26.012.1/sysdeps/unix/sysv/linux/i386/syscall.S <==
   <http://www.gnu.org/licenses/>.  */

#include <sysdep.h>

/* Please consult the file sysdeps/unix/sysv/linux/i386/sysdep.h for
   more information about the value -4095 used below.*/

        .text
ENTRY (syscall)

        PUSHARGS_6              /* Save register contents.  */
        _DOARGS_6(44)           /* Load arguments.  */
        movl 20(%esp), %eax     /* Load syscall number into %eax.  */
        ENTER_KERNEL            /* Do the system call.  */
        POPARGS_6               /* Restore register contents.  */
        cmpl $-4095, %eax       /* Check %eax for error.  */
        jae SYSCALL_ERROR_LABEL /* Jump to error handler if error.  */
        ret                     /* Return to caller.  */

PSEUDO_END (syscall)

==> glibc-2.29-alt2.E2K.26.012.1/sysdeps/unix/sysv/linux/ia64/syscall.S <==

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, see
   <http://www.gnu.org/licenses/>.  */

#include <sysdep.h>

ENTRY(syscall)
        /* We are called like so:
           {out0,out1,...,out6} registers -> {NR, arg1, ..., arg6}
           Shift the register window so that {out1...out6} are available
           in {out0...out5} like the kernel syscall handler expects.  */
        alloc r2=ar.pfs,1,0,8,0
        mov r15=r32             /* syscall number */
        break __BREAK_SYSCALL
        ;;
        cmp.ne p6,p0=-1,r10     /* r10 = -1 on error */
(p6)    ret
        br.cond.spnt.few __syscall_error
PSEUDO_END(syscall)

==> glibc-2.29-alt2.E2K.26.012.1/sysdeps/unix/sysv/linux/m68k/syscall.S <==

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library.  If not, see
   <http://www.gnu.org/licenses/>.  */

#include <sysdep.h>

/* Please consult the file sysdeps/unix/sysv/linux/m68k/sysdep.h for
   more information about the value -4095 used below.*/

        .text
ENTRY (syscall)
        move.l 4(%sp), %d0      /* Load syscall number.  */
        _DOARGS_6 (28)          /* Frob arguments.  */
        trap &0                 /* Do the system call.  */
        UNDOARGS_6              /* Unfrob arguments.  */
        cmp.l &-4095, %d0       /* Check %d0 for error.  */
        jcc SYSCALL_ERROR_LABEL /* Jump to error handler if negative.  */
        rts                     /* Return to caller.  */
PSEUDO_END (syscall)

==> glibc-2.29-alt2.E2K.26.012.1/sysdeps/unix/sysv/linux/microblaze/syscall.S <==

#include <sysdep.h>

        .text
ENTRY (syscall)
        addk    r12,r0,r5
        addk    r5,r0,r6
        addk    r6,r0,r7
        addk    r7,r0,r8
        addk    r8,r0,r9
        addk    r9,r0,r10
        lwi     r10,r1,28
        brki    r14,8
        addk    r0,r0,r0
        addik   r4,r0,-4095
        cmpu    r4,r4,r3
        bgei    r4,SYSCALL_ERROR_LABEL
        rtsd    r15,8
        nop
PSEUDO_END (syscall)

==> glibc-2.29-alt2.E2K.26.012.1/sysdeps/unix/sysv/linux/nios2/syscall.S <==
   <http://www.gnu.org/licenses/>.  */

#include <sysdep.h>

/* We don't need a special syscall to implement syscall().  It won't work
   reliably with 64-bit arguments (but that is true on many modern platforms).
*/

ENTRY (syscall)
        mov     r2, r4
        mov     r4, r5
        mov     r5, r6
        mov     r6, r7
        ldw     r7, 0(sp)
        ldw     r8, 4(sp)
        ldw     r9, 8(sp)
        trap
        bne     r7, zero, SYSCALL_ERROR_LABEL
        ret
PSEUDO_END (syscall)

==> glibc-2.29-alt2.E2K.26.012.1/sysdeps/unix/sysv/linux/powerpc/syscall.S <==
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, see
   <http://www.gnu.org/licenses/>.  */

#include <sysdep.h>

ENTRY (syscall)
        mr   r0,r3
        mr   r3,r4
        mr   r4,r5
        mr   r5,r6
        mr   r6,r7
        mr   r7,r8
        mr   r8,r9
        sc
        PSEUDO_RET
PSEUDO_END (syscall)

==> glibc-2.29-alt2.E2K.26.012.1/sysdeps/unix/sysv/linux/sh/syscall.S <==
        mov     r7, r6                  // p3
        mov.l   @r15, r7                // p4
        mov.l   @(4,r15), r0            // p5
        mov.l   @(8,r15), r1            // p6
        mov.l   @(12,r15), r2           // p7
        trapa   #0x17
        mov     r0, r1
        mov     #-12, r2
        shad    r2, r1
        not     r1, r1                  // r1=0 means r0 = -1 to -4095
        tst     r1, r1                  // i.e. error in linux
        bf      .Lpseudo_end
        SYSCALL_ERROR_HANDLER
.Lpseudo_end:
        rts
         nop

PSEUDO_END (__syscall)

weak_alias (__syscall, syscall)

==> glibc-2.29-alt2.E2K.26.012.1/sysdeps/unix/sysv/linux/x86_64/syscall.S <==
/* Usage: long syscall (syscall_number, arg1, arg2, arg3, arg4, arg5, arg6)
   We need to do some arg shifting, the syscall_number will be in
   rax.  */


        .text
ENTRY (syscall)
        movq %rdi, %rax         /* Syscall number -> rax.  */
        movq %rsi, %rdi         /* shift arg1 - arg5.  */
        movq %rdx, %rsi
        movq %rcx, %rdx
        movq %r8, %r10
        movq %r9, %r8
        movq 8(%rsp),%r9        /* arg6 is on the stack.  */
        syscall                 /* Do the system call.  */
        cmpq $-4095, %rax       /* Check %rax for error.  */
        jae SYSCALL_ERROR_LABEL /* Jump to error handler if error.  */
        ret                     /* Return to caller.  */

PSEUDO_END (syscall)
Comment 5 Ivan Zakharyaschev 2023-06-02 18:26:28 MSK
> Это UB только теоретически.

Проявлений на практике не было описано.

Исправлять не надо,...

> ведь даже в реализациях syscall.S в glibc тупо загружают для системного вызова максимум аргументов вне зависимости от конкретного вызова. Т.е. эту задачу не пытаются решить.