|
|
|
|
@ -62,28 +62,28 @@
|
|
|
|
|
#include <asm/unistd.h>
|
|
|
|
|
|
|
|
|
|
#ifndef SET_UNALIGN_CTL
|
|
|
|
|
# define SET_UNALIGN_CTL(a,b) (-EINVAL)
|
|
|
|
|
# define SET_UNALIGN_CTL(a, b) (-EINVAL)
|
|
|
|
|
#endif
|
|
|
|
|
#ifndef GET_UNALIGN_CTL
|
|
|
|
|
# define GET_UNALIGN_CTL(a,b) (-EINVAL)
|
|
|
|
|
# define GET_UNALIGN_CTL(a, b) (-EINVAL)
|
|
|
|
|
#endif
|
|
|
|
|
#ifndef SET_FPEMU_CTL
|
|
|
|
|
# define SET_FPEMU_CTL(a,b) (-EINVAL)
|
|
|
|
|
# define SET_FPEMU_CTL(a, b) (-EINVAL)
|
|
|
|
|
#endif
|
|
|
|
|
#ifndef GET_FPEMU_CTL
|
|
|
|
|
# define GET_FPEMU_CTL(a,b) (-EINVAL)
|
|
|
|
|
# define GET_FPEMU_CTL(a, b) (-EINVAL)
|
|
|
|
|
#endif
|
|
|
|
|
#ifndef SET_FPEXC_CTL
|
|
|
|
|
# define SET_FPEXC_CTL(a,b) (-EINVAL)
|
|
|
|
|
# define SET_FPEXC_CTL(a, b) (-EINVAL)
|
|
|
|
|
#endif
|
|
|
|
|
#ifndef GET_FPEXC_CTL
|
|
|
|
|
# define GET_FPEXC_CTL(a,b) (-EINVAL)
|
|
|
|
|
# define GET_FPEXC_CTL(a, b) (-EINVAL)
|
|
|
|
|
#endif
|
|
|
|
|
#ifndef GET_ENDIAN
|
|
|
|
|
# define GET_ENDIAN(a,b) (-EINVAL)
|
|
|
|
|
# define GET_ENDIAN(a, b) (-EINVAL)
|
|
|
|
|
#endif
|
|
|
|
|
#ifndef SET_ENDIAN
|
|
|
|
|
# define SET_ENDIAN(a,b) (-EINVAL)
|
|
|
|
|
# define SET_ENDIAN(a, b) (-EINVAL)
|
|
|
|
|
#endif
|
|
|
|
|
#ifndef GET_TSC_CTL
|
|
|
|
|
# define GET_TSC_CTL(a) (-EINVAL)
|
|
|
|
|
@ -182,39 +182,40 @@ SYSCALL_DEFINE3(setpriority, int, which, int, who, int, niceval)
|
|
|
|
|
rcu_read_lock();
|
|
|
|
|
read_lock(&tasklist_lock);
|
|
|
|
|
switch (which) {
|
|
|
|
|
case PRIO_PROCESS:
|
|
|
|
|
if (who)
|
|
|
|
|
p = find_task_by_vpid(who);
|
|
|
|
|
else
|
|
|
|
|
p = current;
|
|
|
|
|
if (p)
|
|
|
|
|
error = set_one_prio(p, niceval, error);
|
|
|
|
|
break;
|
|
|
|
|
case PRIO_PGRP:
|
|
|
|
|
if (who)
|
|
|
|
|
pgrp = find_vpid(who);
|
|
|
|
|
else
|
|
|
|
|
pgrp = task_pgrp(current);
|
|
|
|
|
do_each_pid_thread(pgrp, PIDTYPE_PGID, p) {
|
|
|
|
|
error = set_one_prio(p, niceval, error);
|
|
|
|
|
} while_each_pid_thread(pgrp, PIDTYPE_PGID, p);
|
|
|
|
|
break;
|
|
|
|
|
case PRIO_USER:
|
|
|
|
|
uid = make_kuid(cred->user_ns, who);
|
|
|
|
|
user = cred->user;
|
|
|
|
|
if (!who)
|
|
|
|
|
uid = cred->uid;
|
|
|
|
|
else if (!uid_eq(uid, cred->uid) &&
|
|
|
|
|
!(user = find_user(uid)))
|
|
|
|
|
case PRIO_PROCESS:
|
|
|
|
|
if (who)
|
|
|
|
|
p = find_task_by_vpid(who);
|
|
|
|
|
else
|
|
|
|
|
p = current;
|
|
|
|
|
if (p)
|
|
|
|
|
error = set_one_prio(p, niceval, error);
|
|
|
|
|
break;
|
|
|
|
|
case PRIO_PGRP:
|
|
|
|
|
if (who)
|
|
|
|
|
pgrp = find_vpid(who);
|
|
|
|
|
else
|
|
|
|
|
pgrp = task_pgrp(current);
|
|
|
|
|
do_each_pid_thread(pgrp, PIDTYPE_PGID, p) {
|
|
|
|
|
error = set_one_prio(p, niceval, error);
|
|
|
|
|
} while_each_pid_thread(pgrp, PIDTYPE_PGID, p);
|
|
|
|
|
break;
|
|
|
|
|
case PRIO_USER:
|
|
|
|
|
uid = make_kuid(cred->user_ns, who);
|
|
|
|
|
user = cred->user;
|
|
|
|
|
if (!who)
|
|
|
|
|
uid = cred->uid;
|
|
|
|
|
else if (!uid_eq(uid, cred->uid)) {
|
|
|
|
|
user = find_user(uid);
|
|
|
|
|
if (!user)
|
|
|
|
|
goto out_unlock; /* No processes for this user */
|
|
|
|
|
|
|
|
|
|
do_each_thread(g, p) {
|
|
|
|
|
if (uid_eq(task_uid(p), uid))
|
|
|
|
|
error = set_one_prio(p, niceval, error);
|
|
|
|
|
} while_each_thread(g, p);
|
|
|
|
|
if (!uid_eq(uid, cred->uid))
|
|
|
|
|
free_uid(user); /* For find_user() */
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
do_each_thread(g, p) {
|
|
|
|
|
if (uid_eq(task_uid(p), uid))
|
|
|
|
|
error = set_one_prio(p, niceval, error);
|
|
|
|
|
} while_each_thread(g, p);
|
|
|
|
|
if (!uid_eq(uid, cred->uid))
|
|
|
|
|
free_uid(user); /* For find_user() */
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
out_unlock:
|
|
|
|
|
read_unlock(&tasklist_lock);
|
|
|
|
|
@ -244,47 +245,48 @@ SYSCALL_DEFINE2(getpriority, int, which, int, who)
|
|
|
|
|
rcu_read_lock();
|
|
|
|
|
read_lock(&tasklist_lock);
|
|
|
|
|
switch (which) {
|
|
|
|
|
case PRIO_PROCESS:
|
|
|
|
|
if (who)
|
|
|
|
|
p = find_task_by_vpid(who);
|
|
|
|
|
else
|
|
|
|
|
p = current;
|
|
|
|
|
if (p) {
|
|
|
|
|
case PRIO_PROCESS:
|
|
|
|
|
if (who)
|
|
|
|
|
p = find_task_by_vpid(who);
|
|
|
|
|
else
|
|
|
|
|
p = current;
|
|
|
|
|
if (p) {
|
|
|
|
|
niceval = nice_to_rlimit(task_nice(p));
|
|
|
|
|
if (niceval > retval)
|
|
|
|
|
retval = niceval;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case PRIO_PGRP:
|
|
|
|
|
if (who)
|
|
|
|
|
pgrp = find_vpid(who);
|
|
|
|
|
else
|
|
|
|
|
pgrp = task_pgrp(current);
|
|
|
|
|
do_each_pid_thread(pgrp, PIDTYPE_PGID, p) {
|
|
|
|
|
niceval = nice_to_rlimit(task_nice(p));
|
|
|
|
|
if (niceval > retval)
|
|
|
|
|
retval = niceval;
|
|
|
|
|
} while_each_pid_thread(pgrp, PIDTYPE_PGID, p);
|
|
|
|
|
break;
|
|
|
|
|
case PRIO_USER:
|
|
|
|
|
uid = make_kuid(cred->user_ns, who);
|
|
|
|
|
user = cred->user;
|
|
|
|
|
if (!who)
|
|
|
|
|
uid = cred->uid;
|
|
|
|
|
else if (!uid_eq(uid, cred->uid)) {
|
|
|
|
|
user = find_user(uid);
|
|
|
|
|
if (!user)
|
|
|
|
|
goto out_unlock; /* No processes for this user */
|
|
|
|
|
}
|
|
|
|
|
do_each_thread(g, p) {
|
|
|
|
|
if (uid_eq(task_uid(p), uid)) {
|
|
|
|
|
niceval = nice_to_rlimit(task_nice(p));
|
|
|
|
|
if (niceval > retval)
|
|
|
|
|
retval = niceval;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case PRIO_PGRP:
|
|
|
|
|
if (who)
|
|
|
|
|
pgrp = find_vpid(who);
|
|
|
|
|
else
|
|
|
|
|
pgrp = task_pgrp(current);
|
|
|
|
|
do_each_pid_thread(pgrp, PIDTYPE_PGID, p) {
|
|
|
|
|
niceval = nice_to_rlimit(task_nice(p));
|
|
|
|
|
if (niceval > retval)
|
|
|
|
|
retval = niceval;
|
|
|
|
|
} while_each_pid_thread(pgrp, PIDTYPE_PGID, p);
|
|
|
|
|
break;
|
|
|
|
|
case PRIO_USER:
|
|
|
|
|
uid = make_kuid(cred->user_ns, who);
|
|
|
|
|
user = cred->user;
|
|
|
|
|
if (!who)
|
|
|
|
|
uid = cred->uid;
|
|
|
|
|
else if (!uid_eq(uid, cred->uid) &&
|
|
|
|
|
!(user = find_user(uid)))
|
|
|
|
|
goto out_unlock; /* No processes for this user */
|
|
|
|
|
|
|
|
|
|
do_each_thread(g, p) {
|
|
|
|
|
if (uid_eq(task_uid(p), uid)) {
|
|
|
|
|
niceval = nice_to_rlimit(task_nice(p));
|
|
|
|
|
if (niceval > retval)
|
|
|
|
|
retval = niceval;
|
|
|
|
|
}
|
|
|
|
|
} while_each_thread(g, p);
|
|
|
|
|
if (!uid_eq(uid, cred->uid))
|
|
|
|
|
free_uid(user); /* for find_user() */
|
|
|
|
|
break;
|
|
|
|
|
} while_each_thread(g, p);
|
|
|
|
|
if (!uid_eq(uid, cred->uid))
|
|
|
|
|
free_uid(user); /* for find_user() */
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
out_unlock:
|
|
|
|
|
read_unlock(&tasklist_lock);
|
|
|
|
|
@ -306,7 +308,7 @@ out_unlock:
|
|
|
|
|
*
|
|
|
|
|
* The general idea is that a program which uses just setregid() will be
|
|
|
|
|
* 100% compatible with BSD. A program which uses just setgid() will be
|
|
|
|
|
* 100% compatible with POSIX with saved IDs.
|
|
|
|
|
* 100% compatible with POSIX with saved IDs.
|
|
|
|
|
*
|
|
|
|
|
* SMP: There are not races, the GIDs are checked only by filesystem
|
|
|
|
|
* operations (as far as semantic preservation is concerned).
|
|
|
|
|
@ -364,7 +366,7 @@ error:
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* setgid() is implemented like SysV w/ SAVED_IDS
|
|
|
|
|
* setgid() is implemented like SysV w/ SAVED_IDS
|
|
|
|
|
*
|
|
|
|
|
* SMP: Same implicit races as above.
|
|
|
|
|
*/
|
|
|
|
|
@ -442,7 +444,7 @@ static int set_user(struct cred *new)
|
|
|
|
|
*
|
|
|
|
|
* The general idea is that a program which uses just setreuid() will be
|
|
|
|
|
* 100% compatible with BSD. A program which uses just setuid() will be
|
|
|
|
|
* 100% compatible with POSIX with saved IDs.
|
|
|
|
|
* 100% compatible with POSIX with saved IDs.
|
|
|
|
|
*/
|
|
|
|
|
SYSCALL_DEFINE2(setreuid, uid_t, ruid, uid_t, euid)
|
|
|
|
|
{
|
|
|
|
|
@ -503,17 +505,17 @@ error:
|
|
|
|
|
abort_creds(new);
|
|
|
|
|
return retval;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* setuid() is implemented like SysV with SAVED_IDS
|
|
|
|
|
*
|
|
|
|
|
* setuid() is implemented like SysV with SAVED_IDS
|
|
|
|
|
*
|
|
|
|
|
* Note that SAVED_ID's is deficient in that a setuid root program
|
|
|
|
|
* like sendmail, for example, cannot set its uid to be a normal
|
|
|
|
|
* like sendmail, for example, cannot set its uid to be a normal
|
|
|
|
|
* user and then switch back, because if you're root, setuid() sets
|
|
|
|
|
* the saved uid too. If you don't like this, blame the bright people
|
|
|
|
|
* in the POSIX committee and/or USG. Note that the BSD-style setreuid()
|
|
|
|
|
* will allow a root program to temporarily drop privileges and be able to
|
|
|
|
|
* regain them by swapping the real and effective uid.
|
|
|
|
|
* regain them by swapping the real and effective uid.
|
|
|
|
|
*/
|
|
|
|
|
SYSCALL_DEFINE1(setuid, uid_t, uid)
|
|
|
|
|
{
|
|
|
|
|
@ -637,10 +639,12 @@ SYSCALL_DEFINE3(getresuid, uid_t __user *, ruidp, uid_t __user *, euidp, uid_t _
|
|
|
|
|
euid = from_kuid_munged(cred->user_ns, cred->euid);
|
|
|
|
|
suid = from_kuid_munged(cred->user_ns, cred->suid);
|
|
|
|
|
|
|
|
|
|
if (!(retval = put_user(ruid, ruidp)) &&
|
|
|
|
|
!(retval = put_user(euid, euidp)))
|
|
|
|
|
retval = put_user(suid, suidp);
|
|
|
|
|
|
|
|
|
|
retval = put_user(ruid, ruidp);
|
|
|
|
|
if (!retval) {
|
|
|
|
|
retval = put_user(euid, euidp);
|
|
|
|
|
if (!retval)
|
|
|
|
|
return put_user(suid, suidp);
|
|
|
|
|
}
|
|
|
|
|
return retval;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -709,9 +713,12 @@ SYSCALL_DEFINE3(getresgid, gid_t __user *, rgidp, gid_t __user *, egidp, gid_t _
|
|
|
|
|
egid = from_kgid_munged(cred->user_ns, cred->egid);
|
|
|
|
|
sgid = from_kgid_munged(cred->user_ns, cred->sgid);
|
|
|
|
|
|
|
|
|
|
if (!(retval = put_user(rgid, rgidp)) &&
|
|
|
|
|
!(retval = put_user(egid, egidp)))
|
|
|
|
|
retval = put_user(sgid, sgidp);
|
|
|
|
|
retval = put_user(rgid, rgidp);
|
|
|
|
|
if (!retval) {
|
|
|
|
|
retval = put_user(egid, egidp);
|
|
|
|
|
if (!retval)
|
|
|
|
|
retval = put_user(sgid, sgidp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
|
}
|
|
|
|
|
@ -1284,7 +1291,6 @@ SYSCALL_DEFINE2(getrlimit, unsigned int, resource, struct rlimit __user *, rlim)
|
|
|
|
|
/*
|
|
|
|
|
* Back compatibility for getrlimit. Needed for some apps.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
SYSCALL_DEFINE2(old_getrlimit, unsigned int, resource,
|
|
|
|
|
struct rlimit __user *, rlim)
|
|
|
|
|
{
|
|
|
|
|
@ -1299,7 +1305,7 @@ SYSCALL_DEFINE2(old_getrlimit, unsigned int, resource,
|
|
|
|
|
x.rlim_cur = 0x7FFFFFFF;
|
|
|
|
|
if (x.rlim_max > 0x7FFFFFFF)
|
|
|
|
|
x.rlim_max = 0x7FFFFFFF;
|
|
|
|
|
return copy_to_user(rlim, &x, sizeof(x))?-EFAULT:0;
|
|
|
|
|
return copy_to_user(rlim, &x, sizeof(x)) ? -EFAULT : 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
@ -1527,7 +1533,7 @@ static void k_getrusage(struct task_struct *p, int who, struct rusage *r)
|
|
|
|
|
cputime_t tgutime, tgstime, utime, stime;
|
|
|
|
|
unsigned long maxrss = 0;
|
|
|
|
|
|
|
|
|
|
memset((char *) r, 0, sizeof *r);
|
|
|
|
|
memset((char *)r, 0, sizeof (*r));
|
|
|
|
|
utime = stime = 0;
|
|
|
|
|
|
|
|
|
|
if (who == RUSAGE_THREAD) {
|
|
|
|
|
@ -1541,41 +1547,41 @@ static void k_getrusage(struct task_struct *p, int who, struct rusage *r)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
switch (who) {
|
|
|
|
|
case RUSAGE_BOTH:
|
|
|
|
|
case RUSAGE_CHILDREN:
|
|
|
|
|
utime = p->signal->cutime;
|
|
|
|
|
stime = p->signal->cstime;
|
|
|
|
|
r->ru_nvcsw = p->signal->cnvcsw;
|
|
|
|
|
r->ru_nivcsw = p->signal->cnivcsw;
|
|
|
|
|
r->ru_minflt = p->signal->cmin_flt;
|
|
|
|
|
r->ru_majflt = p->signal->cmaj_flt;
|
|
|
|
|
r->ru_inblock = p->signal->cinblock;
|
|
|
|
|
r->ru_oublock = p->signal->coublock;
|
|
|
|
|
maxrss = p->signal->cmaxrss;
|
|
|
|
|
case RUSAGE_BOTH:
|
|
|
|
|
case RUSAGE_CHILDREN:
|
|
|
|
|
utime = p->signal->cutime;
|
|
|
|
|
stime = p->signal->cstime;
|
|
|
|
|
r->ru_nvcsw = p->signal->cnvcsw;
|
|
|
|
|
r->ru_nivcsw = p->signal->cnivcsw;
|
|
|
|
|
r->ru_minflt = p->signal->cmin_flt;
|
|
|
|
|
r->ru_majflt = p->signal->cmaj_flt;
|
|
|
|
|
r->ru_inblock = p->signal->cinblock;
|
|
|
|
|
r->ru_oublock = p->signal->coublock;
|
|
|
|
|
maxrss = p->signal->cmaxrss;
|
|
|
|
|
|
|
|
|
|
if (who == RUSAGE_CHILDREN)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case RUSAGE_SELF:
|
|
|
|
|
thread_group_cputime_adjusted(p, &tgutime, &tgstime);
|
|
|
|
|
utime += tgutime;
|
|
|
|
|
stime += tgstime;
|
|
|
|
|
r->ru_nvcsw += p->signal->nvcsw;
|
|
|
|
|
r->ru_nivcsw += p->signal->nivcsw;
|
|
|
|
|
r->ru_minflt += p->signal->min_flt;
|
|
|
|
|
r->ru_majflt += p->signal->maj_flt;
|
|
|
|
|
r->ru_inblock += p->signal->inblock;
|
|
|
|
|
r->ru_oublock += p->signal->oublock;
|
|
|
|
|
if (maxrss < p->signal->maxrss)
|
|
|
|
|
maxrss = p->signal->maxrss;
|
|
|
|
|
t = p;
|
|
|
|
|
do {
|
|
|
|
|
accumulate_thread_rusage(t, r);
|
|
|
|
|
} while_each_thread(p, t);
|
|
|
|
|
if (who == RUSAGE_CHILDREN)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
BUG();
|
|
|
|
|
case RUSAGE_SELF:
|
|
|
|
|
thread_group_cputime_adjusted(p, &tgutime, &tgstime);
|
|
|
|
|
utime += tgutime;
|
|
|
|
|
stime += tgstime;
|
|
|
|
|
r->ru_nvcsw += p->signal->nvcsw;
|
|
|
|
|
r->ru_nivcsw += p->signal->nivcsw;
|
|
|
|
|
r->ru_minflt += p->signal->min_flt;
|
|
|
|
|
r->ru_majflt += p->signal->maj_flt;
|
|
|
|
|
r->ru_inblock += p->signal->inblock;
|
|
|
|
|
r->ru_oublock += p->signal->oublock;
|
|
|
|
|
if (maxrss < p->signal->maxrss)
|
|
|
|
|
maxrss = p->signal->maxrss;
|
|
|
|
|
t = p;
|
|
|
|
|
do {
|
|
|
|
|
accumulate_thread_rusage(t, r);
|
|
|
|
|
} while_each_thread(p, t);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
BUG();
|
|
|
|
|
}
|
|
|
|
|
unlock_task_sighand(p, &flags);
|
|
|
|
|
|
|
|
|
|
@ -1585,6 +1591,7 @@ out:
|
|
|
|
|
|
|
|
|
|
if (who != RUSAGE_CHILDREN) {
|
|
|
|
|
struct mm_struct *mm = get_task_mm(p);
|
|
|
|
|
|
|
|
|
|
if (mm) {
|
|
|
|
|
setmax_mm_hiwater_rss(&maxrss, mm);
|
|
|
|
|
mmput(mm);
|
|
|
|
|
@ -1596,6 +1603,7 @@ out:
|
|
|
|
|
int getrusage(struct task_struct *p, int who, struct rusage __user *ru)
|
|
|
|
|
{
|
|
|
|
|
struct rusage r;
|
|
|
|
|
|
|
|
|
|
k_getrusage(p, who, &r);
|
|
|
|
|
return copy_to_user(ru, &r, sizeof(r)) ? -EFAULT : 0;
|
|
|
|
|
}
|
|
|
|
|
@ -1628,12 +1636,14 @@ SYSCALL_DEFINE1(umask, int, mask)
|
|
|
|
|
return mask;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int prctl_set_mm_exe_file(struct mm_struct *mm, unsigned int fd)
|
|
|
|
|
static int prctl_set_mm_exe_file_locked(struct mm_struct *mm, unsigned int fd)
|
|
|
|
|
{
|
|
|
|
|
struct fd exe;
|
|
|
|
|
struct inode *inode;
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
VM_BUG_ON_MM(!rwsem_is_locked(&mm->mmap_sem), mm);
|
|
|
|
|
|
|
|
|
|
exe = fdget(fd);
|
|
|
|
|
if (!exe.file)
|
|
|
|
|
return -EBADF;
|
|
|
|
|
@ -1654,8 +1664,6 @@ static int prctl_set_mm_exe_file(struct mm_struct *mm, unsigned int fd)
|
|
|
|
|
if (err)
|
|
|
|
|
goto exit;
|
|
|
|
|
|
|
|
|
|
down_write(&mm->mmap_sem);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Forbid mm->exe_file change if old file still mapped.
|
|
|
|
|
*/
|
|
|
|
|
@ -1667,7 +1675,7 @@ static int prctl_set_mm_exe_file(struct mm_struct *mm, unsigned int fd)
|
|
|
|
|
if (vma->vm_file &&
|
|
|
|
|
path_equal(&vma->vm_file->f_path,
|
|
|
|
|
&mm->exe_file->f_path))
|
|
|
|
|
goto exit_unlock;
|
|
|
|
|
goto exit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
@ -1678,34 +1686,222 @@ static int prctl_set_mm_exe_file(struct mm_struct *mm, unsigned int fd)
|
|
|
|
|
*/
|
|
|
|
|
err = -EPERM;
|
|
|
|
|
if (test_and_set_bit(MMF_EXE_FILE_CHANGED, &mm->flags))
|
|
|
|
|
goto exit_unlock;
|
|
|
|
|
goto exit;
|
|
|
|
|
|
|
|
|
|
err = 0;
|
|
|
|
|
set_mm_exe_file(mm, exe.file); /* this grabs a reference to exe.file */
|
|
|
|
|
exit_unlock:
|
|
|
|
|
up_write(&mm->mmap_sem);
|
|
|
|
|
|
|
|
|
|
exit:
|
|
|
|
|
fdput(exe);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef CONFIG_CHECKPOINT_RESTORE
|
|
|
|
|
/*
|
|
|
|
|
* WARNING: we don't require any capability here so be very careful
|
|
|
|
|
* in what is allowed for modification from userspace.
|
|
|
|
|
*/
|
|
|
|
|
static int validate_prctl_map(struct prctl_mm_map *prctl_map)
|
|
|
|
|
{
|
|
|
|
|
unsigned long mmap_max_addr = TASK_SIZE;
|
|
|
|
|
struct mm_struct *mm = current->mm;
|
|
|
|
|
int error = -EINVAL, i;
|
|
|
|
|
|
|
|
|
|
static const unsigned char offsets[] = {
|
|
|
|
|
offsetof(struct prctl_mm_map, start_code),
|
|
|
|
|
offsetof(struct prctl_mm_map, end_code),
|
|
|
|
|
offsetof(struct prctl_mm_map, start_data),
|
|
|
|
|
offsetof(struct prctl_mm_map, end_data),
|
|
|
|
|
offsetof(struct prctl_mm_map, start_brk),
|
|
|
|
|
offsetof(struct prctl_mm_map, brk),
|
|
|
|
|
offsetof(struct prctl_mm_map, start_stack),
|
|
|
|
|
offsetof(struct prctl_mm_map, arg_start),
|
|
|
|
|
offsetof(struct prctl_mm_map, arg_end),
|
|
|
|
|
offsetof(struct prctl_mm_map, env_start),
|
|
|
|
|
offsetof(struct prctl_mm_map, env_end),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Make sure the members are not somewhere outside
|
|
|
|
|
* of allowed address space.
|
|
|
|
|
*/
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(offsets); i++) {
|
|
|
|
|
u64 val = *(u64 *)((char *)prctl_map + offsets[i]);
|
|
|
|
|
|
|
|
|
|
if ((unsigned long)val >= mmap_max_addr ||
|
|
|
|
|
(unsigned long)val < mmap_min_addr)
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Make sure the pairs are ordered.
|
|
|
|
|
*/
|
|
|
|
|
#define __prctl_check_order(__m1, __op, __m2) \
|
|
|
|
|
((unsigned long)prctl_map->__m1 __op \
|
|
|
|
|
(unsigned long)prctl_map->__m2) ? 0 : -EINVAL
|
|
|
|
|
error = __prctl_check_order(start_code, <, end_code);
|
|
|
|
|
error |= __prctl_check_order(start_data, <, end_data);
|
|
|
|
|
error |= __prctl_check_order(start_brk, <=, brk);
|
|
|
|
|
error |= __prctl_check_order(arg_start, <=, arg_end);
|
|
|
|
|
error |= __prctl_check_order(env_start, <=, env_end);
|
|
|
|
|
if (error)
|
|
|
|
|
goto out;
|
|
|
|
|
#undef __prctl_check_order
|
|
|
|
|
|
|
|
|
|
error = -EINVAL;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* @brk should be after @end_data in traditional maps.
|
|
|
|
|
*/
|
|
|
|
|
if (prctl_map->start_brk <= prctl_map->end_data ||
|
|
|
|
|
prctl_map->brk <= prctl_map->end_data)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Neither we should allow to override limits if they set.
|
|
|
|
|
*/
|
|
|
|
|
if (check_data_rlimit(rlimit(RLIMIT_DATA), prctl_map->brk,
|
|
|
|
|
prctl_map->start_brk, prctl_map->end_data,
|
|
|
|
|
prctl_map->start_data))
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Someone is trying to cheat the auxv vector.
|
|
|
|
|
*/
|
|
|
|
|
if (prctl_map->auxv_size) {
|
|
|
|
|
if (!prctl_map->auxv || prctl_map->auxv_size > sizeof(mm->saved_auxv))
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Finally, make sure the caller has the rights to
|
|
|
|
|
* change /proc/pid/exe link: only local root should
|
|
|
|
|
* be allowed to.
|
|
|
|
|
*/
|
|
|
|
|
if (prctl_map->exe_fd != (u32)-1) {
|
|
|
|
|
struct user_namespace *ns = current_user_ns();
|
|
|
|
|
const struct cred *cred = current_cred();
|
|
|
|
|
|
|
|
|
|
if (!uid_eq(cred->uid, make_kuid(ns, 0)) ||
|
|
|
|
|
!gid_eq(cred->gid, make_kgid(ns, 0)))
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
error = 0;
|
|
|
|
|
out:
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int prctl_set_mm_map(int opt, const void __user *addr, unsigned long data_size)
|
|
|
|
|
{
|
|
|
|
|
struct prctl_mm_map prctl_map = { .exe_fd = (u32)-1, };
|
|
|
|
|
unsigned long user_auxv[AT_VECTOR_SIZE];
|
|
|
|
|
struct mm_struct *mm = current->mm;
|
|
|
|
|
int error;
|
|
|
|
|
|
|
|
|
|
BUILD_BUG_ON(sizeof(user_auxv) != sizeof(mm->saved_auxv));
|
|
|
|
|
BUILD_BUG_ON(sizeof(struct prctl_mm_map) > 256);
|
|
|
|
|
|
|
|
|
|
if (opt == PR_SET_MM_MAP_SIZE)
|
|
|
|
|
return put_user((unsigned int)sizeof(prctl_map),
|
|
|
|
|
(unsigned int __user *)addr);
|
|
|
|
|
|
|
|
|
|
if (data_size != sizeof(prctl_map))
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
if (copy_from_user(&prctl_map, addr, sizeof(prctl_map)))
|
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
|
|
error = validate_prctl_map(&prctl_map);
|
|
|
|
|
if (error)
|
|
|
|
|
return error;
|
|
|
|
|
|
|
|
|
|
if (prctl_map.auxv_size) {
|
|
|
|
|
memset(user_auxv, 0, sizeof(user_auxv));
|
|
|
|
|
if (copy_from_user(user_auxv,
|
|
|
|
|
(const void __user *)prctl_map.auxv,
|
|
|
|
|
prctl_map.auxv_size))
|
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
|
|
/* Last entry must be AT_NULL as specification requires */
|
|
|
|
|
user_auxv[AT_VECTOR_SIZE - 2] = AT_NULL;
|
|
|
|
|
user_auxv[AT_VECTOR_SIZE - 1] = AT_NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
down_write(&mm->mmap_sem);
|
|
|
|
|
if (prctl_map.exe_fd != (u32)-1)
|
|
|
|
|
error = prctl_set_mm_exe_file_locked(mm, prctl_map.exe_fd);
|
|
|
|
|
downgrade_write(&mm->mmap_sem);
|
|
|
|
|
if (error)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* We don't validate if these members are pointing to
|
|
|
|
|
* real present VMAs because application may have correspond
|
|
|
|
|
* VMAs already unmapped and kernel uses these members for statistics
|
|
|
|
|
* output in procfs mostly, except
|
|
|
|
|
*
|
|
|
|
|
* - @start_brk/@brk which are used in do_brk but kernel lookups
|
|
|
|
|
* for VMAs when updating these memvers so anything wrong written
|
|
|
|
|
* here cause kernel to swear at userspace program but won't lead
|
|
|
|
|
* to any problem in kernel itself
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
mm->start_code = prctl_map.start_code;
|
|
|
|
|
mm->end_code = prctl_map.end_code;
|
|
|
|
|
mm->start_data = prctl_map.start_data;
|
|
|
|
|
mm->end_data = prctl_map.end_data;
|
|
|
|
|
mm->start_brk = prctl_map.start_brk;
|
|
|
|
|
mm->brk = prctl_map.brk;
|
|
|
|
|
mm->start_stack = prctl_map.start_stack;
|
|
|
|
|
mm->arg_start = prctl_map.arg_start;
|
|
|
|
|
mm->arg_end = prctl_map.arg_end;
|
|
|
|
|
mm->env_start = prctl_map.env_start;
|
|
|
|
|
mm->env_end = prctl_map.env_end;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Note this update of @saved_auxv is lockless thus
|
|
|
|
|
* if someone reads this member in procfs while we're
|
|
|
|
|
* updating -- it may get partly updated results. It's
|
|
|
|
|
* known and acceptable trade off: we leave it as is to
|
|
|
|
|
* not introduce additional locks here making the kernel
|
|
|
|
|
* more complex.
|
|
|
|
|
*/
|
|
|
|
|
if (prctl_map.auxv_size)
|
|
|
|
|
memcpy(mm->saved_auxv, user_auxv, sizeof(user_auxv));
|
|
|
|
|
|
|
|
|
|
error = 0;
|
|
|
|
|
out:
|
|
|
|
|
up_read(&mm->mmap_sem);
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
#endif /* CONFIG_CHECKPOINT_RESTORE */
|
|
|
|
|
|
|
|
|
|
static int prctl_set_mm(int opt, unsigned long addr,
|
|
|
|
|
unsigned long arg4, unsigned long arg5)
|
|
|
|
|
{
|
|
|
|
|
unsigned long rlim = rlimit(RLIMIT_DATA);
|
|
|
|
|
struct mm_struct *mm = current->mm;
|
|
|
|
|
struct vm_area_struct *vma;
|
|
|
|
|
int error;
|
|
|
|
|
|
|
|
|
|
if (arg5 || (arg4 && opt != PR_SET_MM_AUXV))
|
|
|
|
|
if (arg5 || (arg4 && (opt != PR_SET_MM_AUXV &&
|
|
|
|
|
opt != PR_SET_MM_MAP &&
|
|
|
|
|
opt != PR_SET_MM_MAP_SIZE)))
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
#ifdef CONFIG_CHECKPOINT_RESTORE
|
|
|
|
|
if (opt == PR_SET_MM_MAP || opt == PR_SET_MM_MAP_SIZE)
|
|
|
|
|
return prctl_set_mm_map(opt, (const void __user *)addr, arg4);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (!capable(CAP_SYS_RESOURCE))
|
|
|
|
|
return -EPERM;
|
|
|
|
|
|
|
|
|
|
if (opt == PR_SET_MM_EXE_FILE)
|
|
|
|
|
return prctl_set_mm_exe_file(mm, (unsigned int)addr);
|
|
|
|
|
if (opt == PR_SET_MM_EXE_FILE) {
|
|
|
|
|
down_write(&mm->mmap_sem);
|
|
|
|
|
error = prctl_set_mm_exe_file_locked(mm, (unsigned int)addr);
|
|
|
|
|
up_write(&mm->mmap_sem);
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (addr >= TASK_SIZE || addr < mmap_min_addr)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
@ -1733,9 +1929,8 @@ static int prctl_set_mm(int opt, unsigned long addr,
|
|
|
|
|
if (addr <= mm->end_data)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
if (rlim < RLIM_INFINITY &&
|
|
|
|
|
(mm->brk - addr) +
|
|
|
|
|
(mm->end_data - mm->start_data) > rlim)
|
|
|
|
|
if (check_data_rlimit(rlimit(RLIMIT_DATA), mm->brk, addr,
|
|
|
|
|
mm->end_data, mm->start_data))
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
mm->start_brk = addr;
|
|
|
|
|
@ -1745,9 +1940,8 @@ static int prctl_set_mm(int opt, unsigned long addr,
|
|
|
|
|
if (addr <= mm->end_data)
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
if (rlim < RLIM_INFINITY &&
|
|
|
|
|
(addr - mm->start_brk) +
|
|
|
|
|
(mm->end_data - mm->start_data) > rlim)
|
|
|
|
|
if (check_data_rlimit(rlimit(RLIMIT_DATA), addr, mm->start_brk,
|
|
|
|
|
mm->end_data, mm->start_data))
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
mm->brk = addr;
|
|
|
|
|
@ -2023,6 +2217,7 @@ SYSCALL_DEFINE3(getcpu, unsigned __user *, cpup, unsigned __user *, nodep,
|
|
|
|
|
{
|
|
|
|
|
int err = 0;
|
|
|
|
|
int cpu = raw_smp_processor_id();
|
|
|
|
|
|
|
|
|
|
if (cpup)
|
|
|
|
|
err |= put_user(cpu, cpup);
|
|
|
|
|
if (nodep)
|
|
|
|
|
@ -2135,7 +2330,7 @@ COMPAT_SYSCALL_DEFINE1(sysinfo, struct compat_sysinfo __user *, info)
|
|
|
|
|
/* Check to see if any memory value is too large for 32-bit and scale
|
|
|
|
|
* down if needed
|
|
|
|
|
*/
|
|
|
|
|
if ((s.totalram >> 32) || (s.totalswap >> 32)) {
|
|
|
|
|
if (upper_32_bits(s.totalram) || upper_32_bits(s.totalswap)) {
|
|
|
|
|
int bitcount = 0;
|
|
|
|
|
|
|
|
|
|
while (s.mem_unit < PAGE_SIZE) {
|
|
|
|
|
|