Summary: | [PATCH] fs: generic_file_llseek_size() should recognize invalid whence values | ||
---|---|---|---|
Product: | Gentoo Linux | Reporter: | Richard Yao (RETIRED) <ryao> |
Component: | [OLD] Core system | Assignee: | Gentoo Kernel Bug Wranglers and Kernel Maintainers <kernel> |
Status: | RESOLVED INVALID | ||
Severity: | trivial | CC: | slyfox |
Priority: | Low | Keywords: | Bug, PATCH, UPSTREAM |
Version: | unspecified | ||
Hardware: | All | ||
OS: | Linux | ||
URL: | http://marc.info/?l=linux-fsdevel&m=137134416829849&w=2 | ||
Whiteboard: | |||
Package list: | Runtime testing required: | --- | |
Attachments: | Patch to fix issue |
Description
Richard Yao (RETIRED)
2013-06-16 01:10:45 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 (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. |