Gentoo Websites Logo
Go to: Gentoo Home Documentation Forums Lists Bugs Planet Store Wiki Get Gentoo!
Bug 473444 - [PATCH] fs: generic_file_llseek_size() should recognize invalid whence values
Summary: [PATCH] fs: generic_file_llseek_size() should recognize invalid whence values
Status: RESOLVED INVALID
Alias: None
Product: Gentoo Linux
Classification: Unclassified
Component: [OLD] Core system (show other bugs)
Hardware: All Linux
: Low trivial (vote)
Assignee: Gentoo Kernel Bug Wranglers and Kernel Maintainers
URL: http://marc.info/?l=linux-fsdevel&m=1...
Whiteboard:
Keywords: Bug, PATCH, UPSTREAM
Depends on:
Blocks:
 
Reported: 2013-06-16 01:10 UTC by Richard Yao (RETIRED)
Modified: 2013-06-16 05:26 UTC (History)
1 user (show)

See Also:
Package list:
Runtime testing required: ---


Attachments
Patch to fix issue (0001-fs-generic_file_llseek_size-should-recognize-invalid.patch,1.14 KB, patch)
2013-06-16 01:10 UTC, Richard Yao (RETIRED)
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Richard Yao (RETIRED) gentoo-dev 2013-06-16 01:10:45 UTC
Created attachment 351072 [details, diff]
Patch to fix issue

generic_file_llseek_size() handles whence values in a switch statement,
but it lacks cases for both SEEK_SET and invalid values. This causes it
to treat all invalid whence values as SEEK_SET, which is wrong.

I have sent this patch upstream, but it should be safe to apply without any approval from upstream. The official documentation is clear that EINVAL should be returned on an invalid whence value:

http://linux.die.net/man/2/llseek

With that said, all kernels in the main tree are affected. However, generic_file_llseek_size() was called generic_file_llseek_unlocked() in 2.6.32.
Comment 1 Sergei Trofimovich (RETIRED) gentoo-dev 2013-06-16 04:37:56 UTC
Care to post exact filesystem name and exact source-file reproducer?

The manpage you linked is outdated.
It does not mention SEEK_DATA / SEEK_HOLE
(man 2 lseek does though).

Value sanitizers live in syscal calls:

fs/read_write.c:

SYSCALL_DEFINE3(lseek, unsigned int, fd, off_t, offset, unsigned int, whence)
{
        off_t retval;
        struct fd f = fdget(fd);
        if (!f.file)
                return -EBADF;

        retval = -EINVAL;
        if (whence <= SEEK_MAX) {
                loff_t res = vfs_llseek(f.file, offset, whence);
                retval = res;
                if (res != (loff_t)retval)
                        retval = -EOVERFLOW;    /* LFS: should only happen on 32 bit platforms */
        }
        fdput(f);
        return retval;
}


#ifdef CONFIG_COMPAT
COMPAT_SYSCALL_DEFINE3(lseek, unsigned int, fd, compat_off_t, offset, unsigned int, whence)
{
        return sys_lseek(fd, offset, whence);
}
#endif

#ifdef __ARCH_WANT_SYS_LLSEEK
SYSCALL_DEFINE5(llseek, unsigned int, fd, unsigned long, offset_high,
                unsigned long, offset_low, loff_t __user *, result,
                unsigned int, whence)
{
        int retval;
        struct fd f = fdget(fd);
        loff_t offset;

        if (!f.file)
                return -EBADF;

        retval = -EINVAL;
        if (whence > SEEK_MAX)
                goto out_putf;

        offset = vfs_llseek(f.file, ((loff_t) offset_high << 32) | offset_low,
                        whence);

        retval = (int)offset;
        if (offset >= 0) {
                retval = -EFAULT;
                if (!copy_to_user(result, &offset, sizeof(offset)))
                        retval = 0;
        }
out_putf:
        fdput(f);
        return retval;
}
#endif

include/uapi/linux/fs.h:

#define SEEK_SET        0       /* seek relative to beginning of file */
#define SEEK_CUR        1       /* seek relative to current file position */
#define SEEK_END        2       /* seek relative to end of file */
#define SEEK_DATA       3       /* seek to the next data */
#define SEEK_HOLE       4       /* seek to the next hole */
#define SEEK_MAX        SEEK_HOLE
Comment 2 Richard Yao (RETIRED) gentoo-dev 2013-06-16 05:26:02 UTC
(In reply to Sergei Trofimovich from comment #1)
> Care to post exact filesystem name and exact source-file reproducer?
> 
> The manpage you linked is outdated.
> It does not mention SEEK_DATA / SEEK_HOLE
> (man 2 lseek does though).
> 
> Value sanitizers live in syscal calls:
> 
> fs/read_write.c:
> 
> SYSCALL_DEFINE3(lseek, unsigned int, fd, off_t, offset, unsigned int, whence)
> {
>         off_t retval;
>         struct fd f = fdget(fd);
>         if (!f.file)
>                 return -EBADF;
> 
>         retval = -EINVAL;
>         if (whence <= SEEK_MAX) {
>                 loff_t res = vfs_llseek(f.file, offset, whence);
>                 retval = res;
>                 if (res != (loff_t)retval)
>                         retval = -EOVERFLOW;    /* LFS: should only happen
> on 32 bit platforms */
>         }
>         fdput(f);
>         return retval;
> }
> 
> 
> #ifdef CONFIG_COMPAT
> COMPAT_SYSCALL_DEFINE3(lseek, unsigned int, fd, compat_off_t, offset,
> unsigned int, whence)
> {
>         return sys_lseek(fd, offset, whence);
> }
> #endif
> 
> #ifdef __ARCH_WANT_SYS_LLSEEK
> SYSCALL_DEFINE5(llseek, unsigned int, fd, unsigned long, offset_high,
>                 unsigned long, offset_low, loff_t __user *, result,
>                 unsigned int, whence)
> {
>         int retval;
>         struct fd f = fdget(fd);
>         loff_t offset;
> 
>         if (!f.file)
>                 return -EBADF;
> 
>         retval = -EINVAL;
>         if (whence > SEEK_MAX)
>                 goto out_putf;
> 
>         offset = vfs_llseek(f.file, ((loff_t) offset_high << 32) |
> offset_low,
>                         whence);
> 
>         retval = (int)offset;
>         if (offset >= 0) {
>                 retval = -EFAULT;
>                 if (!copy_to_user(result, &offset, sizeof(offset)))
>                         retval = 0;
>         }
> out_putf:
>         fdput(f);
>         return retval;
> }
> #endif
> 
> include/uapi/linux/fs.h:
> 
> #define SEEK_SET        0       /* seek relative to beginning of file */
> #define SEEK_CUR        1       /* seek relative to current file position */
> #define SEEK_END        2       /* seek relative to end of file */
> #define SEEK_DATA       3       /* seek to the next data */
> #define SEEK_HOLE       4       /* seek to the next hole */
> #define SEEK_MAX        SEEK_HOLE

It had not occurred to me to check the VFS code between the filesystem interface and the llseek() implementation for additional sanitization. It appears that the VFS calls themselves have checks to prevent this, which means that this will not happen, but it is remarkably ugly code. It should be cleaned up at upstream.

Next time, I will make time to write a test case. Thanks for looking into this.