[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#618562: syscall in libc6 is broken for __NR_fanotify_mark

Hi Aurelien,

Got it, thanks for the explanation.

Just curious, does the same problem exist on X86-64(64bit kernel + 32 bit userland)? If yes, should the caller of syscall() need to split each 64bit argument into two 32bit arguments?

2011/3/26 Aurelien Jarno <aurelien@aurel32.net>
On Wed, Mar 16, 2011 at 12:36:30PM +0000, Chen Jie wrote:
> Package: libc6
> Version: 2.11.2-10
> syscall(__NR_fanotify_mark, fanotify_fd, flags, mask, dfd, pathname),
> failed with 'Invalid argument'.
> strace shows 'syscall' didn't pass the correct parameters:
>     syscall(0x10f1, 0x3, 0x11, 0, 0x20, 0, 0xffffff9c, 0x4013e0)
>     ( The expected one is: syscall(0x10f1, 0x3, 0x17, 0x20, 0, -100, 0x4013e0) )

So let me summarize the difference between the real and expected values:
- arg1: 0x10f1 in both cases, no problem
- arg2: 0x3 in both cases, no problem
- arg3: 0x11 in the syscall, 0x17 expected. The problem is actually the
 test program which uses %d to display an hexadecimal number 0x11 = 17.
 No problem on the glibc side here.
- arg4: 0 in the syscall, not expected, will explain below why.
- arg5 (syscall) / arg4 (expected): 0x20 in both cases, no problem
- arg6 (syscall) / arg5 (expected): 0 in both cases, no problem
- arg7 (syscall) / arg6 (expected) 0xffffff9c in the syscall, -100
 expected. This is actually the same value.
- arg8 (syscall) / arg7 (expected): 0x4013e0 in both cases, no problem.

So the only non-explained problem is the 4th argument as displayed by
strace. The syscall() function takes a variable number of arguments, and
pass them to the __NR_syscall syscall (4000), which is the indirect
syscall, as shown in the code below:

  eecd0:       3c1c0009        lui     gp,0x9
  eecd4:       279c5c90        addiu   gp,gp,23696
  eecd8:       0399e021        addu    gp,gp,t9
  eecdc:       24020fa0        li      v0,4000
  eece0:       0000000c        syscall
  eece4:       14e0fff6        bnez    a3,eecc0 <syslog+0x54>
  eece8:       00200825        move    at,at
  eecec:       03e00008        jr      ra
  eecf0:       00200825        move    at,at

syscall() follows the MIPS ABI, which means a 64-bit argument has to be
64-bit aligned. That's why the arguments are organized as below:
- a0:    0x000010f1
- a1:    0x00000003
- a2:    0x00000011
- a3:    not used (due to 64-bit alignement)
- sp+16: 0x00000020
- sp+20: 0x00000000
- sp+24: 0xffffff9c
- sp+28: 0x004013e0

On the kernel side, the arguments are simply shifted by one (to remove
the syscall number), without taking into account the ABI and the real
syscall called. That's why there is an additional argument to the

Now I don't think there is any way to fix it on the libc6 side.
syscall() doesn't know what are the type of the passed arguments, and is
following the MIPS ABI. It might be fixed on the kernel side, but it
means having a table with the type of all arguments. It doesn't seems to
be easy to do.

That said, there is an easy workaround by passing only 32-bit values to
the syscall() function on a 32-bit machine.

Aurelien Jarno                          GPG: 1024D/F1BCDB73
aurelien@aurel32.net                 http://www.aurel32.net

Reply to: