summaryrefslogtreecommitdiff
path: root/ipc/shm.c
diff options
context:
space:
mode:
Diffstat (limited to 'ipc/shm.c')
-rw-r--r--ipc/shm.c533
1 files changed, 363 insertions, 170 deletions
diff --git a/ipc/shm.c b/ipc/shm.c
index 8fc97beb5234..1b3adfe3c60e 100644
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -202,7 +202,7 @@ static int __shm_open(struct vm_area_struct *vma)
if (IS_ERR(shp))
return PTR_ERR(shp);
- shp->shm_atim = get_seconds();
+ shp->shm_atim = ktime_get_real_seconds();
shp->shm_lprid = task_tgid_vnr(current);
shp->shm_nattch++;
shm_unlock(shp);
@@ -289,7 +289,7 @@ static void shm_close(struct vm_area_struct *vma)
goto done; /* no-op */
shp->shm_lprid = task_tgid_vnr(current);
- shp->shm_dtim = get_seconds();
+ shp->shm_dtim = ktime_get_real_seconds();
shp->shm_nattch--;
if (shm_may_destroy(ns, shp))
shm_destroy(ns, shp);
@@ -594,7 +594,7 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
shp->shm_cprid = task_tgid_vnr(current);
shp->shm_lprid = 0;
shp->shm_atim = shp->shm_dtim = 0;
- shp->shm_ctim = get_seconds();
+ shp->shm_ctim = ktime_get_real_seconds();
shp->shm_segsz = size;
shp->shm_nattch = 0;
shp->shm_file = file;
@@ -815,23 +815,17 @@ static void shm_get_stat(struct ipc_namespace *ns, unsigned long *rss,
* NOTE: no locks must be held, the rwsem is taken inside this function.
*/
static int shmctl_down(struct ipc_namespace *ns, int shmid, int cmd,
- struct shmid_ds __user *buf, int version)
+ struct shmid64_ds *shmid64)
{
struct kern_ipc_perm *ipcp;
- struct shmid64_ds shmid64;
struct shmid_kernel *shp;
int err;
- if (cmd == IPC_SET) {
- if (copy_shmid_from_user(&shmid64, buf, version))
- return -EFAULT;
- }
-
down_write(&shm_ids(ns).rwsem);
rcu_read_lock();
ipcp = ipcctl_pre_down_nolock(ns, &shm_ids(ns), shmid, cmd,
- &shmid64.shm_perm, 0);
+ &shmid64->shm_perm, 0);
if (IS_ERR(ipcp)) {
err = PTR_ERR(ipcp);
goto out_unlock1;
@@ -851,10 +845,10 @@ static int shmctl_down(struct ipc_namespace *ns, int shmid, int cmd,
goto out_up;
case IPC_SET:
ipc_lock_object(&shp->shm_perm);
- err = ipc_update_perm(&shmid64.shm_perm, ipcp);
+ err = ipc_update_perm(&shmid64->shm_perm, ipcp);
if (err)
goto out_unlock0;
- shp->shm_ctim = get_seconds();
+ shp->shm_ctim = ktime_get_real_seconds();
break;
default:
err = -EINVAL;
@@ -870,125 +864,175 @@ out_up:
return err;
}
-static int shmctl_nolock(struct ipc_namespace *ns, int shmid,
- int cmd, int version, void __user *buf)
+static int shmctl_ipc_info(struct ipc_namespace *ns,
+ struct shminfo64 *shminfo)
{
- int err;
- struct shmid_kernel *shp;
-
- /* preliminary security checks for *_INFO */
- if (cmd == IPC_INFO || cmd == SHM_INFO) {
- err = security_shm_shmctl(NULL, cmd);
- if (err)
- return err;
- }
-
- switch (cmd) {
- case IPC_INFO:
- {
- struct shminfo64 shminfo;
-
- memset(&shminfo, 0, sizeof(shminfo));
- shminfo.shmmni = shminfo.shmseg = ns->shm_ctlmni;
- shminfo.shmmax = ns->shm_ctlmax;
- shminfo.shmall = ns->shm_ctlall;
-
- shminfo.shmmin = SHMMIN;
- if (copy_shminfo_to_user(buf, &shminfo, version))
- return -EFAULT;
-
+ int err = security_shm_shmctl(NULL, IPC_INFO);
+ if (!err) {
+ memset(shminfo, 0, sizeof(*shminfo));
+ shminfo->shmmni = shminfo->shmseg = ns->shm_ctlmni;
+ shminfo->shmmax = ns->shm_ctlmax;
+ shminfo->shmall = ns->shm_ctlall;
+ shminfo->shmmin = SHMMIN;
down_read(&shm_ids(ns).rwsem);
err = ipc_get_maxid(&shm_ids(ns));
up_read(&shm_ids(ns).rwsem);
-
if (err < 0)
err = 0;
- goto out;
}
- case SHM_INFO:
- {
- struct shm_info shm_info;
+ return err;
+}
- memset(&shm_info, 0, sizeof(shm_info));
+static int shmctl_shm_info(struct ipc_namespace *ns,
+ struct shm_info *shm_info)
+{
+ int err = security_shm_shmctl(NULL, SHM_INFO);
+ if (!err) {
+ memset(shm_info, 0, sizeof(*shm_info));
down_read(&shm_ids(ns).rwsem);
- shm_info.used_ids = shm_ids(ns).in_use;
- shm_get_stat(ns, &shm_info.shm_rss, &shm_info.shm_swp);
- shm_info.shm_tot = ns->shm_tot;
- shm_info.swap_attempts = 0;
- shm_info.swap_successes = 0;
+ shm_info->used_ids = shm_ids(ns).in_use;
+ shm_get_stat(ns, &shm_info->shm_rss, &shm_info->shm_swp);
+ shm_info->shm_tot = ns->shm_tot;
+ shm_info->swap_attempts = 0;
+ shm_info->swap_successes = 0;
err = ipc_get_maxid(&shm_ids(ns));
up_read(&shm_ids(ns).rwsem);
- if (copy_to_user(buf, &shm_info, sizeof(shm_info))) {
- err = -EFAULT;
- goto out;
+ if (err < 0)
+ err = 0;
+ }
+ return err;
+}
+
+static int shmctl_stat(struct ipc_namespace *ns, int shmid,
+ int cmd, struct shmid64_ds *tbuf)
+{
+ struct shmid_kernel *shp;
+ int result;
+ int err;
+
+ rcu_read_lock();
+ if (cmd == SHM_STAT) {
+ shp = shm_obtain_object(ns, shmid);
+ if (IS_ERR(shp)) {
+ err = PTR_ERR(shp);
+ goto out_unlock;
+ }
+ result = shp->shm_perm.id;
+ } else {
+ shp = shm_obtain_object_check(ns, shmid);
+ if (IS_ERR(shp)) {
+ err = PTR_ERR(shp);
+ goto out_unlock;
}
+ result = 0;
+ }
- err = err < 0 ? 0 : err;
- goto out;
+ err = -EACCES;
+ if (ipcperms(ns, &shp->shm_perm, S_IRUGO))
+ goto out_unlock;
+
+ err = security_shm_shmctl(shp, cmd);
+ if (err)
+ goto out_unlock;
+
+ memset(tbuf, 0, sizeof(*tbuf));
+ kernel_to_ipc64_perm(&shp->shm_perm, &tbuf->shm_perm);
+ tbuf->shm_segsz = shp->shm_segsz;
+ tbuf->shm_atime = shp->shm_atim;
+ tbuf->shm_dtime = shp->shm_dtim;
+ tbuf->shm_ctime = shp->shm_ctim;
+ tbuf->shm_cpid = shp->shm_cprid;
+ tbuf->shm_lpid = shp->shm_lprid;
+ tbuf->shm_nattch = shp->shm_nattch;
+ rcu_read_unlock();
+ return result;
+
+out_unlock:
+ rcu_read_unlock();
+ return err;
+}
+
+static int shmctl_do_lock(struct ipc_namespace *ns, int shmid, int cmd)
+{
+ struct shmid_kernel *shp;
+ struct file *shm_file;
+ int err;
+
+ rcu_read_lock();
+ shp = shm_obtain_object_check(ns, shmid);
+ if (IS_ERR(shp)) {
+ err = PTR_ERR(shp);
+ goto out_unlock1;
}
- case SHM_STAT:
- case IPC_STAT:
- {
- struct shmid64_ds tbuf;
- int result;
-
- rcu_read_lock();
- if (cmd == SHM_STAT) {
- shp = shm_obtain_object(ns, shmid);
- if (IS_ERR(shp)) {
- err = PTR_ERR(shp);
- goto out_unlock;
- }
- result = shp->shm_perm.id;
- } else {
- shp = shm_obtain_object_check(ns, shmid);
- if (IS_ERR(shp)) {
- err = PTR_ERR(shp);
- goto out_unlock;
- }
- result = 0;
- }
- err = -EACCES;
- if (ipcperms(ns, &shp->shm_perm, S_IRUGO))
- goto out_unlock;
+ audit_ipc_obj(&(shp->shm_perm));
+ err = security_shm_shmctl(shp, cmd);
+ if (err)
+ goto out_unlock1;
- err = security_shm_shmctl(shp, cmd);
- if (err)
- goto out_unlock;
+ ipc_lock_object(&shp->shm_perm);
- memset(&tbuf, 0, sizeof(tbuf));
- kernel_to_ipc64_perm(&shp->shm_perm, &tbuf.shm_perm);
- tbuf.shm_segsz = shp->shm_segsz;
- tbuf.shm_atime = shp->shm_atim;
- tbuf.shm_dtime = shp->shm_dtim;
- tbuf.shm_ctime = shp->shm_ctim;
- tbuf.shm_cpid = shp->shm_cprid;
- tbuf.shm_lpid = shp->shm_lprid;
- tbuf.shm_nattch = shp->shm_nattch;
- rcu_read_unlock();
-
- if (copy_shmid_to_user(buf, &tbuf, version))
- err = -EFAULT;
- else
- err = result;
- goto out;
+ /* check if shm_destroy() is tearing down shp */
+ if (!ipc_valid_object(&shp->shm_perm)) {
+ err = -EIDRM;
+ goto out_unlock0;
}
- default:
- return -EINVAL;
+
+ if (!ns_capable(ns->user_ns, CAP_IPC_LOCK)) {
+ kuid_t euid = current_euid();
+
+ if (!uid_eq(euid, shp->shm_perm.uid) &&
+ !uid_eq(euid, shp->shm_perm.cuid)) {
+ err = -EPERM;
+ goto out_unlock0;
+ }
+ if (cmd == SHM_LOCK && !rlimit(RLIMIT_MEMLOCK)) {
+ err = -EPERM;
+ goto out_unlock0;
+ }
}
-out_unlock:
+ shm_file = shp->shm_file;
+ if (is_file_hugepages(shm_file))
+ goto out_unlock0;
+
+ if (cmd == SHM_LOCK) {
+ struct user_struct *user = current_user();
+
+ err = shmem_lock(shm_file, 1, user);
+ if (!err && !(shp->shm_perm.mode & SHM_LOCKED)) {
+ shp->shm_perm.mode |= SHM_LOCKED;
+ shp->mlock_user = user;
+ }
+ goto out_unlock0;
+ }
+
+ /* SHM_UNLOCK */
+ if (!(shp->shm_perm.mode & SHM_LOCKED))
+ goto out_unlock0;
+ shmem_lock(shm_file, 0, shp->mlock_user);
+ shp->shm_perm.mode &= ~SHM_LOCKED;
+ shp->mlock_user = NULL;
+ get_file(shm_file);
+ ipc_unlock_object(&shp->shm_perm);
+ rcu_read_unlock();
+ shmem_unlock_mapping(shm_file->f_mapping);
+
+ fput(shm_file);
+ return err;
+
+out_unlock0:
+ ipc_unlock_object(&shp->shm_perm);
+out_unlock1:
rcu_read_unlock();
-out:
return err;
}
SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf)
{
- struct shmid_kernel *shp;
int err, version;
struct ipc_namespace *ns;
+ struct shmid64_ds sem64;
if (cmd < 0 || shmid < 0)
return -EINVAL;
@@ -997,92 +1041,222 @@ SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf)
ns = current->nsproxy->ipc_ns;
switch (cmd) {
- case IPC_INFO:
- case SHM_INFO:
+ case IPC_INFO: {
+ struct shminfo64 shminfo;
+ err = shmctl_ipc_info(ns, &shminfo);
+ if (err < 0)
+ return err;
+ if (copy_shminfo_to_user(buf, &shminfo, version))
+ err = -EFAULT;
+ return err;
+ }
+ case SHM_INFO: {
+ struct shm_info shm_info;
+ err = shmctl_shm_info(ns, &shm_info);
+ if (err < 0)
+ return err;
+ if (copy_to_user(buf, &shm_info, sizeof(shm_info)))
+ err = -EFAULT;
+ return err;
+ }
case SHM_STAT:
- case IPC_STAT:
- return shmctl_nolock(ns, shmid, cmd, version, buf);
- case IPC_RMID:
+ case IPC_STAT: {
+ err = shmctl_stat(ns, shmid, cmd, &sem64);
+ if (err < 0)
+ return err;
+ if (copy_shmid_to_user(buf, &sem64, version))
+ err = -EFAULT;
+ return err;
+ }
case IPC_SET:
- return shmctl_down(ns, shmid, cmd, buf, version);
+ if (copy_shmid_from_user(&sem64, buf, version))
+ return -EFAULT;
+ /* fallthru */
+ case IPC_RMID:
+ return shmctl_down(ns, shmid, cmd, &sem64);
case SHM_LOCK:
case SHM_UNLOCK:
- {
- struct file *shm_file;
+ return shmctl_do_lock(ns, shmid, cmd);
+ default:
+ return -EINVAL;
+ }
+}
- rcu_read_lock();
- shp = shm_obtain_object_check(ns, shmid);
- if (IS_ERR(shp)) {
- err = PTR_ERR(shp);
- goto out_unlock1;
- }
+#ifdef CONFIG_COMPAT
+
+struct compat_shmid_ds {
+ struct compat_ipc_perm shm_perm;
+ int shm_segsz;
+ compat_time_t shm_atime;
+ compat_time_t shm_dtime;
+ compat_time_t shm_ctime;
+ compat_ipc_pid_t shm_cpid;
+ compat_ipc_pid_t shm_lpid;
+ unsigned short shm_nattch;
+ unsigned short shm_unused;
+ compat_uptr_t shm_unused2;
+ compat_uptr_t shm_unused3;
+};
- audit_ipc_obj(&(shp->shm_perm));
- err = security_shm_shmctl(shp, cmd);
- if (err)
- goto out_unlock1;
+struct compat_shminfo64 {
+ compat_ulong_t shmmax;
+ compat_ulong_t shmmin;
+ compat_ulong_t shmmni;
+ compat_ulong_t shmseg;
+ compat_ulong_t shmall;
+ compat_ulong_t __unused1;
+ compat_ulong_t __unused2;
+ compat_ulong_t __unused3;
+ compat_ulong_t __unused4;
+};
- ipc_lock_object(&shp->shm_perm);
+struct compat_shm_info {
+ compat_int_t used_ids;
+ compat_ulong_t shm_tot, shm_rss, shm_swp;
+ compat_ulong_t swap_attempts, swap_successes;
+};
- /* check if shm_destroy() is tearing down shp */
- if (!ipc_valid_object(&shp->shm_perm)) {
- err = -EIDRM;
- goto out_unlock0;
- }
+static int copy_compat_shminfo_to_user(void __user *buf, struct shminfo64 *in,
+ int version)
+{
+ if (in->shmmax > INT_MAX)
+ in->shmmax = INT_MAX;
+ if (version == IPC_64) {
+ struct compat_shminfo64 info;
+ memset(&info, 0, sizeof(info));
+ info.shmmax = in->shmmax;
+ info.shmmin = in->shmmin;
+ info.shmmni = in->shmmni;
+ info.shmseg = in->shmseg;
+ info.shmall = in->shmall;
+ return copy_to_user(buf, &info, sizeof(info));
+ } else {
+ struct shminfo info;
+ memset(&info, 0, sizeof(info));
+ info.shmmax = in->shmmax;
+ info.shmmin = in->shmmin;
+ info.shmmni = in->shmmni;
+ info.shmseg = in->shmseg;
+ info.shmall = in->shmall;
+ return copy_to_user(buf, &info, sizeof(info));
+ }
+}
- if (!ns_capable(ns->user_ns, CAP_IPC_LOCK)) {
- kuid_t euid = current_euid();
-
- if (!uid_eq(euid, shp->shm_perm.uid) &&
- !uid_eq(euid, shp->shm_perm.cuid)) {
- err = -EPERM;
- goto out_unlock0;
- }
- if (cmd == SHM_LOCK && !rlimit(RLIMIT_MEMLOCK)) {
- err = -EPERM;
- goto out_unlock0;
- }
- }
+static int put_compat_shm_info(struct shm_info *ip,
+ struct compat_shm_info __user *uip)
+{
+ struct compat_shm_info info;
+
+ memset(&info, 0, sizeof(info));
+ info.used_ids = ip->used_ids;
+ info.shm_tot = ip->shm_tot;
+ info.shm_rss = ip->shm_rss;
+ info.shm_swp = ip->shm_swp;
+ info.swap_attempts = ip->swap_attempts;
+ info.swap_successes = ip->swap_successes;
+ return copy_to_user(up, &info, sizeof(info));
+}
- shm_file = shp->shm_file;
- if (is_file_hugepages(shm_file))
- goto out_unlock0;
+static int copy_compat_shmid_to_user(void __user *buf, struct shmid64_ds *in,
+ int version)
+{
+ if (version == IPC_64) {
+ struct compat_shmid64_ds v;
+ memset(&v, 0, sizeof(v));
+ to_compat_ipc64_perm(&v.shm_perm, &in->shm_perm);
+ v.shm_atime = in->shm_atime;
+ v.shm_dtime = in->shm_dtime;
+ v.shm_ctime = in->shm_ctime;
+ v.shm_segsz = in->shm_segsz;
+ v.shm_nattch = in->shm_nattch;
+ v.shm_cpid = in->shm_cpid;
+ v.shm_lpid = in->shm_lpid;
+ return copy_to_user(buf, &v, sizeof(v));
+ } else {
+ struct compat_shmid_ds v;
+ memset(&v, 0, sizeof(v));
+ to_compat_ipc_perm(&v.shm_perm, &in->shm_perm);
+ v.shm_perm.key = in->shm_perm.key;
+ v.shm_atime = in->shm_atime;
+ v.shm_dtime = in->shm_dtime;
+ v.shm_ctime = in->shm_ctime;
+ v.shm_segsz = in->shm_segsz;
+ v.shm_nattch = in->shm_nattch;
+ v.shm_cpid = in->shm_cpid;
+ v.shm_lpid = in->shm_lpid;
+ return copy_to_user(buf, &v, sizeof(v));
+ }
+}
- if (cmd == SHM_LOCK) {
- struct user_struct *user = current_user();
+static int copy_compat_shmid_from_user(struct shmid64_ds *out, void __user *buf,
+ int version)
+{
+ memset(out, 0, sizeof(*out));
+ if (version == IPC_64) {
+ struct compat_shmid64_ds *p = buf;
+ return get_compat_ipc64_perm(&out->shm_perm, &p->shm_perm);
+ } else {
+ struct compat_shmid_ds *p = buf;
+ return get_compat_ipc_perm(&out->shm_perm, &p->shm_perm);
+ }
+}
- err = shmem_lock(shm_file, 1, user);
- if (!err && !(shp->shm_perm.mode & SHM_LOCKED)) {
- shp->shm_perm.mode |= SHM_LOCKED;
- shp->mlock_user = user;
- }
- goto out_unlock0;
- }
+COMPAT_SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, void __user *, uptr)
+{
+ struct ipc_namespace *ns;
+ struct shmid64_ds sem64;
+ int version = compat_ipc_parse_version(&cmd);
+ int err;
- /* SHM_UNLOCK */
- if (!(shp->shm_perm.mode & SHM_LOCKED))
- goto out_unlock0;
- shmem_lock(shm_file, 0, shp->mlock_user);
- shp->shm_perm.mode &= ~SHM_LOCKED;
- shp->mlock_user = NULL;
- get_file(shm_file);
- ipc_unlock_object(&shp->shm_perm);
- rcu_read_unlock();
- shmem_unlock_mapping(shm_file->f_mapping);
+ ns = current->nsproxy->ipc_ns;
+
+ if (cmd < 0 || shmid < 0)
+ return -EINVAL;
- fput(shm_file);
+ switch (cmd) {
+ case IPC_INFO: {
+ struct shminfo64 shminfo;
+ err = shmctl_ipc_info(ns, &shminfo);
+ if (err < 0)
+ return err;
+ if (copy_compat_shminfo_to_user(uptr, &shminfo, version))
+ err = -EFAULT;
+ return err;
+ }
+ case SHM_INFO: {
+ struct shm_info shm_info;
+ err = shmctl_shm_info(ns, &shm_info);
+ if (err < 0)
+ return err;
+ if (put_compat_shm_info(&shm_info, uptr))
+ err = -EFAULT;
return err;
}
+ case IPC_STAT:
+ case SHM_STAT:
+ err = shmctl_stat(ns, shmid, cmd, &sem64);
+ if (err < 0)
+ return err;
+ if (copy_compat_shmid_to_user(&sem64, uptr, version))
+ err = -EFAULT;
+ return err;
+
+ case IPC_SET:
+ if (copy_compat_shmid_from_user(&sem64, uptr, version))
+ return -EFAULT;
+ /* fallthru */
+ case IPC_RMID:
+ return shmctl_down(ns, shmid, cmd, &sem64);
+ case SHM_LOCK:
+ case SHM_UNLOCK:
+ return shmctl_do_lock(ns, shmid, cmd);
+ break;
default:
return -EINVAL;
}
-
-out_unlock0:
- ipc_unlock_object(&shp->shm_perm);
-out_unlock1:
- rcu_read_unlock();
return err;
}
+#endif
/*
* Fix shmaddr, allocate descriptor, map shm, add attach descriptor to lists.
@@ -1267,6 +1441,25 @@ SYSCALL_DEFINE3(shmat, int, shmid, char __user *, shmaddr, int, shmflg)
return (long)ret;
}
+#ifdef CONFIG_COMPAT
+
+#ifndef COMPAT_SHMLBA
+#define COMPAT_SHMLBA SHMLBA
+#endif
+
+COMPAT_SYSCALL_DEFINE3(shmat, int, shmid, compat_uptr_t, shmaddr, int, shmflg)
+{
+ unsigned long ret;
+ long err;
+
+ err = do_shmat(shmid, compat_ptr(shmaddr), shmflg, &ret, COMPAT_SHMLBA);
+ if (err)
+ return err;
+ force_successful_syscall_return();
+ return (long)ret;
+}
+#endif
+
/*
* detach and kill segment if marked destroyed.
* The work is done in shm_close.
@@ -1397,7 +1590,7 @@ static int sysvipc_shm_proc_show(struct seq_file *s, void *it)
seq_printf(s,
"%10d %10d %4o " SIZE_SPEC " %5u %5u "
- "%5lu %5u %5u %5u %5u %10lu %10lu %10lu "
+ "%5lu %5u %5u %5u %5u %10llu %10llu %10llu "
SIZE_SPEC " " SIZE_SPEC "\n",
shp->shm_perm.key,
shp->shm_perm.id,