Skip to content

Commit

Permalink
compat: Add helper functions to read/write struct timeval, timespec
Browse files Browse the repository at this point in the history
Add helper functions to read and write struct timeval and struct
timespec from userspace.  We already had helper functions for reading
and writing struct compat_timespec; add a set of functions to do the
same with struct timeval, and add a second suite of functions which
can be sensitive to COMPAT_USE_64BIT_TIME and access either 32- or
64-bit time structures.

This also exports these helper functions to modules.

Rename the existing inlines for converting between struct
compat_timeval and native struct timespec so we can have a saner
naming convention for the exported functions.

Suggested-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: H. Peter Anvin <hpa@zytor.com>
  • Loading branch information
H. Peter Anvin committed Feb 20, 2012
1 parent 45e8778 commit 6684ba2
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 8 deletions.
16 changes: 16 additions & 0 deletions include/linux/compat.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,26 @@ typedef struct {
compat_sigset_word sig[_COMPAT_NSIG_WORDS];
} compat_sigset_t;

/*
* These functions operate strictly on struct compat_time*
*/
extern int get_compat_timespec(struct timespec *,
const struct compat_timespec __user *);
extern int put_compat_timespec(const struct timespec *,
struct compat_timespec __user *);
extern int get_compat_timeval(struct timeval *,
const struct compat_timeval __user *);
extern int put_compat_timeval(const struct timeval *,
struct compat_timeval __user *);
/*
* These functions operate on 32- or 64-bit specs depending on
* COMPAT_USE_64BIT_TIME, hence the void user pointer arguments and the
* naming as compat_get/put_ rather than get/put_compat_.
*/
extern int compat_get_timespec(struct timespec *, const void __user *);
extern int compat_put_timespec(const struct timespec *, void __user *);
extern int compat_get_timeval(struct timeval *, const void __user *);
extern int compat_put_timeval(const struct timeval *, void __user *);

struct compat_iovec {
compat_uptr_t iov_base;
Expand Down
68 changes: 60 additions & 8 deletions kernel/compat.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,10 @@
#include <asm/uaccess.h>

/*
* Note that the native side is already converted to a timespec, because
* that's what we want anyway.
* Get/set struct timeval with struct timespec on the native side
*/
static int compat_get_timeval(struct timespec *o,
struct compat_timeval __user *i)
static int compat_get_timeval_convert(struct timespec *o,
struct compat_timeval __user *i)
{
long usec;

Expand All @@ -46,8 +45,8 @@ static int compat_get_timeval(struct timespec *o,
return 0;
}

static int compat_put_timeval(struct compat_timeval __user *o,
struct timeval *i)
static int compat_put_timeval_convert(struct compat_timeval __user *o,
struct timeval *i)
{
return (put_user(i->tv_sec, &o->tv_sec) ||
put_user(i->tv_usec, &o->tv_usec)) ? -EFAULT : 0;
Expand Down Expand Up @@ -117,7 +116,7 @@ asmlinkage long compat_sys_gettimeofday(struct compat_timeval __user *tv,
if (tv) {
struct timeval ktv;
do_gettimeofday(&ktv);
if (compat_put_timeval(tv, &ktv))
if (compat_put_timeval_convert(tv, &ktv))
return -EFAULT;
}
if (tz) {
Expand All @@ -135,7 +134,7 @@ asmlinkage long compat_sys_settimeofday(struct compat_timeval __user *tv,
struct timezone ktz;

if (tv) {
if (compat_get_timeval(&kts, tv))
if (compat_get_timeval_convert(&kts, tv))
return -EFAULT;
}
if (tz) {
Expand All @@ -146,12 +145,29 @@ asmlinkage long compat_sys_settimeofday(struct compat_timeval __user *tv,
return do_sys_settimeofday(tv ? &kts : NULL, tz ? &ktz : NULL);
}

int get_compat_timeval(struct timeval *tv, const struct compat_timeval __user *ctv)
{
return (!access_ok(VERIFY_READ, ctv, sizeof(*ctv)) ||
__get_user(tv->tv_sec, &ctv->tv_sec) ||
__get_user(tv->tv_usec, &ctv->tv_usec)) ? -EFAULT : 0;
}
EXPORT_SYMBOL_GPL(get_compat_timeval);

int put_compat_timeval(const struct timeval *tv, struct compat_timeval __user *ctv)
{
return (!access_ok(VERIFY_WRITE, ctv, sizeof(*ctv)) ||
__put_user(tv->tv_sec, &ctv->tv_sec) ||
__put_user(tv->tv_usec, &ctv->tv_usec)) ? -EFAULT : 0;
}
EXPORT_SYMBOL_GPL(put_compat_timeval);

int get_compat_timespec(struct timespec *ts, const struct compat_timespec __user *cts)
{
return (!access_ok(VERIFY_READ, cts, sizeof(*cts)) ||
__get_user(ts->tv_sec, &cts->tv_sec) ||
__get_user(ts->tv_nsec, &cts->tv_nsec)) ? -EFAULT : 0;
}
EXPORT_SYMBOL_GPL(get_compat_timespec);

int put_compat_timespec(const struct timespec *ts, struct compat_timespec __user *cts)
{
Expand All @@ -161,6 +177,42 @@ int put_compat_timespec(const struct timespec *ts, struct compat_timespec __user
}
EXPORT_SYMBOL_GPL(put_compat_timespec);

int compat_get_timeval(struct timeval *tv, const void __user *utv)
{
if (COMPAT_USE_64BIT_TIME)
return copy_from_user(tv, utv, sizeof *tv) ? -EFAULT : 0;
else
return get_compat_timeval(tv, utv);
}
EXPORT_SYMBOL_GPL(compat_get_timeval);

int compat_put_timeval(const struct timeval *tv, void __user *utv)
{
if (COMPAT_USE_64BIT_TIME)
return copy_to_user(utv, tv, sizeof *tv) ? -EFAULT : 0;
else
return put_compat_timeval(tv, utv);
}
EXPORT_SYMBOL_GPL(compat_put_timeval);

int compat_get_timespec(struct timespec *ts, const void __user *uts)
{
if (COMPAT_USE_64BIT_TIME)
return copy_from_user(ts, uts, sizeof *ts) ? -EFAULT : 0;
else
return get_compat_timespec(ts, uts);
}
EXPORT_SYMBOL_GPL(compat_get_timespec);

int compat_put_timespec(const struct timespec *ts, void __user *uts)
{
if (COMPAT_USE_64BIT_TIME)
return copy_to_user(uts, ts, sizeof *ts) ? -EFAULT : 0;
else
return put_compat_timespec(ts, uts);
}
EXPORT_SYMBOL_GPL(compat_put_timespec);

static long compat_nanosleep_restart(struct restart_block *restart)
{
struct compat_timespec __user *rmtp;
Expand Down

0 comments on commit 6684ba2

Please sign in to comment.