Bug 8611

Summary: syscalls with 6 arguments broken on x86 32bit
Product: uClibc Reporter: Ronald Wahl <ronald.wahl>
Component: OtherAssignee: unassigned
Status: NEW ---    
Severity: major CC: uclibc-cvs
Priority: P5    
Version: 0.9.33.2   
Target Milestone: ---   
Hardware: PC   
OS: Linux   
Host: Target:
Build:
Attachments: fix/workaround for broken syscall6 on x86 32bit
small test tool that helps to verify the fix

Description Ronald Wahl 2016-01-15 14:30:20 UTC
Created attachment 6281 [details]
fix/workaround for broken syscall6 on x86 32bit

Syscalls with 6 arguments are currently broken or at least fragile on x86 32bit.
This becomes a larger issue especially when using linux 4.3+ where the socket calls like sendto() are directly accessible.

In case of sendto the address len parameter is not correctly pushed to the kernel (can be checked with a recent version of strace that supports the new direct syscalls). This may sometimes not lead to malfunctioning code immediately.

Copying the comment I added in the code here:

Syscalls with 6 arguments on x86/32bit arch need the 6th argument in the ebp register. This register must been saved on the stack before assigning the argument value. This actually happens inside inline assembler code. The actual argument to be placed in ebp may itself being referenced via esp register. Since pushing ebp will change esp behind gcc's back the value stored in ebp may be wrong. It looks like gcc uses ebp when refering to stack arguments in case
of an existing frame pointer. So we enforce frame pointers in that case. We also need to specify the noinline attribute.

The attached patch is just a workaround that is working for me with gcc 4.8.3 but is no guarantee that it works on all cases and all affected syscalls.

As far as I know glibc used a special hack for 6arg-syscalls by using a helper macro that gets the first args in registers and all additional args via a structure or array and the helper does the register setup for the actual syscall.
Comment 1 Ronald Wahl 2016-01-26 10:47:51 UTC
Created attachment 6291 [details]
small test tool that helps to verify the fix

The example is incomplete as there might be more 6arg syscalls like fallocate that I havn't in this example. The file can be compiled with gcc on a 32bit x86 platform.

gcc -o syscall6-test syscall6-test.c

Prerequirement:

kernel 4.3+
uClibc build against kernel headers of linux 4.3+
strace 4.11+ (not sure if rebuild against linux 4.3 is necessary)

The interesting value is not the return value but the 6th parameter of the last parameter of the syscalls below which is not as specified in the fail case except for sync_file_range.

Fail case:
$ strace -e splice,sync_file_range,sendto,recvfrom syscall6_test 
splice(10, NULL, 11, NULL, 0, 0)        = 0
sync_file_range(10, 0, 0, SYNC_FILE_RANGE_WAIT_BEFORE|SYNC_FILE_RANGE_WAIT_AFTER) = -1 EBADF (Bad file descriptor)
sendto(4, "\250\0\0\0\374_y\267xX\256\277(Uy\2674X\256\277\0\20y\267\0\0\0\0\0\20y\267"..., 1024, 0, 0xbfae5780, 0) = -1 EINVAL (Invalid argument)
sendto() failed
+++ exited with 0 +++

Working case:
$ strace -e splice,sync_file_range,sendto,recvfrom syscall6_test 
splice(10, NULL, 11, NULL, 0, SPLICE_F_MOVE|SPLICE_F_NONBLOCK|SPLICE_F_MORE|SPLICE_F_GIFT) = 0
sync_file_range(10, 0, 0, SYNC_FILE_RANGE_WAIT_BEFORE|SYNC_FILE_RANGE_WAIT_AFTER) = -1 EBADF (Bad file descriptor)
sendto(4, "\250\0\0\0\374\237p\267\330\224\223\277(\225p\267\224\224\223\277\0Pp\267\0\0\0\0\0Pp\267"..., 1024, 0, {sa_family=AF_INET, sin_port=htons(46279), sin_addr=inet_addr("127.0.0.1")}, 16) = 1024
recvfrom(3, "\250\0\0\0\374\237p\267\330\224\223\277(\225p\267\224\224\223\277\0Pp\267\0\0\0\0\0Pp\267"..., 1024, 0, {sa_family=AF_INET, sin_port=htons(40050), sin_addr=inet_addr("127.0.0.1")}, [16]) = 1024