Re: [PATCH v2 2/3] libc: fix sign extension in fallocate()

Vineet Gupta <[email protected]> Wed, 23 Sep 2015 03:36:58 +0000
Newsgroups gmane.comp.lib.uclibc.general
Message-ID <C2D7FE5348E1B147BCA15975FBA23075D78186B7@IN01WEMBXB.internal.synopsys.com>
On Tuesday 22 September 2015 11:46 PM, Yuriy Kolerov wrote:
> Arguments offset and len in fallocate system call in Linux
> kernel have 64-bit types. 

Perhaps better to say that for common generic syscall ABI fallocate syscall
handler in kernel expects 64 bit signed args for offset, len

> However these arguments in fallocate
> wrapper in uClibc may have 32-bit types. In this case it's
> necessary to pass two 32-bit words to the systemc call for every
> argument - an actual argument and its sign extension.

No - this is not technically accurate. User space in theory can pass a true 64 bit
value, not just a 32 bit value and a 32 bit sign extension.

> Thus high word of 64-bit value must be 0 or 0xFFFFFFFF depending
> on sign of the original 32-bit value (offset or len). It is how
> sign extansion works - all high bits of the negative value must be 1.
>
> fallocate does sign extension incorrectly when 32 bit values are
> passed (offset or len). It just fills the second word of 64-bit
> value by zeros. E.g. fallocate works incorrectly when offset or
> length is negative value - in this case kernel thinks that positive
> values are passed.

Again while the fix may be obvious, can u divulge the specific test case it fixes.


> Signed-off-by: Yuriy Kolerov <[email protected]>
> ---
>  libc/sysdeps/linux/common/fallocate.c | 10 ++++------
>  1 file changed, 4 insertions(+), 6 deletions(-)
>
> diff --git a/libc/sysdeps/linux/common/fallocate.c b/libc/sysdeps/linux/common/fallocate.c
> index b2309e9..446d9ef 100644
> --- a/libc/sysdeps/linux/common/fallocate.c
> +++ b/libc/sysdeps/linux/common/fallocate.c
> @@ -21,14 +21,12 @@ int attribute_hidden __libc_fallocate(int fd, int mode, __off_t offset, __off_t
>  	int ret;
>  
>  # if __WORDSIZE == 32
> -	uint32_t off_low = offset;
> -	uint32_t len_low = len;
> -	/* may assert that these >>31 are 0 */
> -	uint32_t zero = 0;
> +	int32_t offset_sign_extension = (offset < 0) ? 0xFFFFFFFF : 0;
> +	int32_t len_sign_extension = (len < 0) ? 0xFFFFFFFF : 0;
>  	INTERNAL_SYSCALL_DECL(err);
>  	ret = (int) (INTERNAL_SYSCALL(fallocate, err, 6, fd, mode,
> -		__LONG_LONG_PAIR (zero, off_low),
> -		__LONG_LONG_PAIR (zero, len_low)));
> +		__LONG_LONG_PAIR (offset_sign_extension, offset),
> +		__LONG_LONG_PAIR (len_sign_extension, len)));

This is too ugly.  Can u use existing macro OFF64_HI_LO() macro.
Something like below:

int64_t offset_ll = offset ; /* does appropriate sign extension */
int64_t len_ll = len;

ret = (int) (INTERNAL_SYSCALL(fallocate, err, 6, fd, mode,
            OFF64_HI_LO(offset_ll),
            OFF64_HI_LO(len_ll));


>  # elif __WORDSIZE == 64
>  	INTERNAL_SYSCALL_DECL(err);
>  	ret = (int) (INTERNAL_SYSCALL(fallocate, err, 4, fd, mode, offset, len));