diff options
Diffstat (limited to 'kernel/compat.c')
| -rw-r--r-- | kernel/compat.c | 111 |
1 files changed, 72 insertions, 39 deletions
diff --git a/kernel/compat.c b/kernel/compat.c index 32c254a8ab9a..8eafe3eb50d9 100644 --- a/kernel/compat.c +++ b/kernel/compat.c | |||
| @@ -23,9 +23,68 @@ | |||
| 23 | #include <linux/timex.h> | 23 | #include <linux/timex.h> |
| 24 | #include <linux/migrate.h> | 24 | #include <linux/migrate.h> |
| 25 | #include <linux/posix-timers.h> | 25 | #include <linux/posix-timers.h> |
| 26 | #include <linux/times.h> | ||
| 26 | 27 | ||
| 27 | #include <asm/uaccess.h> | 28 | #include <asm/uaccess.h> |
| 28 | 29 | ||
| 30 | /* | ||
| 31 | * Note that the native side is already converted to a timespec, because | ||
| 32 | * that's what we want anyway. | ||
| 33 | */ | ||
| 34 | static int compat_get_timeval(struct timespec *o, | ||
| 35 | struct compat_timeval __user *i) | ||
| 36 | { | ||
| 37 | long usec; | ||
| 38 | |||
| 39 | if (get_user(o->tv_sec, &i->tv_sec) || | ||
| 40 | get_user(usec, &i->tv_usec)) | ||
| 41 | return -EFAULT; | ||
| 42 | o->tv_nsec = usec * 1000; | ||
| 43 | return 0; | ||
| 44 | } | ||
| 45 | |||
| 46 | static int compat_put_timeval(struct compat_timeval __user *o, | ||
| 47 | struct timeval *i) | ||
| 48 | { | ||
| 49 | return (put_user(i->tv_sec, &o->tv_sec) || | ||
| 50 | put_user(i->tv_usec, &o->tv_usec)) ? -EFAULT : 0; | ||
| 51 | } | ||
| 52 | |||
| 53 | asmlinkage long compat_sys_gettimeofday(struct compat_timeval __user *tv, | ||
| 54 | struct timezone __user *tz) | ||
| 55 | { | ||
| 56 | if (tv) { | ||
| 57 | struct timeval ktv; | ||
| 58 | do_gettimeofday(&ktv); | ||
| 59 | if (compat_put_timeval(tv, &ktv)) | ||
| 60 | return -EFAULT; | ||
| 61 | } | ||
| 62 | if (tz) { | ||
| 63 | if (copy_to_user(tz, &sys_tz, sizeof(sys_tz))) | ||
| 64 | return -EFAULT; | ||
| 65 | } | ||
| 66 | |||
| 67 | return 0; | ||
| 68 | } | ||
| 69 | |||
| 70 | asmlinkage long compat_sys_settimeofday(struct compat_timeval __user *tv, | ||
| 71 | struct timezone __user *tz) | ||
| 72 | { | ||
| 73 | struct timespec kts; | ||
| 74 | struct timezone ktz; | ||
| 75 | |||
| 76 | if (tv) { | ||
| 77 | if (compat_get_timeval(&kts, tv)) | ||
| 78 | return -EFAULT; | ||
| 79 | } | ||
| 80 | if (tz) { | ||
| 81 | if (copy_from_user(&ktz, tz, sizeof(ktz))) | ||
| 82 | return -EFAULT; | ||
| 83 | } | ||
| 84 | |||
| 85 | return do_sys_settimeofday(tv ? &kts : NULL, tz ? &ktz : NULL); | ||
| 86 | } | ||
| 87 | |||
| 29 | int get_compat_timespec(struct timespec *ts, const struct compat_timespec __user *cts) | 88 | int get_compat_timespec(struct timespec *ts, const struct compat_timespec __user *cts) |
| 30 | { | 89 | { |
| 31 | return (!access_ok(VERIFY_READ, cts, sizeof(*cts)) || | 90 | return (!access_ok(VERIFY_READ, cts, sizeof(*cts)) || |
| @@ -150,49 +209,23 @@ asmlinkage long compat_sys_setitimer(int which, | |||
| 150 | return 0; | 209 | return 0; |
| 151 | } | 210 | } |
| 152 | 211 | ||
| 212 | static compat_clock_t clock_t_to_compat_clock_t(clock_t x) | ||
| 213 | { | ||
| 214 | return compat_jiffies_to_clock_t(clock_t_to_jiffies(x)); | ||
| 215 | } | ||
| 216 | |||
| 153 | asmlinkage long compat_sys_times(struct compat_tms __user *tbuf) | 217 | asmlinkage long compat_sys_times(struct compat_tms __user *tbuf) |
| 154 | { | 218 | { |
| 155 | /* | ||
| 156 | * In the SMP world we might just be unlucky and have one of | ||
| 157 | * the times increment as we use it. Since the value is an | ||
| 158 | * atomically safe type this is just fine. Conceptually its | ||
| 159 | * as if the syscall took an instant longer to occur. | ||
| 160 | */ | ||
| 161 | if (tbuf) { | 219 | if (tbuf) { |
| 220 | struct tms tms; | ||
| 162 | struct compat_tms tmp; | 221 | struct compat_tms tmp; |
| 163 | struct task_struct *tsk = current; | 222 | |
| 164 | struct task_struct *t; | 223 | do_sys_times(&tms); |
| 165 | cputime_t utime, stime, cutime, cstime; | 224 | /* Convert our struct tms to the compat version. */ |
| 166 | 225 | tmp.tms_utime = clock_t_to_compat_clock_t(tms.tms_utime); | |
| 167 | read_lock(&tasklist_lock); | 226 | tmp.tms_stime = clock_t_to_compat_clock_t(tms.tms_stime); |
| 168 | utime = tsk->signal->utime; | 227 | tmp.tms_cutime = clock_t_to_compat_clock_t(tms.tms_cutime); |
| 169 | stime = tsk->signal->stime; | 228 | tmp.tms_cstime = clock_t_to_compat_clock_t(tms.tms_cstime); |
| 170 | t = tsk; | ||
| 171 | do { | ||
| 172 | utime = cputime_add(utime, t->utime); | ||
| 173 | stime = cputime_add(stime, t->stime); | ||
| 174 | t = next_thread(t); | ||
| 175 | } while (t != tsk); | ||
| 176 | |||
| 177 | /* | ||
| 178 | * While we have tasklist_lock read-locked, no dying thread | ||
| 179 | * can be updating current->signal->[us]time. Instead, | ||
| 180 | * we got their counts included in the live thread loop. | ||
| 181 | * However, another thread can come in right now and | ||
| 182 | * do a wait call that updates current->signal->c[us]time. | ||
| 183 | * To make sure we always see that pair updated atomically, | ||
| 184 | * we take the siglock around fetching them. | ||
| 185 | */ | ||
| 186 | spin_lock_irq(&tsk->sighand->siglock); | ||
| 187 | cutime = tsk->signal->cutime; | ||
| 188 | cstime = tsk->signal->cstime; | ||
| 189 | spin_unlock_irq(&tsk->sighand->siglock); | ||
| 190 | read_unlock(&tasklist_lock); | ||
| 191 | |||
| 192 | tmp.tms_utime = compat_jiffies_to_clock_t(cputime_to_jiffies(utime)); | ||
| 193 | tmp.tms_stime = compat_jiffies_to_clock_t(cputime_to_jiffies(stime)); | ||
| 194 | tmp.tms_cutime = compat_jiffies_to_clock_t(cputime_to_jiffies(cutime)); | ||
| 195 | tmp.tms_cstime = compat_jiffies_to_clock_t(cputime_to_jiffies(cstime)); | ||
| 196 | if (copy_to_user(tbuf, &tmp, sizeof(tmp))) | 229 | if (copy_to_user(tbuf, &tmp, sizeof(tmp))) |
| 197 | return -EFAULT; | 230 | return -EFAULT; |
| 198 | } | 231 | } |
