'struct rusage' is not compatible with user space that defines time_t as 64-bit. While there will never be an overflow of the timeval members in this structure, the current definition breaks any program that relies on the member to be a timeval.
This introduces a new struct __kernel_rusage that is defined to match the 64-bit version of struct rusage. 32-bit architectures that use CONFIG_COMPAT_TIME can then use compat_sys_getrusage() etc to get the original structure, while the normal sys_getrusage() function will provide the new layout in both native and compat32 mode.
On 32-bit architectures that do not set CONFIG_COMPAT_TIME, as well as all 64-bit architectures, this patch is intented to have no user-visible impact.
Signed-off-by: Arnd Bergmann arnd@arndb.de --- arch/alpha/kernel/osf_sys.c | 4 ++-- include/linux/compat_time.h | 3 +-- include/linux/resource.h | 8 ++++++-- include/linux/syscalls.h | 7 +++---- include/uapi/linux/resource.h | 32 ++++++++++++++++++++++++++++++++ kernel/compat.c | 42 +++++++++++++++++++++++++++++++++++++----- kernel/exit.c | 6 +++--- kernel/sys.c | 23 ++++++++++++++--------- 8 files changed, 98 insertions(+), 27 deletions(-)
diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c index e51f578636a5..6824e68d32ad 100644 --- a/arch/alpha/kernel/osf_sys.c +++ b/arch/alpha/kernel/osf_sys.c @@ -1166,7 +1166,7 @@ SYSCALL_DEFINE2(osf_getrusage, int, who, struct rusage32 __user *, ru) SYSCALL_DEFINE4(osf_wait4, pid_t, pid, int __user *, ustatus, int, options, struct rusage32 __user *, ur) { - struct rusage r; + struct __kernel_rusage r; long ret, err; unsigned int status = 0; mm_segment_t old_fs; @@ -1178,7 +1178,7 @@ SYSCALL_DEFINE4(osf_wait4, pid_t, pid, int __user *, ustatus, int, options, set_fs (KERNEL_DS); ret = sys_wait4(pid, (unsigned int __user *) &status, options, - (struct rusage __user *) &r); + (struct __kernel_rusage __user *) &r); set_fs (old_fs);
if (!access_ok(VERIFY_WRITE, ur, sizeof(*ur))) diff --git a/include/linux/compat_time.h b/include/linux/compat_time.h index 789b6415e90e..e3b43bd79801 100644 --- a/include/linux/compat_time.h +++ b/include/linux/compat_time.h @@ -141,8 +141,7 @@ struct compat_rusage { compat_long_t ru_nivcsw; };
-struct rusage; -extern int put_compat_rusage(const struct rusage *, +extern int put_compat_rusage(const struct __kernel_rusage *, struct compat_rusage __user *);
static inline int compat_timeval_compare(struct compat_timeval *lhs, diff --git a/include/linux/resource.h b/include/linux/resource.h index 5bc3116e649c..8cdecf204607 100644 --- a/include/linux/resource.h +++ b/include/linux/resource.h @@ -1,12 +1,16 @@ #ifndef _LINUX_RESOURCE_H #define _LINUX_RESOURCE_H
-#include <uapi/linux/resource.h> +#ifndef CONFIG_COMPAT_TIME +#define __kernel_rusage rusage +#endif
+#include <uapi/linux/resource.h>
struct task_struct;
-int getrusage(struct task_struct *p, int who, struct rusage __user *ru); +int getrusage(struct task_struct *p, int who, + struct __kernel_rusage __user *ru); int do_prlimit(struct task_struct *tsk, unsigned int resource, struct rlimit *new_rlim, struct rlimit *old_rlim);
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 71b574b0365e..f3fdc312627b 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -36,7 +36,6 @@ struct old_utsname; struct pollfd; struct rlimit; struct rlimit64; -struct rusage; struct sched_param; struct sched_attr; struct sel_arg_struct; @@ -325,10 +324,10 @@ asmlinkage long sys_kexec_file_load(int kernel_fd, int initrd_fd, asmlinkage long sys_exit(int error_code); asmlinkage long sys_exit_group(int error_code); asmlinkage long sys_wait4(pid_t pid, int __user *stat_addr, - int options, struct rusage __user *ru); + int options, struct __kernel_rusage __user *ru); asmlinkage long sys_waitid(int which, pid_t pid, struct siginfo __user *infop, - int options, struct rusage __user *ru); + int options, struct __kernel_rusage __user *ru); asmlinkage long sys_waitpid(pid_t pid, int __user *stat_addr, int options); asmlinkage long sys_set_tid_address(int __user *tidptr); asmlinkage long sys_futex(u32 __user *uaddr, int op, u32 val, @@ -650,7 +649,7 @@ asmlinkage long sys_setrlimit(unsigned int resource, asmlinkage long sys_prlimit64(pid_t pid, unsigned int resource, const struct rlimit64 __user *new_rlim, struct rlimit64 __user *old_rlim); -asmlinkage long sys_getrusage(int who, struct rusage __user *ru); +asmlinkage long sys_getrusage(int who, struct __kernel_rusage __user *ru); asmlinkage long sys_umask(int mask);
asmlinkage long sys_msgget(key_t key, int msgflg); diff --git a/include/uapi/linux/resource.h b/include/uapi/linux/resource.h index 36fb3b5fb181..c4f3ba44db00 100644 --- a/include/uapi/linux/resource.h +++ b/include/uapi/linux/resource.h @@ -39,6 +39,38 @@ struct rusage { __kernel_long_t ru_nivcsw; /* involuntary " */ };
+ +/* + * __kernel_rusage replaces rusage, and matches the layout of 64-bit rusage + * on both 32-bit and 64-bit machines, to let 32-bit user space migrate to + * 64-bit tv_sec. + */ +#ifndef __kernel_rusage +struct __kernel_rusage_timeval { + __s64 tv_sec; + __s64 tv_usec; +}; + +struct __kernel_rusage { + struct __kernel_rusage_timeval ru_utime; /* user time used */ + struct __kernel_rusage_timeval ru_stime; /* system time used */ + __s64 ru_maxrss; /* maximum resident set size */ + __s64 ru_ixrss; /* integral shared memory size */ + __s64 ru_idrss; /* integral unshared data size */ + __s64 ru_isrss; /* integral unshared stack size */ + __s64 ru_minflt; /* page reclaims */ + __s64 ru_majflt; /* page faults */ + __s64 ru_nswap; /* swaps */ + __s64 ru_inblock; /* block input operations */ + __s64 ru_oublock; /* block output operations */ + __s64 ru_msgsnd; /* messages sent */ + __s64 ru_msgrcv; /* messages received */ + __s64 ru_nsignals; /* signals received */ + __s64 ru_nvcsw; /* voluntary context switches */ + __s64 ru_nivcsw; /* involuntary " */ +}; +#endif + struct rlimit { __kernel_ulong_t rlim_cur; __kernel_ulong_t rlim_max; diff --git a/kernel/compat.c b/kernel/compat.c index 03bb63995027..e56ee6a23c0f 100644 --- a/kernel/compat.c +++ b/kernel/compat.c @@ -517,7 +517,7 @@ COMPAT_SYSCALL_DEFINE2(getrlimit, unsigned int, resource, #endif
#ifdef CONFIG_COMPAT_TIME -int put_compat_rusage(const struct rusage *r, struct compat_rusage __user *ru) +int put_compat_rusage(const struct __kernel_rusage *r, struct compat_rusage __user *ru) { if (!access_ok(VERIFY_WRITE, ru, sizeof(*ru)) || __put_user(r->ru_utime.tv_sec, &ru->ru_utime.tv_sec) || @@ -551,7 +551,7 @@ COMPAT_SYSCALL_DEFINE4(wait4, if (!ru) { return sys_wait4(pid, stat_addr, options, NULL); } else { - struct rusage r; + struct __kernel_rusage r; int ret; unsigned int status; mm_segment_t old_fs = get_fs(); @@ -560,7 +560,7 @@ COMPAT_SYSCALL_DEFINE4(wait4, ret = sys_wait4(pid, (stat_addr ? (unsigned int __user *) &status : NULL), - options, (struct rusage __user *) &r); + options, (struct __kernel_rusage __user *) &r); set_fs (old_fs);
if (ret > 0) { @@ -579,7 +579,7 @@ COMPAT_SYSCALL_DEFINE5(waitid, struct compat_rusage __user *, uru) { siginfo_t info; - struct rusage ru; + struct __kernel_rusage ru; long ret; mm_segment_t old_fs = get_fs();
@@ -587,7 +587,7 @@ COMPAT_SYSCALL_DEFINE5(waitid,
set_fs(KERNEL_DS); ret = sys_waitid(which, pid, (siginfo_t __user *)&info, options, - uru ? (struct rusage __user *)&ru : NULL); + uru ? (struct __kernel_rusage __user *)&ru : NULL); set_fs(old_fs);
if ((ret < 0) || (info.si_signo == 0)) @@ -610,6 +610,38 @@ COMPAT_SYSCALL_DEFINE5(waitid, #endif
#ifdef CONFIG_COMPAT +COMPAT_SYSCALL_DEFINE5(waitid_time64, + int, which, compat_pid_t, pid, + struct compat_siginfo __user *, uinfo, int, options, + struct compat_rusage __user *, uru) +{ + siginfo_t info; + struct __kernel_rusage ru; + long ret; + mm_segment_t old_fs = get_fs(); + + memset(&info, 0, sizeof(info)); + + set_fs(KERNEL_DS); + ret = sys_waitid(which, pid, (siginfo_t __user *)&info, options, + uru ? (struct __kernel_rusage __user *)&ru : NULL); + set_fs(old_fs); + + if ((ret < 0) || (info.si_signo == 0)) + return ret; + + if (uru) { + /* sys_waitid() overwrites everything in ru */ + ret = copy_to_user(uru, &ru, sizeof(ru)); + if (ret) + return -EFAULT; + } + + BUG_ON(info.si_code & __SI_MASK); + info.si_code |= __SI_CHLD; + return copy_siginfo_to_user32(uinfo, &info); +} + static int compat_get_user_cpu_mask(compat_ulong_t __user *user_mask_ptr, unsigned len, struct cpumask *new_mask) { diff --git a/kernel/exit.c b/kernel/exit.c index 22fcc05dec40..bf2de12e6ae1 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -894,7 +894,7 @@ struct wait_opts {
struct siginfo __user *wo_info; int __user *wo_stat; - struct rusage __user *wo_rusage; + struct __kernel_rusage __user *wo_rusage;
wait_queue_t child_wait; int notask_error; @@ -1514,7 +1514,7 @@ end: }
SYSCALL_DEFINE5(waitid, int, which, pid_t, upid, struct siginfo __user *, - infop, int, options, struct rusage __user *, ru) + infop, int, options, struct __kernel_rusage __user *, ru) { struct wait_opts wo; struct pid *pid = NULL; @@ -1582,7 +1582,7 @@ SYSCALL_DEFINE5(waitid, int, which, pid_t, upid, struct siginfo __user *, }
SYSCALL_DEFINE4(wait4, pid_t, upid, int __user *, stat_addr, - int, options, struct rusage __user *, ru) + int, options, struct __kernel_rusage __user *, ru) { struct wait_opts wo; struct pid *pid = NULL; diff --git a/kernel/sys.c b/kernel/sys.c index a4e372b798a5..e578f33a286e 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -1529,7 +1529,7 @@ SYSCALL_DEFINE2(setrlimit, unsigned int, resource, struct rlimit __user *, rlim) * */
-static void accumulate_thread_rusage(struct task_struct *t, struct rusage *r) +static void accumulate_thread_rusage(struct task_struct *t, struct __kernel_rusage *r) { r->ru_nvcsw += t->nvcsw; r->ru_nivcsw += t->nivcsw; @@ -1539,12 +1539,13 @@ static void accumulate_thread_rusage(struct task_struct *t, struct rusage *r) r->ru_oublock += task_io_get_oublock(t); }
-static void k_getrusage(struct task_struct *p, int who, struct rusage *r) +static void k_getrusage(struct task_struct *p, int who, struct __kernel_rusage *r) { struct task_struct *t; unsigned long flags; cputime_t tgutime, tgstime, utime, stime; unsigned long maxrss = 0; + struct timeval tv;
memset((char *)r, 0, sizeof (*r)); utime = stime = 0; @@ -1599,8 +1600,12 @@ static void k_getrusage(struct task_struct *p, int who, struct rusage *r) unlock_task_sighand(p, &flags);
out: - cputime_to_timeval(utime, &r->ru_utime); - cputime_to_timeval(stime, &r->ru_stime); + cputime_to_timeval(utime, &tv); + r->ru_utime.tv_sec = tv.tv_sec; + r->ru_utime.tv_usec = tv.tv_usec; + cputime_to_timeval(stime, &tv); + r->ru_stime.tv_sec = tv.tv_sec; + r->ru_stime.tv_usec = tv.tv_usec;
if (who != RUSAGE_CHILDREN) { struct mm_struct *mm = get_task_mm(p); @@ -1613,15 +1618,15 @@ out: r->ru_maxrss = maxrss * (PAGE_SIZE / 1024); /* convert pages to KBs */ }
-int getrusage(struct task_struct *p, int who, struct rusage __user *ru) +int getrusage(struct task_struct *p, int who, struct __kernel_rusage __user *ru) { - struct rusage r; + struct __kernel_rusage r;
k_getrusage(p, who, &r); return copy_to_user(ru, &r, sizeof(r)) ? -EFAULT : 0; }
-SYSCALL_DEFINE2(getrusage, int, who, struct rusage __user *, ru) +SYSCALL_DEFINE2(getrusage, int, who, struct __kernel_rusage __user *, ru) { if (who != RUSAGE_SELF && who != RUSAGE_CHILDREN && who != RUSAGE_THREAD) @@ -1629,10 +1634,10 @@ SYSCALL_DEFINE2(getrusage, int, who, struct rusage __user *, ru) return getrusage(current, who, ru); }
-#ifdef CONFIG_COMPAT +#ifdef CONFIG_COMPAT_TIME COMPAT_SYSCALL_DEFINE2(getrusage, int, who, struct compat_rusage __user *, ru) { - struct rusage r; + struct __kernel_rusage r;
if (who != RUSAGE_SELF && who != RUSAGE_CHILDREN && who != RUSAGE_THREAD)