162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/ipc/shm.c 462306a36Sopenharmony_ci * Copyright (C) 1992, 1993 Krishna Balasubramanian 562306a36Sopenharmony_ci * Many improvements/fixes by Bruno Haible. 662306a36Sopenharmony_ci * Replaced `struct shm_desc' by `struct vm_area_struct', July 1994. 762306a36Sopenharmony_ci * Fixed the shm swap deallocation (shm_unuse()), August 1998 Andrea Arcangeli. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * /proc/sysvipc/shm support (c) 1999 Dragos Acostachioaie <dragos@iname.com> 1062306a36Sopenharmony_ci * BIGMEM support, Andrea Arcangeli <andrea@suse.de> 1162306a36Sopenharmony_ci * SMP thread shm, Jean-Luc Boyard <jean-luc.boyard@siemens.fr> 1262306a36Sopenharmony_ci * HIGHMEM support, Ingo Molnar <mingo@redhat.com> 1362306a36Sopenharmony_ci * Make shmmax, shmall, shmmni sysctl'able, Christoph Rohland <cr@sap.com> 1462306a36Sopenharmony_ci * Shared /dev/zero support, Kanoj Sarcar <kanoj@sgi.com> 1562306a36Sopenharmony_ci * Move the mm functionality over to mm/shmem.c, Christoph Rohland <cr@sap.com> 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * support for audit of ipc object properties and permission changes 1862306a36Sopenharmony_ci * Dustin Kirkland <dustin.kirkland@us.ibm.com> 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * namespaces support 2162306a36Sopenharmony_ci * OpenVZ, SWsoft Inc. 2262306a36Sopenharmony_ci * Pavel Emelianov <xemul@openvz.org> 2362306a36Sopenharmony_ci * 2462306a36Sopenharmony_ci * Better ipc lock (kern_ipc_perm.lock) handling 2562306a36Sopenharmony_ci * Davidlohr Bueso <davidlohr.bueso@hp.com>, June 2013. 2662306a36Sopenharmony_ci */ 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include <linux/slab.h> 2962306a36Sopenharmony_ci#include <linux/mm.h> 3062306a36Sopenharmony_ci#include <linux/hugetlb.h> 3162306a36Sopenharmony_ci#include <linux/shm.h> 3262306a36Sopenharmony_ci#include <linux/init.h> 3362306a36Sopenharmony_ci#include <linux/file.h> 3462306a36Sopenharmony_ci#include <linux/mman.h> 3562306a36Sopenharmony_ci#include <linux/shmem_fs.h> 3662306a36Sopenharmony_ci#include <linux/security.h> 3762306a36Sopenharmony_ci#include <linux/syscalls.h> 3862306a36Sopenharmony_ci#include <linux/audit.h> 3962306a36Sopenharmony_ci#include <linux/capability.h> 4062306a36Sopenharmony_ci#include <linux/ptrace.h> 4162306a36Sopenharmony_ci#include <linux/seq_file.h> 4262306a36Sopenharmony_ci#include <linux/rwsem.h> 4362306a36Sopenharmony_ci#include <linux/nsproxy.h> 4462306a36Sopenharmony_ci#include <linux/mount.h> 4562306a36Sopenharmony_ci#include <linux/ipc_namespace.h> 4662306a36Sopenharmony_ci#include <linux/rhashtable.h> 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci#include <linux/uaccess.h> 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#include "util.h" 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistruct shmid_kernel /* private to the kernel */ 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci struct kern_ipc_perm shm_perm; 5562306a36Sopenharmony_ci struct file *shm_file; 5662306a36Sopenharmony_ci unsigned long shm_nattch; 5762306a36Sopenharmony_ci unsigned long shm_segsz; 5862306a36Sopenharmony_ci time64_t shm_atim; 5962306a36Sopenharmony_ci time64_t shm_dtim; 6062306a36Sopenharmony_ci time64_t shm_ctim; 6162306a36Sopenharmony_ci struct pid *shm_cprid; 6262306a36Sopenharmony_ci struct pid *shm_lprid; 6362306a36Sopenharmony_ci struct ucounts *mlock_ucounts; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci /* 6662306a36Sopenharmony_ci * The task created the shm object, for 6762306a36Sopenharmony_ci * task_lock(shp->shm_creator) 6862306a36Sopenharmony_ci */ 6962306a36Sopenharmony_ci struct task_struct *shm_creator; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci /* 7262306a36Sopenharmony_ci * List by creator. task_lock(->shm_creator) required for read/write. 7362306a36Sopenharmony_ci * If list_empty(), then the creator is dead already. 7462306a36Sopenharmony_ci */ 7562306a36Sopenharmony_ci struct list_head shm_clist; 7662306a36Sopenharmony_ci struct ipc_namespace *ns; 7762306a36Sopenharmony_ci} __randomize_layout; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/* shm_mode upper byte flags */ 8062306a36Sopenharmony_ci#define SHM_DEST 01000 /* segment will be destroyed on last detach */ 8162306a36Sopenharmony_ci#define SHM_LOCKED 02000 /* segment will not be swapped */ 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistruct shm_file_data { 8462306a36Sopenharmony_ci int id; 8562306a36Sopenharmony_ci struct ipc_namespace *ns; 8662306a36Sopenharmony_ci struct file *file; 8762306a36Sopenharmony_ci const struct vm_operations_struct *vm_ops; 8862306a36Sopenharmony_ci}; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci#define shm_file_data(file) (*((struct shm_file_data **)&(file)->private_data)) 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic const struct file_operations shm_file_operations; 9362306a36Sopenharmony_cistatic const struct vm_operations_struct shm_vm_ops; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci#define shm_ids(ns) ((ns)->ids[IPC_SHM_IDS]) 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci#define shm_unlock(shp) \ 9862306a36Sopenharmony_ci ipc_unlock(&(shp)->shm_perm) 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic int newseg(struct ipc_namespace *, struct ipc_params *); 10162306a36Sopenharmony_cistatic void shm_open(struct vm_area_struct *vma); 10262306a36Sopenharmony_cistatic void shm_close(struct vm_area_struct *vma); 10362306a36Sopenharmony_cistatic void shm_destroy(struct ipc_namespace *ns, struct shmid_kernel *shp); 10462306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS 10562306a36Sopenharmony_cistatic int sysvipc_shm_proc_show(struct seq_file *s, void *it); 10662306a36Sopenharmony_ci#endif 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_civoid shm_init_ns(struct ipc_namespace *ns) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci ns->shm_ctlmax = SHMMAX; 11162306a36Sopenharmony_ci ns->shm_ctlall = SHMALL; 11262306a36Sopenharmony_ci ns->shm_ctlmni = SHMMNI; 11362306a36Sopenharmony_ci ns->shm_rmid_forced = 0; 11462306a36Sopenharmony_ci ns->shm_tot = 0; 11562306a36Sopenharmony_ci ipc_init_ids(&shm_ids(ns)); 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci/* 11962306a36Sopenharmony_ci * Called with shm_ids.rwsem (writer) and the shp structure locked. 12062306a36Sopenharmony_ci * Only shm_ids.rwsem remains locked on exit. 12162306a36Sopenharmony_ci */ 12262306a36Sopenharmony_cistatic void do_shm_rmid(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci struct shmid_kernel *shp; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci shp = container_of(ipcp, struct shmid_kernel, shm_perm); 12762306a36Sopenharmony_ci WARN_ON(ns != shp->ns); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if (shp->shm_nattch) { 13062306a36Sopenharmony_ci shp->shm_perm.mode |= SHM_DEST; 13162306a36Sopenharmony_ci /* Do not find it any more */ 13262306a36Sopenharmony_ci ipc_set_key_private(&shm_ids(ns), &shp->shm_perm); 13362306a36Sopenharmony_ci shm_unlock(shp); 13462306a36Sopenharmony_ci } else 13562306a36Sopenharmony_ci shm_destroy(ns, shp); 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci#ifdef CONFIG_IPC_NS 13962306a36Sopenharmony_civoid shm_exit_ns(struct ipc_namespace *ns) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci free_ipcs(ns, &shm_ids(ns), do_shm_rmid); 14262306a36Sopenharmony_ci idr_destroy(&ns->ids[IPC_SHM_IDS].ipcs_idr); 14362306a36Sopenharmony_ci rhashtable_destroy(&ns->ids[IPC_SHM_IDS].key_ht); 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci#endif 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic int __init ipc_ns_init(void) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci shm_init_ns(&init_ipc_ns); 15062306a36Sopenharmony_ci return 0; 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cipure_initcall(ipc_ns_init); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_civoid __init shm_init(void) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci ipc_init_proc_interface("sysvipc/shm", 15862306a36Sopenharmony_ci#if BITS_PER_LONG <= 32 15962306a36Sopenharmony_ci " key shmid perms size cpid lpid nattch uid gid cuid cgid atime dtime ctime rss swap\n", 16062306a36Sopenharmony_ci#else 16162306a36Sopenharmony_ci " key shmid perms size cpid lpid nattch uid gid cuid cgid atime dtime ctime rss swap\n", 16262306a36Sopenharmony_ci#endif 16362306a36Sopenharmony_ci IPC_SHM_IDS, sysvipc_shm_proc_show); 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic inline struct shmid_kernel *shm_obtain_object(struct ipc_namespace *ns, int id) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci struct kern_ipc_perm *ipcp = ipc_obtain_object_idr(&shm_ids(ns), id); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci if (IS_ERR(ipcp)) 17162306a36Sopenharmony_ci return ERR_CAST(ipcp); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci return container_of(ipcp, struct shmid_kernel, shm_perm); 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic inline struct shmid_kernel *shm_obtain_object_check(struct ipc_namespace *ns, int id) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci struct kern_ipc_perm *ipcp = ipc_obtain_object_check(&shm_ids(ns), id); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci if (IS_ERR(ipcp)) 18162306a36Sopenharmony_ci return ERR_CAST(ipcp); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci return container_of(ipcp, struct shmid_kernel, shm_perm); 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci/* 18762306a36Sopenharmony_ci * shm_lock_(check_) routines are called in the paths where the rwsem 18862306a36Sopenharmony_ci * is not necessarily held. 18962306a36Sopenharmony_ci */ 19062306a36Sopenharmony_cistatic inline struct shmid_kernel *shm_lock(struct ipc_namespace *ns, int id) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci struct kern_ipc_perm *ipcp; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci rcu_read_lock(); 19562306a36Sopenharmony_ci ipcp = ipc_obtain_object_idr(&shm_ids(ns), id); 19662306a36Sopenharmony_ci if (IS_ERR(ipcp)) 19762306a36Sopenharmony_ci goto err; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci ipc_lock_object(ipcp); 20062306a36Sopenharmony_ci /* 20162306a36Sopenharmony_ci * ipc_rmid() may have already freed the ID while ipc_lock_object() 20262306a36Sopenharmony_ci * was spinning: here verify that the structure is still valid. 20362306a36Sopenharmony_ci * Upon races with RMID, return -EIDRM, thus indicating that 20462306a36Sopenharmony_ci * the ID points to a removed identifier. 20562306a36Sopenharmony_ci */ 20662306a36Sopenharmony_ci if (ipc_valid_object(ipcp)) { 20762306a36Sopenharmony_ci /* return a locked ipc object upon success */ 20862306a36Sopenharmony_ci return container_of(ipcp, struct shmid_kernel, shm_perm); 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci ipc_unlock_object(ipcp); 21262306a36Sopenharmony_ci ipcp = ERR_PTR(-EIDRM); 21362306a36Sopenharmony_cierr: 21462306a36Sopenharmony_ci rcu_read_unlock(); 21562306a36Sopenharmony_ci /* 21662306a36Sopenharmony_ci * Callers of shm_lock() must validate the status of the returned ipc 21762306a36Sopenharmony_ci * object pointer and error out as appropriate. 21862306a36Sopenharmony_ci */ 21962306a36Sopenharmony_ci return ERR_CAST(ipcp); 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic inline void shm_lock_by_ptr(struct shmid_kernel *ipcp) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci rcu_read_lock(); 22562306a36Sopenharmony_ci ipc_lock_object(&ipcp->shm_perm); 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistatic void shm_rcu_free(struct rcu_head *head) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci struct kern_ipc_perm *ptr = container_of(head, struct kern_ipc_perm, 23162306a36Sopenharmony_ci rcu); 23262306a36Sopenharmony_ci struct shmid_kernel *shp = container_of(ptr, struct shmid_kernel, 23362306a36Sopenharmony_ci shm_perm); 23462306a36Sopenharmony_ci security_shm_free(&shp->shm_perm); 23562306a36Sopenharmony_ci kfree(shp); 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci/* 23962306a36Sopenharmony_ci * It has to be called with shp locked. 24062306a36Sopenharmony_ci * It must be called before ipc_rmid() 24162306a36Sopenharmony_ci */ 24262306a36Sopenharmony_cistatic inline void shm_clist_rm(struct shmid_kernel *shp) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci struct task_struct *creator; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci /* ensure that shm_creator does not disappear */ 24762306a36Sopenharmony_ci rcu_read_lock(); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci /* 25062306a36Sopenharmony_ci * A concurrent exit_shm may do a list_del_init() as well. 25162306a36Sopenharmony_ci * Just do nothing if exit_shm already did the work 25262306a36Sopenharmony_ci */ 25362306a36Sopenharmony_ci if (!list_empty(&shp->shm_clist)) { 25462306a36Sopenharmony_ci /* 25562306a36Sopenharmony_ci * shp->shm_creator is guaranteed to be valid *only* 25662306a36Sopenharmony_ci * if shp->shm_clist is not empty. 25762306a36Sopenharmony_ci */ 25862306a36Sopenharmony_ci creator = shp->shm_creator; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci task_lock(creator); 26162306a36Sopenharmony_ci /* 26262306a36Sopenharmony_ci * list_del_init() is a nop if the entry was already removed 26362306a36Sopenharmony_ci * from the list. 26462306a36Sopenharmony_ci */ 26562306a36Sopenharmony_ci list_del_init(&shp->shm_clist); 26662306a36Sopenharmony_ci task_unlock(creator); 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci rcu_read_unlock(); 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic inline void shm_rmid(struct shmid_kernel *s) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci shm_clist_rm(s); 27462306a36Sopenharmony_ci ipc_rmid(&shm_ids(s->ns), &s->shm_perm); 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_cistatic int __shm_open(struct shm_file_data *sfd) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci struct shmid_kernel *shp; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci shp = shm_lock(sfd->ns, sfd->id); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci if (IS_ERR(shp)) 28562306a36Sopenharmony_ci return PTR_ERR(shp); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci if (shp->shm_file != sfd->file) { 28862306a36Sopenharmony_ci /* ID was reused */ 28962306a36Sopenharmony_ci shm_unlock(shp); 29062306a36Sopenharmony_ci return -EINVAL; 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci shp->shm_atim = ktime_get_real_seconds(); 29462306a36Sopenharmony_ci ipc_update_pid(&shp->shm_lprid, task_tgid(current)); 29562306a36Sopenharmony_ci shp->shm_nattch++; 29662306a36Sopenharmony_ci shm_unlock(shp); 29762306a36Sopenharmony_ci return 0; 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci/* This is called by fork, once for every shm attach. */ 30162306a36Sopenharmony_cistatic void shm_open(struct vm_area_struct *vma) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci struct file *file = vma->vm_file; 30462306a36Sopenharmony_ci struct shm_file_data *sfd = shm_file_data(file); 30562306a36Sopenharmony_ci int err; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci /* Always call underlying open if present */ 30862306a36Sopenharmony_ci if (sfd->vm_ops->open) 30962306a36Sopenharmony_ci sfd->vm_ops->open(vma); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci err = __shm_open(sfd); 31262306a36Sopenharmony_ci /* 31362306a36Sopenharmony_ci * We raced in the idr lookup or with shm_destroy(). 31462306a36Sopenharmony_ci * Either way, the ID is busted. 31562306a36Sopenharmony_ci */ 31662306a36Sopenharmony_ci WARN_ON_ONCE(err); 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci/* 32062306a36Sopenharmony_ci * shm_destroy - free the struct shmid_kernel 32162306a36Sopenharmony_ci * 32262306a36Sopenharmony_ci * @ns: namespace 32362306a36Sopenharmony_ci * @shp: struct to free 32462306a36Sopenharmony_ci * 32562306a36Sopenharmony_ci * It has to be called with shp and shm_ids.rwsem (writer) locked, 32662306a36Sopenharmony_ci * but returns with shp unlocked and freed. 32762306a36Sopenharmony_ci */ 32862306a36Sopenharmony_cistatic void shm_destroy(struct ipc_namespace *ns, struct shmid_kernel *shp) 32962306a36Sopenharmony_ci{ 33062306a36Sopenharmony_ci struct file *shm_file; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci shm_file = shp->shm_file; 33362306a36Sopenharmony_ci shp->shm_file = NULL; 33462306a36Sopenharmony_ci ns->shm_tot -= (shp->shm_segsz + PAGE_SIZE - 1) >> PAGE_SHIFT; 33562306a36Sopenharmony_ci shm_rmid(shp); 33662306a36Sopenharmony_ci shm_unlock(shp); 33762306a36Sopenharmony_ci if (!is_file_hugepages(shm_file)) 33862306a36Sopenharmony_ci shmem_lock(shm_file, 0, shp->mlock_ucounts); 33962306a36Sopenharmony_ci fput(shm_file); 34062306a36Sopenharmony_ci ipc_update_pid(&shp->shm_cprid, NULL); 34162306a36Sopenharmony_ci ipc_update_pid(&shp->shm_lprid, NULL); 34262306a36Sopenharmony_ci ipc_rcu_putref(&shp->shm_perm, shm_rcu_free); 34362306a36Sopenharmony_ci} 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci/* 34662306a36Sopenharmony_ci * shm_may_destroy - identifies whether shm segment should be destroyed now 34762306a36Sopenharmony_ci * 34862306a36Sopenharmony_ci * Returns true if and only if there are no active users of the segment and 34962306a36Sopenharmony_ci * one of the following is true: 35062306a36Sopenharmony_ci * 35162306a36Sopenharmony_ci * 1) shmctl(id, IPC_RMID, NULL) was called for this shp 35262306a36Sopenharmony_ci * 35362306a36Sopenharmony_ci * 2) sysctl kernel.shm_rmid_forced is set to 1. 35462306a36Sopenharmony_ci */ 35562306a36Sopenharmony_cistatic bool shm_may_destroy(struct shmid_kernel *shp) 35662306a36Sopenharmony_ci{ 35762306a36Sopenharmony_ci return (shp->shm_nattch == 0) && 35862306a36Sopenharmony_ci (shp->ns->shm_rmid_forced || 35962306a36Sopenharmony_ci (shp->shm_perm.mode & SHM_DEST)); 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci/* 36362306a36Sopenharmony_ci * remove the attach descriptor vma. 36462306a36Sopenharmony_ci * free memory for segment if it is marked destroyed. 36562306a36Sopenharmony_ci * The descriptor has already been removed from the current->mm->mmap list 36662306a36Sopenharmony_ci * and will later be kfree()d. 36762306a36Sopenharmony_ci */ 36862306a36Sopenharmony_cistatic void __shm_close(struct shm_file_data *sfd) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci struct shmid_kernel *shp; 37162306a36Sopenharmony_ci struct ipc_namespace *ns = sfd->ns; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci down_write(&shm_ids(ns).rwsem); 37462306a36Sopenharmony_ci /* remove from the list of attaches of the shm segment */ 37562306a36Sopenharmony_ci shp = shm_lock(ns, sfd->id); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci /* 37862306a36Sopenharmony_ci * We raced in the idr lookup or with shm_destroy(). 37962306a36Sopenharmony_ci * Either way, the ID is busted. 38062306a36Sopenharmony_ci */ 38162306a36Sopenharmony_ci if (WARN_ON_ONCE(IS_ERR(shp))) 38262306a36Sopenharmony_ci goto done; /* no-op */ 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci ipc_update_pid(&shp->shm_lprid, task_tgid(current)); 38562306a36Sopenharmony_ci shp->shm_dtim = ktime_get_real_seconds(); 38662306a36Sopenharmony_ci shp->shm_nattch--; 38762306a36Sopenharmony_ci if (shm_may_destroy(shp)) 38862306a36Sopenharmony_ci shm_destroy(ns, shp); 38962306a36Sopenharmony_ci else 39062306a36Sopenharmony_ci shm_unlock(shp); 39162306a36Sopenharmony_cidone: 39262306a36Sopenharmony_ci up_write(&shm_ids(ns).rwsem); 39362306a36Sopenharmony_ci} 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_cistatic void shm_close(struct vm_area_struct *vma) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci struct file *file = vma->vm_file; 39862306a36Sopenharmony_ci struct shm_file_data *sfd = shm_file_data(file); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci /* Always call underlying close if present */ 40162306a36Sopenharmony_ci if (sfd->vm_ops->close) 40262306a36Sopenharmony_ci sfd->vm_ops->close(vma); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci __shm_close(sfd); 40562306a36Sopenharmony_ci} 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci/* Called with ns->shm_ids(ns).rwsem locked */ 40862306a36Sopenharmony_cistatic int shm_try_destroy_orphaned(int id, void *p, void *data) 40962306a36Sopenharmony_ci{ 41062306a36Sopenharmony_ci struct ipc_namespace *ns = data; 41162306a36Sopenharmony_ci struct kern_ipc_perm *ipcp = p; 41262306a36Sopenharmony_ci struct shmid_kernel *shp = container_of(ipcp, struct shmid_kernel, shm_perm); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci /* 41562306a36Sopenharmony_ci * We want to destroy segments without users and with already 41662306a36Sopenharmony_ci * exit'ed originating process. 41762306a36Sopenharmony_ci * 41862306a36Sopenharmony_ci * As shp->* are changed under rwsem, it's safe to skip shp locking. 41962306a36Sopenharmony_ci */ 42062306a36Sopenharmony_ci if (!list_empty(&shp->shm_clist)) 42162306a36Sopenharmony_ci return 0; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci if (shm_may_destroy(shp)) { 42462306a36Sopenharmony_ci shm_lock_by_ptr(shp); 42562306a36Sopenharmony_ci shm_destroy(ns, shp); 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci return 0; 42862306a36Sopenharmony_ci} 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_civoid shm_destroy_orphaned(struct ipc_namespace *ns) 43162306a36Sopenharmony_ci{ 43262306a36Sopenharmony_ci down_write(&shm_ids(ns).rwsem); 43362306a36Sopenharmony_ci if (shm_ids(ns).in_use) 43462306a36Sopenharmony_ci idr_for_each(&shm_ids(ns).ipcs_idr, &shm_try_destroy_orphaned, ns); 43562306a36Sopenharmony_ci up_write(&shm_ids(ns).rwsem); 43662306a36Sopenharmony_ci} 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci/* Locking assumes this will only be called with task == current */ 43962306a36Sopenharmony_civoid exit_shm(struct task_struct *task) 44062306a36Sopenharmony_ci{ 44162306a36Sopenharmony_ci for (;;) { 44262306a36Sopenharmony_ci struct shmid_kernel *shp; 44362306a36Sopenharmony_ci struct ipc_namespace *ns; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci task_lock(task); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci if (list_empty(&task->sysvshm.shm_clist)) { 44862306a36Sopenharmony_ci task_unlock(task); 44962306a36Sopenharmony_ci break; 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci shp = list_first_entry(&task->sysvshm.shm_clist, struct shmid_kernel, 45362306a36Sopenharmony_ci shm_clist); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci /* 45662306a36Sopenharmony_ci * 1) Get pointer to the ipc namespace. It is worth to say 45762306a36Sopenharmony_ci * that this pointer is guaranteed to be valid because 45862306a36Sopenharmony_ci * shp lifetime is always shorter than namespace lifetime 45962306a36Sopenharmony_ci * in which shp lives. 46062306a36Sopenharmony_ci * We taken task_lock it means that shp won't be freed. 46162306a36Sopenharmony_ci */ 46262306a36Sopenharmony_ci ns = shp->ns; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci /* 46562306a36Sopenharmony_ci * 2) If kernel.shm_rmid_forced is not set then only keep track of 46662306a36Sopenharmony_ci * which shmids are orphaned, so that a later set of the sysctl 46762306a36Sopenharmony_ci * can clean them up. 46862306a36Sopenharmony_ci */ 46962306a36Sopenharmony_ci if (!ns->shm_rmid_forced) 47062306a36Sopenharmony_ci goto unlink_continue; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci /* 47362306a36Sopenharmony_ci * 3) get a reference to the namespace. 47462306a36Sopenharmony_ci * The refcount could be already 0. If it is 0, then 47562306a36Sopenharmony_ci * the shm objects will be free by free_ipc_work(). 47662306a36Sopenharmony_ci */ 47762306a36Sopenharmony_ci ns = get_ipc_ns_not_zero(ns); 47862306a36Sopenharmony_ci if (!ns) { 47962306a36Sopenharmony_ciunlink_continue: 48062306a36Sopenharmony_ci list_del_init(&shp->shm_clist); 48162306a36Sopenharmony_ci task_unlock(task); 48262306a36Sopenharmony_ci continue; 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci /* 48662306a36Sopenharmony_ci * 4) get a reference to shp. 48762306a36Sopenharmony_ci * This cannot fail: shm_clist_rm() is called before 48862306a36Sopenharmony_ci * ipc_rmid(), thus the refcount cannot be 0. 48962306a36Sopenharmony_ci */ 49062306a36Sopenharmony_ci WARN_ON(!ipc_rcu_getref(&shp->shm_perm)); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci /* 49362306a36Sopenharmony_ci * 5) unlink the shm segment from the list of segments 49462306a36Sopenharmony_ci * created by current. 49562306a36Sopenharmony_ci * This must be done last. After unlinking, 49662306a36Sopenharmony_ci * only the refcounts obtained above prevent IPC_RMID 49762306a36Sopenharmony_ci * from destroying the segment or the namespace. 49862306a36Sopenharmony_ci */ 49962306a36Sopenharmony_ci list_del_init(&shp->shm_clist); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci task_unlock(task); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci /* 50462306a36Sopenharmony_ci * 6) we have all references 50562306a36Sopenharmony_ci * Thus lock & if needed destroy shp. 50662306a36Sopenharmony_ci */ 50762306a36Sopenharmony_ci down_write(&shm_ids(ns).rwsem); 50862306a36Sopenharmony_ci shm_lock_by_ptr(shp); 50962306a36Sopenharmony_ci /* 51062306a36Sopenharmony_ci * rcu_read_lock was implicitly taken in shm_lock_by_ptr, it's 51162306a36Sopenharmony_ci * safe to call ipc_rcu_putref here 51262306a36Sopenharmony_ci */ 51362306a36Sopenharmony_ci ipc_rcu_putref(&shp->shm_perm, shm_rcu_free); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci if (ipc_valid_object(&shp->shm_perm)) { 51662306a36Sopenharmony_ci if (shm_may_destroy(shp)) 51762306a36Sopenharmony_ci shm_destroy(ns, shp); 51862306a36Sopenharmony_ci else 51962306a36Sopenharmony_ci shm_unlock(shp); 52062306a36Sopenharmony_ci } else { 52162306a36Sopenharmony_ci /* 52262306a36Sopenharmony_ci * Someone else deleted the shp from namespace 52362306a36Sopenharmony_ci * idr/kht while we have waited. 52462306a36Sopenharmony_ci * Just unlock and continue. 52562306a36Sopenharmony_ci */ 52662306a36Sopenharmony_ci shm_unlock(shp); 52762306a36Sopenharmony_ci } 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci up_write(&shm_ids(ns).rwsem); 53062306a36Sopenharmony_ci put_ipc_ns(ns); /* paired with get_ipc_ns_not_zero */ 53162306a36Sopenharmony_ci } 53262306a36Sopenharmony_ci} 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_cistatic vm_fault_t shm_fault(struct vm_fault *vmf) 53562306a36Sopenharmony_ci{ 53662306a36Sopenharmony_ci struct file *file = vmf->vma->vm_file; 53762306a36Sopenharmony_ci struct shm_file_data *sfd = shm_file_data(file); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci return sfd->vm_ops->fault(vmf); 54062306a36Sopenharmony_ci} 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_cistatic int shm_may_split(struct vm_area_struct *vma, unsigned long addr) 54362306a36Sopenharmony_ci{ 54462306a36Sopenharmony_ci struct file *file = vma->vm_file; 54562306a36Sopenharmony_ci struct shm_file_data *sfd = shm_file_data(file); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci if (sfd->vm_ops->may_split) 54862306a36Sopenharmony_ci return sfd->vm_ops->may_split(vma, addr); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci return 0; 55162306a36Sopenharmony_ci} 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_cistatic unsigned long shm_pagesize(struct vm_area_struct *vma) 55462306a36Sopenharmony_ci{ 55562306a36Sopenharmony_ci struct file *file = vma->vm_file; 55662306a36Sopenharmony_ci struct shm_file_data *sfd = shm_file_data(file); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci if (sfd->vm_ops->pagesize) 55962306a36Sopenharmony_ci return sfd->vm_ops->pagesize(vma); 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci return PAGE_SIZE; 56262306a36Sopenharmony_ci} 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci#ifdef CONFIG_NUMA 56562306a36Sopenharmony_cistatic int shm_set_policy(struct vm_area_struct *vma, struct mempolicy *new) 56662306a36Sopenharmony_ci{ 56762306a36Sopenharmony_ci struct file *file = vma->vm_file; 56862306a36Sopenharmony_ci struct shm_file_data *sfd = shm_file_data(file); 56962306a36Sopenharmony_ci int err = 0; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci if (sfd->vm_ops->set_policy) 57262306a36Sopenharmony_ci err = sfd->vm_ops->set_policy(vma, new); 57362306a36Sopenharmony_ci return err; 57462306a36Sopenharmony_ci} 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_cistatic struct mempolicy *shm_get_policy(struct vm_area_struct *vma, 57762306a36Sopenharmony_ci unsigned long addr) 57862306a36Sopenharmony_ci{ 57962306a36Sopenharmony_ci struct file *file = vma->vm_file; 58062306a36Sopenharmony_ci struct shm_file_data *sfd = shm_file_data(file); 58162306a36Sopenharmony_ci struct mempolicy *pol = NULL; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci if (sfd->vm_ops->get_policy) 58462306a36Sopenharmony_ci pol = sfd->vm_ops->get_policy(vma, addr); 58562306a36Sopenharmony_ci else if (vma->vm_policy) 58662306a36Sopenharmony_ci pol = vma->vm_policy; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci return pol; 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci#endif 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_cistatic int shm_mmap(struct file *file, struct vm_area_struct *vma) 59362306a36Sopenharmony_ci{ 59462306a36Sopenharmony_ci struct shm_file_data *sfd = shm_file_data(file); 59562306a36Sopenharmony_ci int ret; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci /* 59862306a36Sopenharmony_ci * In case of remap_file_pages() emulation, the file can represent an 59962306a36Sopenharmony_ci * IPC ID that was removed, and possibly even reused by another shm 60062306a36Sopenharmony_ci * segment already. Propagate this case as an error to caller. 60162306a36Sopenharmony_ci */ 60262306a36Sopenharmony_ci ret = __shm_open(sfd); 60362306a36Sopenharmony_ci if (ret) 60462306a36Sopenharmony_ci return ret; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci ret = call_mmap(sfd->file, vma); 60762306a36Sopenharmony_ci if (ret) { 60862306a36Sopenharmony_ci __shm_close(sfd); 60962306a36Sopenharmony_ci return ret; 61062306a36Sopenharmony_ci } 61162306a36Sopenharmony_ci sfd->vm_ops = vma->vm_ops; 61262306a36Sopenharmony_ci#ifdef CONFIG_MMU 61362306a36Sopenharmony_ci WARN_ON(!sfd->vm_ops->fault); 61462306a36Sopenharmony_ci#endif 61562306a36Sopenharmony_ci vma->vm_ops = &shm_vm_ops; 61662306a36Sopenharmony_ci return 0; 61762306a36Sopenharmony_ci} 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_cistatic int shm_release(struct inode *ino, struct file *file) 62062306a36Sopenharmony_ci{ 62162306a36Sopenharmony_ci struct shm_file_data *sfd = shm_file_data(file); 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci put_ipc_ns(sfd->ns); 62462306a36Sopenharmony_ci fput(sfd->file); 62562306a36Sopenharmony_ci shm_file_data(file) = NULL; 62662306a36Sopenharmony_ci kfree(sfd); 62762306a36Sopenharmony_ci return 0; 62862306a36Sopenharmony_ci} 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_cistatic int shm_fsync(struct file *file, loff_t start, loff_t end, int datasync) 63162306a36Sopenharmony_ci{ 63262306a36Sopenharmony_ci struct shm_file_data *sfd = shm_file_data(file); 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci if (!sfd->file->f_op->fsync) 63562306a36Sopenharmony_ci return -EINVAL; 63662306a36Sopenharmony_ci return sfd->file->f_op->fsync(sfd->file, start, end, datasync); 63762306a36Sopenharmony_ci} 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_cistatic long shm_fallocate(struct file *file, int mode, loff_t offset, 64062306a36Sopenharmony_ci loff_t len) 64162306a36Sopenharmony_ci{ 64262306a36Sopenharmony_ci struct shm_file_data *sfd = shm_file_data(file); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci if (!sfd->file->f_op->fallocate) 64562306a36Sopenharmony_ci return -EOPNOTSUPP; 64662306a36Sopenharmony_ci return sfd->file->f_op->fallocate(file, mode, offset, len); 64762306a36Sopenharmony_ci} 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_cistatic unsigned long shm_get_unmapped_area(struct file *file, 65062306a36Sopenharmony_ci unsigned long addr, unsigned long len, unsigned long pgoff, 65162306a36Sopenharmony_ci unsigned long flags) 65262306a36Sopenharmony_ci{ 65362306a36Sopenharmony_ci struct shm_file_data *sfd = shm_file_data(file); 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci return sfd->file->f_op->get_unmapped_area(sfd->file, addr, len, 65662306a36Sopenharmony_ci pgoff, flags); 65762306a36Sopenharmony_ci} 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_cistatic const struct file_operations shm_file_operations = { 66062306a36Sopenharmony_ci .mmap = shm_mmap, 66162306a36Sopenharmony_ci .fsync = shm_fsync, 66262306a36Sopenharmony_ci .release = shm_release, 66362306a36Sopenharmony_ci .get_unmapped_area = shm_get_unmapped_area, 66462306a36Sopenharmony_ci .llseek = noop_llseek, 66562306a36Sopenharmony_ci .fallocate = shm_fallocate, 66662306a36Sopenharmony_ci}; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci/* 66962306a36Sopenharmony_ci * shm_file_operations_huge is now identical to shm_file_operations, 67062306a36Sopenharmony_ci * but we keep it distinct for the sake of is_file_shm_hugepages(). 67162306a36Sopenharmony_ci */ 67262306a36Sopenharmony_cistatic const struct file_operations shm_file_operations_huge = { 67362306a36Sopenharmony_ci .mmap = shm_mmap, 67462306a36Sopenharmony_ci .fsync = shm_fsync, 67562306a36Sopenharmony_ci .release = shm_release, 67662306a36Sopenharmony_ci .get_unmapped_area = shm_get_unmapped_area, 67762306a36Sopenharmony_ci .llseek = noop_llseek, 67862306a36Sopenharmony_ci .fallocate = shm_fallocate, 67962306a36Sopenharmony_ci}; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_cibool is_file_shm_hugepages(struct file *file) 68262306a36Sopenharmony_ci{ 68362306a36Sopenharmony_ci return file->f_op == &shm_file_operations_huge; 68462306a36Sopenharmony_ci} 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_cistatic const struct vm_operations_struct shm_vm_ops = { 68762306a36Sopenharmony_ci .open = shm_open, /* callback for a new vm-area open */ 68862306a36Sopenharmony_ci .close = shm_close, /* callback for when the vm-area is released */ 68962306a36Sopenharmony_ci .fault = shm_fault, 69062306a36Sopenharmony_ci .may_split = shm_may_split, 69162306a36Sopenharmony_ci .pagesize = shm_pagesize, 69262306a36Sopenharmony_ci#if defined(CONFIG_NUMA) 69362306a36Sopenharmony_ci .set_policy = shm_set_policy, 69462306a36Sopenharmony_ci .get_policy = shm_get_policy, 69562306a36Sopenharmony_ci#endif 69662306a36Sopenharmony_ci}; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci/** 69962306a36Sopenharmony_ci * newseg - Create a new shared memory segment 70062306a36Sopenharmony_ci * @ns: namespace 70162306a36Sopenharmony_ci * @params: ptr to the structure that contains key, size and shmflg 70262306a36Sopenharmony_ci * 70362306a36Sopenharmony_ci * Called with shm_ids.rwsem held as a writer. 70462306a36Sopenharmony_ci */ 70562306a36Sopenharmony_cistatic int newseg(struct ipc_namespace *ns, struct ipc_params *params) 70662306a36Sopenharmony_ci{ 70762306a36Sopenharmony_ci key_t key = params->key; 70862306a36Sopenharmony_ci int shmflg = params->flg; 70962306a36Sopenharmony_ci size_t size = params->u.size; 71062306a36Sopenharmony_ci int error; 71162306a36Sopenharmony_ci struct shmid_kernel *shp; 71262306a36Sopenharmony_ci size_t numpages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT; 71362306a36Sopenharmony_ci struct file *file; 71462306a36Sopenharmony_ci char name[13]; 71562306a36Sopenharmony_ci vm_flags_t acctflag = 0; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci if (size < SHMMIN || size > ns->shm_ctlmax) 71862306a36Sopenharmony_ci return -EINVAL; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci if (numpages << PAGE_SHIFT < size) 72162306a36Sopenharmony_ci return -ENOSPC; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci if (ns->shm_tot + numpages < ns->shm_tot || 72462306a36Sopenharmony_ci ns->shm_tot + numpages > ns->shm_ctlall) 72562306a36Sopenharmony_ci return -ENOSPC; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci shp = kmalloc(sizeof(*shp), GFP_KERNEL_ACCOUNT); 72862306a36Sopenharmony_ci if (unlikely(!shp)) 72962306a36Sopenharmony_ci return -ENOMEM; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci shp->shm_perm.key = key; 73262306a36Sopenharmony_ci shp->shm_perm.mode = (shmflg & S_IRWXUGO); 73362306a36Sopenharmony_ci shp->mlock_ucounts = NULL; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci shp->shm_perm.security = NULL; 73662306a36Sopenharmony_ci error = security_shm_alloc(&shp->shm_perm); 73762306a36Sopenharmony_ci if (error) { 73862306a36Sopenharmony_ci kfree(shp); 73962306a36Sopenharmony_ci return error; 74062306a36Sopenharmony_ci } 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci sprintf(name, "SYSV%08x", key); 74362306a36Sopenharmony_ci if (shmflg & SHM_HUGETLB) { 74462306a36Sopenharmony_ci struct hstate *hs; 74562306a36Sopenharmony_ci size_t hugesize; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci hs = hstate_sizelog((shmflg >> SHM_HUGE_SHIFT) & SHM_HUGE_MASK); 74862306a36Sopenharmony_ci if (!hs) { 74962306a36Sopenharmony_ci error = -EINVAL; 75062306a36Sopenharmony_ci goto no_file; 75162306a36Sopenharmony_ci } 75262306a36Sopenharmony_ci hugesize = ALIGN(size, huge_page_size(hs)); 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci /* hugetlb_file_setup applies strict accounting */ 75562306a36Sopenharmony_ci if (shmflg & SHM_NORESERVE) 75662306a36Sopenharmony_ci acctflag = VM_NORESERVE; 75762306a36Sopenharmony_ci file = hugetlb_file_setup(name, hugesize, acctflag, 75862306a36Sopenharmony_ci HUGETLB_SHMFS_INODE, (shmflg >> SHM_HUGE_SHIFT) & SHM_HUGE_MASK); 75962306a36Sopenharmony_ci } else { 76062306a36Sopenharmony_ci /* 76162306a36Sopenharmony_ci * Do not allow no accounting for OVERCOMMIT_NEVER, even 76262306a36Sopenharmony_ci * if it's asked for. 76362306a36Sopenharmony_ci */ 76462306a36Sopenharmony_ci if ((shmflg & SHM_NORESERVE) && 76562306a36Sopenharmony_ci sysctl_overcommit_memory != OVERCOMMIT_NEVER) 76662306a36Sopenharmony_ci acctflag = VM_NORESERVE; 76762306a36Sopenharmony_ci file = shmem_kernel_file_setup(name, size, acctflag); 76862306a36Sopenharmony_ci } 76962306a36Sopenharmony_ci error = PTR_ERR(file); 77062306a36Sopenharmony_ci if (IS_ERR(file)) 77162306a36Sopenharmony_ci goto no_file; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci shp->shm_cprid = get_pid(task_tgid(current)); 77462306a36Sopenharmony_ci shp->shm_lprid = NULL; 77562306a36Sopenharmony_ci shp->shm_atim = shp->shm_dtim = 0; 77662306a36Sopenharmony_ci shp->shm_ctim = ktime_get_real_seconds(); 77762306a36Sopenharmony_ci shp->shm_segsz = size; 77862306a36Sopenharmony_ci shp->shm_nattch = 0; 77962306a36Sopenharmony_ci shp->shm_file = file; 78062306a36Sopenharmony_ci shp->shm_creator = current; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci /* ipc_addid() locks shp upon success. */ 78362306a36Sopenharmony_ci error = ipc_addid(&shm_ids(ns), &shp->shm_perm, ns->shm_ctlmni); 78462306a36Sopenharmony_ci if (error < 0) 78562306a36Sopenharmony_ci goto no_id; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci shp->ns = ns; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci task_lock(current); 79062306a36Sopenharmony_ci list_add(&shp->shm_clist, ¤t->sysvshm.shm_clist); 79162306a36Sopenharmony_ci task_unlock(current); 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci /* 79462306a36Sopenharmony_ci * shmid gets reported as "inode#" in /proc/pid/maps. 79562306a36Sopenharmony_ci * proc-ps tools use this. Changing this will break them. 79662306a36Sopenharmony_ci */ 79762306a36Sopenharmony_ci file_inode(file)->i_ino = shp->shm_perm.id; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci ns->shm_tot += numpages; 80062306a36Sopenharmony_ci error = shp->shm_perm.id; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci ipc_unlock_object(&shp->shm_perm); 80362306a36Sopenharmony_ci rcu_read_unlock(); 80462306a36Sopenharmony_ci return error; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_cino_id: 80762306a36Sopenharmony_ci ipc_update_pid(&shp->shm_cprid, NULL); 80862306a36Sopenharmony_ci ipc_update_pid(&shp->shm_lprid, NULL); 80962306a36Sopenharmony_ci fput(file); 81062306a36Sopenharmony_ci ipc_rcu_putref(&shp->shm_perm, shm_rcu_free); 81162306a36Sopenharmony_ci return error; 81262306a36Sopenharmony_cino_file: 81362306a36Sopenharmony_ci call_rcu(&shp->shm_perm.rcu, shm_rcu_free); 81462306a36Sopenharmony_ci return error; 81562306a36Sopenharmony_ci} 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci/* 81862306a36Sopenharmony_ci * Called with shm_ids.rwsem and ipcp locked. 81962306a36Sopenharmony_ci */ 82062306a36Sopenharmony_cistatic int shm_more_checks(struct kern_ipc_perm *ipcp, struct ipc_params *params) 82162306a36Sopenharmony_ci{ 82262306a36Sopenharmony_ci struct shmid_kernel *shp; 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci shp = container_of(ipcp, struct shmid_kernel, shm_perm); 82562306a36Sopenharmony_ci if (shp->shm_segsz < params->u.size) 82662306a36Sopenharmony_ci return -EINVAL; 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci return 0; 82962306a36Sopenharmony_ci} 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_cilong ksys_shmget(key_t key, size_t size, int shmflg) 83262306a36Sopenharmony_ci{ 83362306a36Sopenharmony_ci struct ipc_namespace *ns; 83462306a36Sopenharmony_ci static const struct ipc_ops shm_ops = { 83562306a36Sopenharmony_ci .getnew = newseg, 83662306a36Sopenharmony_ci .associate = security_shm_associate, 83762306a36Sopenharmony_ci .more_checks = shm_more_checks, 83862306a36Sopenharmony_ci }; 83962306a36Sopenharmony_ci struct ipc_params shm_params; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci ns = current->nsproxy->ipc_ns; 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci shm_params.key = key; 84462306a36Sopenharmony_ci shm_params.flg = shmflg; 84562306a36Sopenharmony_ci shm_params.u.size = size; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci return ipcget(ns, &shm_ids(ns), &shm_ops, &shm_params); 84862306a36Sopenharmony_ci} 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ciSYSCALL_DEFINE3(shmget, key_t, key, size_t, size, int, shmflg) 85162306a36Sopenharmony_ci{ 85262306a36Sopenharmony_ci return ksys_shmget(key, size, shmflg); 85362306a36Sopenharmony_ci} 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_cistatic inline unsigned long copy_shmid_to_user(void __user *buf, struct shmid64_ds *in, int version) 85662306a36Sopenharmony_ci{ 85762306a36Sopenharmony_ci switch (version) { 85862306a36Sopenharmony_ci case IPC_64: 85962306a36Sopenharmony_ci return copy_to_user(buf, in, sizeof(*in)); 86062306a36Sopenharmony_ci case IPC_OLD: 86162306a36Sopenharmony_ci { 86262306a36Sopenharmony_ci struct shmid_ds out; 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci memset(&out, 0, sizeof(out)); 86562306a36Sopenharmony_ci ipc64_perm_to_ipc_perm(&in->shm_perm, &out.shm_perm); 86662306a36Sopenharmony_ci out.shm_segsz = in->shm_segsz; 86762306a36Sopenharmony_ci out.shm_atime = in->shm_atime; 86862306a36Sopenharmony_ci out.shm_dtime = in->shm_dtime; 86962306a36Sopenharmony_ci out.shm_ctime = in->shm_ctime; 87062306a36Sopenharmony_ci out.shm_cpid = in->shm_cpid; 87162306a36Sopenharmony_ci out.shm_lpid = in->shm_lpid; 87262306a36Sopenharmony_ci out.shm_nattch = in->shm_nattch; 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci return copy_to_user(buf, &out, sizeof(out)); 87562306a36Sopenharmony_ci } 87662306a36Sopenharmony_ci default: 87762306a36Sopenharmony_ci return -EINVAL; 87862306a36Sopenharmony_ci } 87962306a36Sopenharmony_ci} 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_cistatic inline unsigned long 88262306a36Sopenharmony_cicopy_shmid_from_user(struct shmid64_ds *out, void __user *buf, int version) 88362306a36Sopenharmony_ci{ 88462306a36Sopenharmony_ci switch (version) { 88562306a36Sopenharmony_ci case IPC_64: 88662306a36Sopenharmony_ci if (copy_from_user(out, buf, sizeof(*out))) 88762306a36Sopenharmony_ci return -EFAULT; 88862306a36Sopenharmony_ci return 0; 88962306a36Sopenharmony_ci case IPC_OLD: 89062306a36Sopenharmony_ci { 89162306a36Sopenharmony_ci struct shmid_ds tbuf_old; 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci if (copy_from_user(&tbuf_old, buf, sizeof(tbuf_old))) 89462306a36Sopenharmony_ci return -EFAULT; 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci out->shm_perm.uid = tbuf_old.shm_perm.uid; 89762306a36Sopenharmony_ci out->shm_perm.gid = tbuf_old.shm_perm.gid; 89862306a36Sopenharmony_ci out->shm_perm.mode = tbuf_old.shm_perm.mode; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci return 0; 90162306a36Sopenharmony_ci } 90262306a36Sopenharmony_ci default: 90362306a36Sopenharmony_ci return -EINVAL; 90462306a36Sopenharmony_ci } 90562306a36Sopenharmony_ci} 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_cistatic inline unsigned long copy_shminfo_to_user(void __user *buf, struct shminfo64 *in, int version) 90862306a36Sopenharmony_ci{ 90962306a36Sopenharmony_ci switch (version) { 91062306a36Sopenharmony_ci case IPC_64: 91162306a36Sopenharmony_ci return copy_to_user(buf, in, sizeof(*in)); 91262306a36Sopenharmony_ci case IPC_OLD: 91362306a36Sopenharmony_ci { 91462306a36Sopenharmony_ci struct shminfo out; 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci if (in->shmmax > INT_MAX) 91762306a36Sopenharmony_ci out.shmmax = INT_MAX; 91862306a36Sopenharmony_ci else 91962306a36Sopenharmony_ci out.shmmax = (int)in->shmmax; 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci out.shmmin = in->shmmin; 92262306a36Sopenharmony_ci out.shmmni = in->shmmni; 92362306a36Sopenharmony_ci out.shmseg = in->shmseg; 92462306a36Sopenharmony_ci out.shmall = in->shmall; 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci return copy_to_user(buf, &out, sizeof(out)); 92762306a36Sopenharmony_ci } 92862306a36Sopenharmony_ci default: 92962306a36Sopenharmony_ci return -EINVAL; 93062306a36Sopenharmony_ci } 93162306a36Sopenharmony_ci} 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci/* 93462306a36Sopenharmony_ci * Calculate and add used RSS and swap pages of a shm. 93562306a36Sopenharmony_ci * Called with shm_ids.rwsem held as a reader 93662306a36Sopenharmony_ci */ 93762306a36Sopenharmony_cistatic void shm_add_rss_swap(struct shmid_kernel *shp, 93862306a36Sopenharmony_ci unsigned long *rss_add, unsigned long *swp_add) 93962306a36Sopenharmony_ci{ 94062306a36Sopenharmony_ci struct inode *inode; 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci inode = file_inode(shp->shm_file); 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci if (is_file_hugepages(shp->shm_file)) { 94562306a36Sopenharmony_ci struct address_space *mapping = inode->i_mapping; 94662306a36Sopenharmony_ci struct hstate *h = hstate_file(shp->shm_file); 94762306a36Sopenharmony_ci *rss_add += pages_per_huge_page(h) * mapping->nrpages; 94862306a36Sopenharmony_ci } else { 94962306a36Sopenharmony_ci#ifdef CONFIG_SHMEM 95062306a36Sopenharmony_ci struct shmem_inode_info *info = SHMEM_I(inode); 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci spin_lock_irq(&info->lock); 95362306a36Sopenharmony_ci *rss_add += inode->i_mapping->nrpages; 95462306a36Sopenharmony_ci *swp_add += info->swapped; 95562306a36Sopenharmony_ci spin_unlock_irq(&info->lock); 95662306a36Sopenharmony_ci#else 95762306a36Sopenharmony_ci *rss_add += inode->i_mapping->nrpages; 95862306a36Sopenharmony_ci#endif 95962306a36Sopenharmony_ci } 96062306a36Sopenharmony_ci} 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci/* 96362306a36Sopenharmony_ci * Called with shm_ids.rwsem held as a reader 96462306a36Sopenharmony_ci */ 96562306a36Sopenharmony_cistatic void shm_get_stat(struct ipc_namespace *ns, unsigned long *rss, 96662306a36Sopenharmony_ci unsigned long *swp) 96762306a36Sopenharmony_ci{ 96862306a36Sopenharmony_ci int next_id; 96962306a36Sopenharmony_ci int total, in_use; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci *rss = 0; 97262306a36Sopenharmony_ci *swp = 0; 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci in_use = shm_ids(ns).in_use; 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci for (total = 0, next_id = 0; total < in_use; next_id++) { 97762306a36Sopenharmony_ci struct kern_ipc_perm *ipc; 97862306a36Sopenharmony_ci struct shmid_kernel *shp; 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci ipc = idr_find(&shm_ids(ns).ipcs_idr, next_id); 98162306a36Sopenharmony_ci if (ipc == NULL) 98262306a36Sopenharmony_ci continue; 98362306a36Sopenharmony_ci shp = container_of(ipc, struct shmid_kernel, shm_perm); 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci shm_add_rss_swap(shp, rss, swp); 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci total++; 98862306a36Sopenharmony_ci } 98962306a36Sopenharmony_ci} 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci/* 99262306a36Sopenharmony_ci * This function handles some shmctl commands which require the rwsem 99362306a36Sopenharmony_ci * to be held in write mode. 99462306a36Sopenharmony_ci * NOTE: no locks must be held, the rwsem is taken inside this function. 99562306a36Sopenharmony_ci */ 99662306a36Sopenharmony_cistatic int shmctl_down(struct ipc_namespace *ns, int shmid, int cmd, 99762306a36Sopenharmony_ci struct shmid64_ds *shmid64) 99862306a36Sopenharmony_ci{ 99962306a36Sopenharmony_ci struct kern_ipc_perm *ipcp; 100062306a36Sopenharmony_ci struct shmid_kernel *shp; 100162306a36Sopenharmony_ci int err; 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci down_write(&shm_ids(ns).rwsem); 100462306a36Sopenharmony_ci rcu_read_lock(); 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci ipcp = ipcctl_obtain_check(ns, &shm_ids(ns), shmid, cmd, 100762306a36Sopenharmony_ci &shmid64->shm_perm, 0); 100862306a36Sopenharmony_ci if (IS_ERR(ipcp)) { 100962306a36Sopenharmony_ci err = PTR_ERR(ipcp); 101062306a36Sopenharmony_ci goto out_unlock1; 101162306a36Sopenharmony_ci } 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci shp = container_of(ipcp, struct shmid_kernel, shm_perm); 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci err = security_shm_shmctl(&shp->shm_perm, cmd); 101662306a36Sopenharmony_ci if (err) 101762306a36Sopenharmony_ci goto out_unlock1; 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci switch (cmd) { 102062306a36Sopenharmony_ci case IPC_RMID: 102162306a36Sopenharmony_ci ipc_lock_object(&shp->shm_perm); 102262306a36Sopenharmony_ci /* do_shm_rmid unlocks the ipc object and rcu */ 102362306a36Sopenharmony_ci do_shm_rmid(ns, ipcp); 102462306a36Sopenharmony_ci goto out_up; 102562306a36Sopenharmony_ci case IPC_SET: 102662306a36Sopenharmony_ci ipc_lock_object(&shp->shm_perm); 102762306a36Sopenharmony_ci err = ipc_update_perm(&shmid64->shm_perm, ipcp); 102862306a36Sopenharmony_ci if (err) 102962306a36Sopenharmony_ci goto out_unlock0; 103062306a36Sopenharmony_ci shp->shm_ctim = ktime_get_real_seconds(); 103162306a36Sopenharmony_ci break; 103262306a36Sopenharmony_ci default: 103362306a36Sopenharmony_ci err = -EINVAL; 103462306a36Sopenharmony_ci goto out_unlock1; 103562306a36Sopenharmony_ci } 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ciout_unlock0: 103862306a36Sopenharmony_ci ipc_unlock_object(&shp->shm_perm); 103962306a36Sopenharmony_ciout_unlock1: 104062306a36Sopenharmony_ci rcu_read_unlock(); 104162306a36Sopenharmony_ciout_up: 104262306a36Sopenharmony_ci up_write(&shm_ids(ns).rwsem); 104362306a36Sopenharmony_ci return err; 104462306a36Sopenharmony_ci} 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_cistatic int shmctl_ipc_info(struct ipc_namespace *ns, 104762306a36Sopenharmony_ci struct shminfo64 *shminfo) 104862306a36Sopenharmony_ci{ 104962306a36Sopenharmony_ci int err = security_shm_shmctl(NULL, IPC_INFO); 105062306a36Sopenharmony_ci if (!err) { 105162306a36Sopenharmony_ci memset(shminfo, 0, sizeof(*shminfo)); 105262306a36Sopenharmony_ci shminfo->shmmni = shminfo->shmseg = ns->shm_ctlmni; 105362306a36Sopenharmony_ci shminfo->shmmax = ns->shm_ctlmax; 105462306a36Sopenharmony_ci shminfo->shmall = ns->shm_ctlall; 105562306a36Sopenharmony_ci shminfo->shmmin = SHMMIN; 105662306a36Sopenharmony_ci down_read(&shm_ids(ns).rwsem); 105762306a36Sopenharmony_ci err = ipc_get_maxidx(&shm_ids(ns)); 105862306a36Sopenharmony_ci up_read(&shm_ids(ns).rwsem); 105962306a36Sopenharmony_ci if (err < 0) 106062306a36Sopenharmony_ci err = 0; 106162306a36Sopenharmony_ci } 106262306a36Sopenharmony_ci return err; 106362306a36Sopenharmony_ci} 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_cistatic int shmctl_shm_info(struct ipc_namespace *ns, 106662306a36Sopenharmony_ci struct shm_info *shm_info) 106762306a36Sopenharmony_ci{ 106862306a36Sopenharmony_ci int err = security_shm_shmctl(NULL, SHM_INFO); 106962306a36Sopenharmony_ci if (!err) { 107062306a36Sopenharmony_ci memset(shm_info, 0, sizeof(*shm_info)); 107162306a36Sopenharmony_ci down_read(&shm_ids(ns).rwsem); 107262306a36Sopenharmony_ci shm_info->used_ids = shm_ids(ns).in_use; 107362306a36Sopenharmony_ci shm_get_stat(ns, &shm_info->shm_rss, &shm_info->shm_swp); 107462306a36Sopenharmony_ci shm_info->shm_tot = ns->shm_tot; 107562306a36Sopenharmony_ci shm_info->swap_attempts = 0; 107662306a36Sopenharmony_ci shm_info->swap_successes = 0; 107762306a36Sopenharmony_ci err = ipc_get_maxidx(&shm_ids(ns)); 107862306a36Sopenharmony_ci up_read(&shm_ids(ns).rwsem); 107962306a36Sopenharmony_ci if (err < 0) 108062306a36Sopenharmony_ci err = 0; 108162306a36Sopenharmony_ci } 108262306a36Sopenharmony_ci return err; 108362306a36Sopenharmony_ci} 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_cistatic int shmctl_stat(struct ipc_namespace *ns, int shmid, 108662306a36Sopenharmony_ci int cmd, struct shmid64_ds *tbuf) 108762306a36Sopenharmony_ci{ 108862306a36Sopenharmony_ci struct shmid_kernel *shp; 108962306a36Sopenharmony_ci int err; 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci memset(tbuf, 0, sizeof(*tbuf)); 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci rcu_read_lock(); 109462306a36Sopenharmony_ci if (cmd == SHM_STAT || cmd == SHM_STAT_ANY) { 109562306a36Sopenharmony_ci shp = shm_obtain_object(ns, shmid); 109662306a36Sopenharmony_ci if (IS_ERR(shp)) { 109762306a36Sopenharmony_ci err = PTR_ERR(shp); 109862306a36Sopenharmony_ci goto out_unlock; 109962306a36Sopenharmony_ci } 110062306a36Sopenharmony_ci } else { /* IPC_STAT */ 110162306a36Sopenharmony_ci shp = shm_obtain_object_check(ns, shmid); 110262306a36Sopenharmony_ci if (IS_ERR(shp)) { 110362306a36Sopenharmony_ci err = PTR_ERR(shp); 110462306a36Sopenharmony_ci goto out_unlock; 110562306a36Sopenharmony_ci } 110662306a36Sopenharmony_ci } 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci /* 110962306a36Sopenharmony_ci * Semantically SHM_STAT_ANY ought to be identical to 111062306a36Sopenharmony_ci * that functionality provided by the /proc/sysvipc/ 111162306a36Sopenharmony_ci * interface. As such, only audit these calls and 111262306a36Sopenharmony_ci * do not do traditional S_IRUGO permission checks on 111362306a36Sopenharmony_ci * the ipc object. 111462306a36Sopenharmony_ci */ 111562306a36Sopenharmony_ci if (cmd == SHM_STAT_ANY) 111662306a36Sopenharmony_ci audit_ipc_obj(&shp->shm_perm); 111762306a36Sopenharmony_ci else { 111862306a36Sopenharmony_ci err = -EACCES; 111962306a36Sopenharmony_ci if (ipcperms(ns, &shp->shm_perm, S_IRUGO)) 112062306a36Sopenharmony_ci goto out_unlock; 112162306a36Sopenharmony_ci } 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci err = security_shm_shmctl(&shp->shm_perm, cmd); 112462306a36Sopenharmony_ci if (err) 112562306a36Sopenharmony_ci goto out_unlock; 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci ipc_lock_object(&shp->shm_perm); 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci if (!ipc_valid_object(&shp->shm_perm)) { 113062306a36Sopenharmony_ci ipc_unlock_object(&shp->shm_perm); 113162306a36Sopenharmony_ci err = -EIDRM; 113262306a36Sopenharmony_ci goto out_unlock; 113362306a36Sopenharmony_ci } 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci kernel_to_ipc64_perm(&shp->shm_perm, &tbuf->shm_perm); 113662306a36Sopenharmony_ci tbuf->shm_segsz = shp->shm_segsz; 113762306a36Sopenharmony_ci tbuf->shm_atime = shp->shm_atim; 113862306a36Sopenharmony_ci tbuf->shm_dtime = shp->shm_dtim; 113962306a36Sopenharmony_ci tbuf->shm_ctime = shp->shm_ctim; 114062306a36Sopenharmony_ci#ifndef CONFIG_64BIT 114162306a36Sopenharmony_ci tbuf->shm_atime_high = shp->shm_atim >> 32; 114262306a36Sopenharmony_ci tbuf->shm_dtime_high = shp->shm_dtim >> 32; 114362306a36Sopenharmony_ci tbuf->shm_ctime_high = shp->shm_ctim >> 32; 114462306a36Sopenharmony_ci#endif 114562306a36Sopenharmony_ci tbuf->shm_cpid = pid_vnr(shp->shm_cprid); 114662306a36Sopenharmony_ci tbuf->shm_lpid = pid_vnr(shp->shm_lprid); 114762306a36Sopenharmony_ci tbuf->shm_nattch = shp->shm_nattch; 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci if (cmd == IPC_STAT) { 115062306a36Sopenharmony_ci /* 115162306a36Sopenharmony_ci * As defined in SUS: 115262306a36Sopenharmony_ci * Return 0 on success 115362306a36Sopenharmony_ci */ 115462306a36Sopenharmony_ci err = 0; 115562306a36Sopenharmony_ci } else { 115662306a36Sopenharmony_ci /* 115762306a36Sopenharmony_ci * SHM_STAT and SHM_STAT_ANY (both Linux specific) 115862306a36Sopenharmony_ci * Return the full id, including the sequence number 115962306a36Sopenharmony_ci */ 116062306a36Sopenharmony_ci err = shp->shm_perm.id; 116162306a36Sopenharmony_ci } 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci ipc_unlock_object(&shp->shm_perm); 116462306a36Sopenharmony_ciout_unlock: 116562306a36Sopenharmony_ci rcu_read_unlock(); 116662306a36Sopenharmony_ci return err; 116762306a36Sopenharmony_ci} 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_cistatic int shmctl_do_lock(struct ipc_namespace *ns, int shmid, int cmd) 117062306a36Sopenharmony_ci{ 117162306a36Sopenharmony_ci struct shmid_kernel *shp; 117262306a36Sopenharmony_ci struct file *shm_file; 117362306a36Sopenharmony_ci int err; 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci rcu_read_lock(); 117662306a36Sopenharmony_ci shp = shm_obtain_object_check(ns, shmid); 117762306a36Sopenharmony_ci if (IS_ERR(shp)) { 117862306a36Sopenharmony_ci err = PTR_ERR(shp); 117962306a36Sopenharmony_ci goto out_unlock1; 118062306a36Sopenharmony_ci } 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci audit_ipc_obj(&(shp->shm_perm)); 118362306a36Sopenharmony_ci err = security_shm_shmctl(&shp->shm_perm, cmd); 118462306a36Sopenharmony_ci if (err) 118562306a36Sopenharmony_ci goto out_unlock1; 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci ipc_lock_object(&shp->shm_perm); 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci /* check if shm_destroy() is tearing down shp */ 119062306a36Sopenharmony_ci if (!ipc_valid_object(&shp->shm_perm)) { 119162306a36Sopenharmony_ci err = -EIDRM; 119262306a36Sopenharmony_ci goto out_unlock0; 119362306a36Sopenharmony_ci } 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci if (!ns_capable(ns->user_ns, CAP_IPC_LOCK)) { 119662306a36Sopenharmony_ci kuid_t euid = current_euid(); 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci if (!uid_eq(euid, shp->shm_perm.uid) && 119962306a36Sopenharmony_ci !uid_eq(euid, shp->shm_perm.cuid)) { 120062306a36Sopenharmony_ci err = -EPERM; 120162306a36Sopenharmony_ci goto out_unlock0; 120262306a36Sopenharmony_ci } 120362306a36Sopenharmony_ci if (cmd == SHM_LOCK && !rlimit(RLIMIT_MEMLOCK)) { 120462306a36Sopenharmony_ci err = -EPERM; 120562306a36Sopenharmony_ci goto out_unlock0; 120662306a36Sopenharmony_ci } 120762306a36Sopenharmony_ci } 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci shm_file = shp->shm_file; 121062306a36Sopenharmony_ci if (is_file_hugepages(shm_file)) 121162306a36Sopenharmony_ci goto out_unlock0; 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci if (cmd == SHM_LOCK) { 121462306a36Sopenharmony_ci struct ucounts *ucounts = current_ucounts(); 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci err = shmem_lock(shm_file, 1, ucounts); 121762306a36Sopenharmony_ci if (!err && !(shp->shm_perm.mode & SHM_LOCKED)) { 121862306a36Sopenharmony_ci shp->shm_perm.mode |= SHM_LOCKED; 121962306a36Sopenharmony_ci shp->mlock_ucounts = ucounts; 122062306a36Sopenharmony_ci } 122162306a36Sopenharmony_ci goto out_unlock0; 122262306a36Sopenharmony_ci } 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci /* SHM_UNLOCK */ 122562306a36Sopenharmony_ci if (!(shp->shm_perm.mode & SHM_LOCKED)) 122662306a36Sopenharmony_ci goto out_unlock0; 122762306a36Sopenharmony_ci shmem_lock(shm_file, 0, shp->mlock_ucounts); 122862306a36Sopenharmony_ci shp->shm_perm.mode &= ~SHM_LOCKED; 122962306a36Sopenharmony_ci shp->mlock_ucounts = NULL; 123062306a36Sopenharmony_ci get_file(shm_file); 123162306a36Sopenharmony_ci ipc_unlock_object(&shp->shm_perm); 123262306a36Sopenharmony_ci rcu_read_unlock(); 123362306a36Sopenharmony_ci shmem_unlock_mapping(shm_file->f_mapping); 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci fput(shm_file); 123662306a36Sopenharmony_ci return err; 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ciout_unlock0: 123962306a36Sopenharmony_ci ipc_unlock_object(&shp->shm_perm); 124062306a36Sopenharmony_ciout_unlock1: 124162306a36Sopenharmony_ci rcu_read_unlock(); 124262306a36Sopenharmony_ci return err; 124362306a36Sopenharmony_ci} 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_cistatic long ksys_shmctl(int shmid, int cmd, struct shmid_ds __user *buf, int version) 124662306a36Sopenharmony_ci{ 124762306a36Sopenharmony_ci int err; 124862306a36Sopenharmony_ci struct ipc_namespace *ns; 124962306a36Sopenharmony_ci struct shmid64_ds sem64; 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_ci if (cmd < 0 || shmid < 0) 125262306a36Sopenharmony_ci return -EINVAL; 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci ns = current->nsproxy->ipc_ns; 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci switch (cmd) { 125762306a36Sopenharmony_ci case IPC_INFO: { 125862306a36Sopenharmony_ci struct shminfo64 shminfo; 125962306a36Sopenharmony_ci err = shmctl_ipc_info(ns, &shminfo); 126062306a36Sopenharmony_ci if (err < 0) 126162306a36Sopenharmony_ci return err; 126262306a36Sopenharmony_ci if (copy_shminfo_to_user(buf, &shminfo, version)) 126362306a36Sopenharmony_ci err = -EFAULT; 126462306a36Sopenharmony_ci return err; 126562306a36Sopenharmony_ci } 126662306a36Sopenharmony_ci case SHM_INFO: { 126762306a36Sopenharmony_ci struct shm_info shm_info; 126862306a36Sopenharmony_ci err = shmctl_shm_info(ns, &shm_info); 126962306a36Sopenharmony_ci if (err < 0) 127062306a36Sopenharmony_ci return err; 127162306a36Sopenharmony_ci if (copy_to_user(buf, &shm_info, sizeof(shm_info))) 127262306a36Sopenharmony_ci err = -EFAULT; 127362306a36Sopenharmony_ci return err; 127462306a36Sopenharmony_ci } 127562306a36Sopenharmony_ci case SHM_STAT: 127662306a36Sopenharmony_ci case SHM_STAT_ANY: 127762306a36Sopenharmony_ci case IPC_STAT: { 127862306a36Sopenharmony_ci err = shmctl_stat(ns, shmid, cmd, &sem64); 127962306a36Sopenharmony_ci if (err < 0) 128062306a36Sopenharmony_ci return err; 128162306a36Sopenharmony_ci if (copy_shmid_to_user(buf, &sem64, version)) 128262306a36Sopenharmony_ci err = -EFAULT; 128362306a36Sopenharmony_ci return err; 128462306a36Sopenharmony_ci } 128562306a36Sopenharmony_ci case IPC_SET: 128662306a36Sopenharmony_ci if (copy_shmid_from_user(&sem64, buf, version)) 128762306a36Sopenharmony_ci return -EFAULT; 128862306a36Sopenharmony_ci fallthrough; 128962306a36Sopenharmony_ci case IPC_RMID: 129062306a36Sopenharmony_ci return shmctl_down(ns, shmid, cmd, &sem64); 129162306a36Sopenharmony_ci case SHM_LOCK: 129262306a36Sopenharmony_ci case SHM_UNLOCK: 129362306a36Sopenharmony_ci return shmctl_do_lock(ns, shmid, cmd); 129462306a36Sopenharmony_ci default: 129562306a36Sopenharmony_ci return -EINVAL; 129662306a36Sopenharmony_ci } 129762306a36Sopenharmony_ci} 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ciSYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf) 130062306a36Sopenharmony_ci{ 130162306a36Sopenharmony_ci return ksys_shmctl(shmid, cmd, buf, IPC_64); 130262306a36Sopenharmony_ci} 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci#ifdef CONFIG_ARCH_WANT_IPC_PARSE_VERSION 130562306a36Sopenharmony_cilong ksys_old_shmctl(int shmid, int cmd, struct shmid_ds __user *buf) 130662306a36Sopenharmony_ci{ 130762306a36Sopenharmony_ci int version = ipc_parse_version(&cmd); 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci return ksys_shmctl(shmid, cmd, buf, version); 131062306a36Sopenharmony_ci} 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ciSYSCALL_DEFINE3(old_shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf) 131362306a36Sopenharmony_ci{ 131462306a36Sopenharmony_ci return ksys_old_shmctl(shmid, cmd, buf); 131562306a36Sopenharmony_ci} 131662306a36Sopenharmony_ci#endif 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_cistruct compat_shmid_ds { 132162306a36Sopenharmony_ci struct compat_ipc_perm shm_perm; 132262306a36Sopenharmony_ci int shm_segsz; 132362306a36Sopenharmony_ci old_time32_t shm_atime; 132462306a36Sopenharmony_ci old_time32_t shm_dtime; 132562306a36Sopenharmony_ci old_time32_t shm_ctime; 132662306a36Sopenharmony_ci compat_ipc_pid_t shm_cpid; 132762306a36Sopenharmony_ci compat_ipc_pid_t shm_lpid; 132862306a36Sopenharmony_ci unsigned short shm_nattch; 132962306a36Sopenharmony_ci unsigned short shm_unused; 133062306a36Sopenharmony_ci compat_uptr_t shm_unused2; 133162306a36Sopenharmony_ci compat_uptr_t shm_unused3; 133262306a36Sopenharmony_ci}; 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_cistruct compat_shminfo64 { 133562306a36Sopenharmony_ci compat_ulong_t shmmax; 133662306a36Sopenharmony_ci compat_ulong_t shmmin; 133762306a36Sopenharmony_ci compat_ulong_t shmmni; 133862306a36Sopenharmony_ci compat_ulong_t shmseg; 133962306a36Sopenharmony_ci compat_ulong_t shmall; 134062306a36Sopenharmony_ci compat_ulong_t __unused1; 134162306a36Sopenharmony_ci compat_ulong_t __unused2; 134262306a36Sopenharmony_ci compat_ulong_t __unused3; 134362306a36Sopenharmony_ci compat_ulong_t __unused4; 134462306a36Sopenharmony_ci}; 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_cistruct compat_shm_info { 134762306a36Sopenharmony_ci compat_int_t used_ids; 134862306a36Sopenharmony_ci compat_ulong_t shm_tot, shm_rss, shm_swp; 134962306a36Sopenharmony_ci compat_ulong_t swap_attempts, swap_successes; 135062306a36Sopenharmony_ci}; 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_cistatic int copy_compat_shminfo_to_user(void __user *buf, struct shminfo64 *in, 135362306a36Sopenharmony_ci int version) 135462306a36Sopenharmony_ci{ 135562306a36Sopenharmony_ci if (in->shmmax > INT_MAX) 135662306a36Sopenharmony_ci in->shmmax = INT_MAX; 135762306a36Sopenharmony_ci if (version == IPC_64) { 135862306a36Sopenharmony_ci struct compat_shminfo64 info; 135962306a36Sopenharmony_ci memset(&info, 0, sizeof(info)); 136062306a36Sopenharmony_ci info.shmmax = in->shmmax; 136162306a36Sopenharmony_ci info.shmmin = in->shmmin; 136262306a36Sopenharmony_ci info.shmmni = in->shmmni; 136362306a36Sopenharmony_ci info.shmseg = in->shmseg; 136462306a36Sopenharmony_ci info.shmall = in->shmall; 136562306a36Sopenharmony_ci return copy_to_user(buf, &info, sizeof(info)); 136662306a36Sopenharmony_ci } else { 136762306a36Sopenharmony_ci struct shminfo info; 136862306a36Sopenharmony_ci memset(&info, 0, sizeof(info)); 136962306a36Sopenharmony_ci info.shmmax = in->shmmax; 137062306a36Sopenharmony_ci info.shmmin = in->shmmin; 137162306a36Sopenharmony_ci info.shmmni = in->shmmni; 137262306a36Sopenharmony_ci info.shmseg = in->shmseg; 137362306a36Sopenharmony_ci info.shmall = in->shmall; 137462306a36Sopenharmony_ci return copy_to_user(buf, &info, sizeof(info)); 137562306a36Sopenharmony_ci } 137662306a36Sopenharmony_ci} 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_cistatic int put_compat_shm_info(struct shm_info *ip, 137962306a36Sopenharmony_ci struct compat_shm_info __user *uip) 138062306a36Sopenharmony_ci{ 138162306a36Sopenharmony_ci struct compat_shm_info info; 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci memset(&info, 0, sizeof(info)); 138462306a36Sopenharmony_ci info.used_ids = ip->used_ids; 138562306a36Sopenharmony_ci info.shm_tot = ip->shm_tot; 138662306a36Sopenharmony_ci info.shm_rss = ip->shm_rss; 138762306a36Sopenharmony_ci info.shm_swp = ip->shm_swp; 138862306a36Sopenharmony_ci info.swap_attempts = ip->swap_attempts; 138962306a36Sopenharmony_ci info.swap_successes = ip->swap_successes; 139062306a36Sopenharmony_ci return copy_to_user(uip, &info, sizeof(info)); 139162306a36Sopenharmony_ci} 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_cistatic int copy_compat_shmid_to_user(void __user *buf, struct shmid64_ds *in, 139462306a36Sopenharmony_ci int version) 139562306a36Sopenharmony_ci{ 139662306a36Sopenharmony_ci if (version == IPC_64) { 139762306a36Sopenharmony_ci struct compat_shmid64_ds v; 139862306a36Sopenharmony_ci memset(&v, 0, sizeof(v)); 139962306a36Sopenharmony_ci to_compat_ipc64_perm(&v.shm_perm, &in->shm_perm); 140062306a36Sopenharmony_ci v.shm_atime = lower_32_bits(in->shm_atime); 140162306a36Sopenharmony_ci v.shm_atime_high = upper_32_bits(in->shm_atime); 140262306a36Sopenharmony_ci v.shm_dtime = lower_32_bits(in->shm_dtime); 140362306a36Sopenharmony_ci v.shm_dtime_high = upper_32_bits(in->shm_dtime); 140462306a36Sopenharmony_ci v.shm_ctime = lower_32_bits(in->shm_ctime); 140562306a36Sopenharmony_ci v.shm_ctime_high = upper_32_bits(in->shm_ctime); 140662306a36Sopenharmony_ci v.shm_segsz = in->shm_segsz; 140762306a36Sopenharmony_ci v.shm_nattch = in->shm_nattch; 140862306a36Sopenharmony_ci v.shm_cpid = in->shm_cpid; 140962306a36Sopenharmony_ci v.shm_lpid = in->shm_lpid; 141062306a36Sopenharmony_ci return copy_to_user(buf, &v, sizeof(v)); 141162306a36Sopenharmony_ci } else { 141262306a36Sopenharmony_ci struct compat_shmid_ds v; 141362306a36Sopenharmony_ci memset(&v, 0, sizeof(v)); 141462306a36Sopenharmony_ci to_compat_ipc_perm(&v.shm_perm, &in->shm_perm); 141562306a36Sopenharmony_ci v.shm_perm.key = in->shm_perm.key; 141662306a36Sopenharmony_ci v.shm_atime = in->shm_atime; 141762306a36Sopenharmony_ci v.shm_dtime = in->shm_dtime; 141862306a36Sopenharmony_ci v.shm_ctime = in->shm_ctime; 141962306a36Sopenharmony_ci v.shm_segsz = in->shm_segsz; 142062306a36Sopenharmony_ci v.shm_nattch = in->shm_nattch; 142162306a36Sopenharmony_ci v.shm_cpid = in->shm_cpid; 142262306a36Sopenharmony_ci v.shm_lpid = in->shm_lpid; 142362306a36Sopenharmony_ci return copy_to_user(buf, &v, sizeof(v)); 142462306a36Sopenharmony_ci } 142562306a36Sopenharmony_ci} 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_cistatic int copy_compat_shmid_from_user(struct shmid64_ds *out, void __user *buf, 142862306a36Sopenharmony_ci int version) 142962306a36Sopenharmony_ci{ 143062306a36Sopenharmony_ci memset(out, 0, sizeof(*out)); 143162306a36Sopenharmony_ci if (version == IPC_64) { 143262306a36Sopenharmony_ci struct compat_shmid64_ds __user *p = buf; 143362306a36Sopenharmony_ci return get_compat_ipc64_perm(&out->shm_perm, &p->shm_perm); 143462306a36Sopenharmony_ci } else { 143562306a36Sopenharmony_ci struct compat_shmid_ds __user *p = buf; 143662306a36Sopenharmony_ci return get_compat_ipc_perm(&out->shm_perm, &p->shm_perm); 143762306a36Sopenharmony_ci } 143862306a36Sopenharmony_ci} 143962306a36Sopenharmony_ci 144062306a36Sopenharmony_cistatic long compat_ksys_shmctl(int shmid, int cmd, void __user *uptr, int version) 144162306a36Sopenharmony_ci{ 144262306a36Sopenharmony_ci struct ipc_namespace *ns; 144362306a36Sopenharmony_ci struct shmid64_ds sem64; 144462306a36Sopenharmony_ci int err; 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_ci ns = current->nsproxy->ipc_ns; 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_ci if (cmd < 0 || shmid < 0) 144962306a36Sopenharmony_ci return -EINVAL; 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci switch (cmd) { 145262306a36Sopenharmony_ci case IPC_INFO: { 145362306a36Sopenharmony_ci struct shminfo64 shminfo; 145462306a36Sopenharmony_ci err = shmctl_ipc_info(ns, &shminfo); 145562306a36Sopenharmony_ci if (err < 0) 145662306a36Sopenharmony_ci return err; 145762306a36Sopenharmony_ci if (copy_compat_shminfo_to_user(uptr, &shminfo, version)) 145862306a36Sopenharmony_ci err = -EFAULT; 145962306a36Sopenharmony_ci return err; 146062306a36Sopenharmony_ci } 146162306a36Sopenharmony_ci case SHM_INFO: { 146262306a36Sopenharmony_ci struct shm_info shm_info; 146362306a36Sopenharmony_ci err = shmctl_shm_info(ns, &shm_info); 146462306a36Sopenharmony_ci if (err < 0) 146562306a36Sopenharmony_ci return err; 146662306a36Sopenharmony_ci if (put_compat_shm_info(&shm_info, uptr)) 146762306a36Sopenharmony_ci err = -EFAULT; 146862306a36Sopenharmony_ci return err; 146962306a36Sopenharmony_ci } 147062306a36Sopenharmony_ci case IPC_STAT: 147162306a36Sopenharmony_ci case SHM_STAT_ANY: 147262306a36Sopenharmony_ci case SHM_STAT: 147362306a36Sopenharmony_ci err = shmctl_stat(ns, shmid, cmd, &sem64); 147462306a36Sopenharmony_ci if (err < 0) 147562306a36Sopenharmony_ci return err; 147662306a36Sopenharmony_ci if (copy_compat_shmid_to_user(uptr, &sem64, version)) 147762306a36Sopenharmony_ci err = -EFAULT; 147862306a36Sopenharmony_ci return err; 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_ci case IPC_SET: 148162306a36Sopenharmony_ci if (copy_compat_shmid_from_user(&sem64, uptr, version)) 148262306a36Sopenharmony_ci return -EFAULT; 148362306a36Sopenharmony_ci fallthrough; 148462306a36Sopenharmony_ci case IPC_RMID: 148562306a36Sopenharmony_ci return shmctl_down(ns, shmid, cmd, &sem64); 148662306a36Sopenharmony_ci case SHM_LOCK: 148762306a36Sopenharmony_ci case SHM_UNLOCK: 148862306a36Sopenharmony_ci return shmctl_do_lock(ns, shmid, cmd); 148962306a36Sopenharmony_ci default: 149062306a36Sopenharmony_ci return -EINVAL; 149162306a36Sopenharmony_ci } 149262306a36Sopenharmony_ci return err; 149362306a36Sopenharmony_ci} 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_ciCOMPAT_SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, void __user *, uptr) 149662306a36Sopenharmony_ci{ 149762306a36Sopenharmony_ci return compat_ksys_shmctl(shmid, cmd, uptr, IPC_64); 149862306a36Sopenharmony_ci} 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_ci#ifdef CONFIG_ARCH_WANT_COMPAT_IPC_PARSE_VERSION 150162306a36Sopenharmony_cilong compat_ksys_old_shmctl(int shmid, int cmd, void __user *uptr) 150262306a36Sopenharmony_ci{ 150362306a36Sopenharmony_ci int version = compat_ipc_parse_version(&cmd); 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_ci return compat_ksys_shmctl(shmid, cmd, uptr, version); 150662306a36Sopenharmony_ci} 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_ciCOMPAT_SYSCALL_DEFINE3(old_shmctl, int, shmid, int, cmd, void __user *, uptr) 150962306a36Sopenharmony_ci{ 151062306a36Sopenharmony_ci return compat_ksys_old_shmctl(shmid, cmd, uptr); 151162306a36Sopenharmony_ci} 151262306a36Sopenharmony_ci#endif 151362306a36Sopenharmony_ci#endif 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_ci/* 151662306a36Sopenharmony_ci * Fix shmaddr, allocate descriptor, map shm, add attach descriptor to lists. 151762306a36Sopenharmony_ci * 151862306a36Sopenharmony_ci * NOTE! Despite the name, this is NOT a direct system call entrypoint. The 151962306a36Sopenharmony_ci * "raddr" thing points to kernel space, and there has to be a wrapper around 152062306a36Sopenharmony_ci * this. 152162306a36Sopenharmony_ci */ 152262306a36Sopenharmony_cilong do_shmat(int shmid, char __user *shmaddr, int shmflg, 152362306a36Sopenharmony_ci ulong *raddr, unsigned long shmlba) 152462306a36Sopenharmony_ci{ 152562306a36Sopenharmony_ci struct shmid_kernel *shp; 152662306a36Sopenharmony_ci unsigned long addr = (unsigned long)shmaddr; 152762306a36Sopenharmony_ci unsigned long size; 152862306a36Sopenharmony_ci struct file *file, *base; 152962306a36Sopenharmony_ci int err; 153062306a36Sopenharmony_ci unsigned long flags = MAP_SHARED; 153162306a36Sopenharmony_ci unsigned long prot; 153262306a36Sopenharmony_ci int acc_mode; 153362306a36Sopenharmony_ci struct ipc_namespace *ns; 153462306a36Sopenharmony_ci struct shm_file_data *sfd; 153562306a36Sopenharmony_ci int f_flags; 153662306a36Sopenharmony_ci unsigned long populate = 0; 153762306a36Sopenharmony_ci 153862306a36Sopenharmony_ci err = -EINVAL; 153962306a36Sopenharmony_ci if (shmid < 0) 154062306a36Sopenharmony_ci goto out; 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_ci if (addr) { 154362306a36Sopenharmony_ci if (addr & (shmlba - 1)) { 154462306a36Sopenharmony_ci if (shmflg & SHM_RND) { 154562306a36Sopenharmony_ci addr &= ~(shmlba - 1); /* round down */ 154662306a36Sopenharmony_ci 154762306a36Sopenharmony_ci /* 154862306a36Sopenharmony_ci * Ensure that the round-down is non-nil 154962306a36Sopenharmony_ci * when remapping. This can happen for 155062306a36Sopenharmony_ci * cases when addr < shmlba. 155162306a36Sopenharmony_ci */ 155262306a36Sopenharmony_ci if (!addr && (shmflg & SHM_REMAP)) 155362306a36Sopenharmony_ci goto out; 155462306a36Sopenharmony_ci } else 155562306a36Sopenharmony_ci#ifndef __ARCH_FORCE_SHMLBA 155662306a36Sopenharmony_ci if (addr & ~PAGE_MASK) 155762306a36Sopenharmony_ci#endif 155862306a36Sopenharmony_ci goto out; 155962306a36Sopenharmony_ci } 156062306a36Sopenharmony_ci 156162306a36Sopenharmony_ci flags |= MAP_FIXED; 156262306a36Sopenharmony_ci } else if ((shmflg & SHM_REMAP)) 156362306a36Sopenharmony_ci goto out; 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_ci if (shmflg & SHM_RDONLY) { 156662306a36Sopenharmony_ci prot = PROT_READ; 156762306a36Sopenharmony_ci acc_mode = S_IRUGO; 156862306a36Sopenharmony_ci f_flags = O_RDONLY; 156962306a36Sopenharmony_ci } else { 157062306a36Sopenharmony_ci prot = PROT_READ | PROT_WRITE; 157162306a36Sopenharmony_ci acc_mode = S_IRUGO | S_IWUGO; 157262306a36Sopenharmony_ci f_flags = O_RDWR; 157362306a36Sopenharmony_ci } 157462306a36Sopenharmony_ci if (shmflg & SHM_EXEC) { 157562306a36Sopenharmony_ci prot |= PROT_EXEC; 157662306a36Sopenharmony_ci acc_mode |= S_IXUGO; 157762306a36Sopenharmony_ci } 157862306a36Sopenharmony_ci 157962306a36Sopenharmony_ci /* 158062306a36Sopenharmony_ci * We cannot rely on the fs check since SYSV IPC does have an 158162306a36Sopenharmony_ci * additional creator id... 158262306a36Sopenharmony_ci */ 158362306a36Sopenharmony_ci ns = current->nsproxy->ipc_ns; 158462306a36Sopenharmony_ci rcu_read_lock(); 158562306a36Sopenharmony_ci shp = shm_obtain_object_check(ns, shmid); 158662306a36Sopenharmony_ci if (IS_ERR(shp)) { 158762306a36Sopenharmony_ci err = PTR_ERR(shp); 158862306a36Sopenharmony_ci goto out_unlock; 158962306a36Sopenharmony_ci } 159062306a36Sopenharmony_ci 159162306a36Sopenharmony_ci err = -EACCES; 159262306a36Sopenharmony_ci if (ipcperms(ns, &shp->shm_perm, acc_mode)) 159362306a36Sopenharmony_ci goto out_unlock; 159462306a36Sopenharmony_ci 159562306a36Sopenharmony_ci err = security_shm_shmat(&shp->shm_perm, shmaddr, shmflg); 159662306a36Sopenharmony_ci if (err) 159762306a36Sopenharmony_ci goto out_unlock; 159862306a36Sopenharmony_ci 159962306a36Sopenharmony_ci ipc_lock_object(&shp->shm_perm); 160062306a36Sopenharmony_ci 160162306a36Sopenharmony_ci /* check if shm_destroy() is tearing down shp */ 160262306a36Sopenharmony_ci if (!ipc_valid_object(&shp->shm_perm)) { 160362306a36Sopenharmony_ci ipc_unlock_object(&shp->shm_perm); 160462306a36Sopenharmony_ci err = -EIDRM; 160562306a36Sopenharmony_ci goto out_unlock; 160662306a36Sopenharmony_ci } 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ci /* 160962306a36Sopenharmony_ci * We need to take a reference to the real shm file to prevent the 161062306a36Sopenharmony_ci * pointer from becoming stale in cases where the lifetime of the outer 161162306a36Sopenharmony_ci * file extends beyond that of the shm segment. It's not usually 161262306a36Sopenharmony_ci * possible, but it can happen during remap_file_pages() emulation as 161362306a36Sopenharmony_ci * that unmaps the memory, then does ->mmap() via file reference only. 161462306a36Sopenharmony_ci * We'll deny the ->mmap() if the shm segment was since removed, but to 161562306a36Sopenharmony_ci * detect shm ID reuse we need to compare the file pointers. 161662306a36Sopenharmony_ci */ 161762306a36Sopenharmony_ci base = get_file(shp->shm_file); 161862306a36Sopenharmony_ci shp->shm_nattch++; 161962306a36Sopenharmony_ci size = i_size_read(file_inode(base)); 162062306a36Sopenharmony_ci ipc_unlock_object(&shp->shm_perm); 162162306a36Sopenharmony_ci rcu_read_unlock(); 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_ci err = -ENOMEM; 162462306a36Sopenharmony_ci sfd = kzalloc(sizeof(*sfd), GFP_KERNEL); 162562306a36Sopenharmony_ci if (!sfd) { 162662306a36Sopenharmony_ci fput(base); 162762306a36Sopenharmony_ci goto out_nattch; 162862306a36Sopenharmony_ci } 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_ci file = alloc_file_clone(base, f_flags, 163162306a36Sopenharmony_ci is_file_hugepages(base) ? 163262306a36Sopenharmony_ci &shm_file_operations_huge : 163362306a36Sopenharmony_ci &shm_file_operations); 163462306a36Sopenharmony_ci err = PTR_ERR(file); 163562306a36Sopenharmony_ci if (IS_ERR(file)) { 163662306a36Sopenharmony_ci kfree(sfd); 163762306a36Sopenharmony_ci fput(base); 163862306a36Sopenharmony_ci goto out_nattch; 163962306a36Sopenharmony_ci } 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_ci sfd->id = shp->shm_perm.id; 164262306a36Sopenharmony_ci sfd->ns = get_ipc_ns(ns); 164362306a36Sopenharmony_ci sfd->file = base; 164462306a36Sopenharmony_ci sfd->vm_ops = NULL; 164562306a36Sopenharmony_ci file->private_data = sfd; 164662306a36Sopenharmony_ci 164762306a36Sopenharmony_ci err = security_mmap_file(file, prot, flags); 164862306a36Sopenharmony_ci if (err) 164962306a36Sopenharmony_ci goto out_fput; 165062306a36Sopenharmony_ci 165162306a36Sopenharmony_ci if (mmap_write_lock_killable(current->mm)) { 165262306a36Sopenharmony_ci err = -EINTR; 165362306a36Sopenharmony_ci goto out_fput; 165462306a36Sopenharmony_ci } 165562306a36Sopenharmony_ci 165662306a36Sopenharmony_ci if (addr && !(shmflg & SHM_REMAP)) { 165762306a36Sopenharmony_ci err = -EINVAL; 165862306a36Sopenharmony_ci if (addr + size < addr) 165962306a36Sopenharmony_ci goto invalid; 166062306a36Sopenharmony_ci 166162306a36Sopenharmony_ci if (find_vma_intersection(current->mm, addr, addr + size)) 166262306a36Sopenharmony_ci goto invalid; 166362306a36Sopenharmony_ci } 166462306a36Sopenharmony_ci 166562306a36Sopenharmony_ci addr = do_mmap(file, addr, size, prot, flags, 0, 0, &populate, NULL); 166662306a36Sopenharmony_ci *raddr = addr; 166762306a36Sopenharmony_ci err = 0; 166862306a36Sopenharmony_ci if (IS_ERR_VALUE(addr)) 166962306a36Sopenharmony_ci err = (long)addr; 167062306a36Sopenharmony_ciinvalid: 167162306a36Sopenharmony_ci mmap_write_unlock(current->mm); 167262306a36Sopenharmony_ci if (populate) 167362306a36Sopenharmony_ci mm_populate(addr, populate); 167462306a36Sopenharmony_ci 167562306a36Sopenharmony_ciout_fput: 167662306a36Sopenharmony_ci fput(file); 167762306a36Sopenharmony_ci 167862306a36Sopenharmony_ciout_nattch: 167962306a36Sopenharmony_ci down_write(&shm_ids(ns).rwsem); 168062306a36Sopenharmony_ci shp = shm_lock(ns, shmid); 168162306a36Sopenharmony_ci shp->shm_nattch--; 168262306a36Sopenharmony_ci 168362306a36Sopenharmony_ci if (shm_may_destroy(shp)) 168462306a36Sopenharmony_ci shm_destroy(ns, shp); 168562306a36Sopenharmony_ci else 168662306a36Sopenharmony_ci shm_unlock(shp); 168762306a36Sopenharmony_ci up_write(&shm_ids(ns).rwsem); 168862306a36Sopenharmony_ci return err; 168962306a36Sopenharmony_ci 169062306a36Sopenharmony_ciout_unlock: 169162306a36Sopenharmony_ci rcu_read_unlock(); 169262306a36Sopenharmony_ciout: 169362306a36Sopenharmony_ci return err; 169462306a36Sopenharmony_ci} 169562306a36Sopenharmony_ci 169662306a36Sopenharmony_ciSYSCALL_DEFINE3(shmat, int, shmid, char __user *, shmaddr, int, shmflg) 169762306a36Sopenharmony_ci{ 169862306a36Sopenharmony_ci unsigned long ret; 169962306a36Sopenharmony_ci long err; 170062306a36Sopenharmony_ci 170162306a36Sopenharmony_ci err = do_shmat(shmid, shmaddr, shmflg, &ret, SHMLBA); 170262306a36Sopenharmony_ci if (err) 170362306a36Sopenharmony_ci return err; 170462306a36Sopenharmony_ci force_successful_syscall_return(); 170562306a36Sopenharmony_ci return (long)ret; 170662306a36Sopenharmony_ci} 170762306a36Sopenharmony_ci 170862306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 170962306a36Sopenharmony_ci 171062306a36Sopenharmony_ci#ifndef COMPAT_SHMLBA 171162306a36Sopenharmony_ci#define COMPAT_SHMLBA SHMLBA 171262306a36Sopenharmony_ci#endif 171362306a36Sopenharmony_ci 171462306a36Sopenharmony_ciCOMPAT_SYSCALL_DEFINE3(shmat, int, shmid, compat_uptr_t, shmaddr, int, shmflg) 171562306a36Sopenharmony_ci{ 171662306a36Sopenharmony_ci unsigned long ret; 171762306a36Sopenharmony_ci long err; 171862306a36Sopenharmony_ci 171962306a36Sopenharmony_ci err = do_shmat(shmid, compat_ptr(shmaddr), shmflg, &ret, COMPAT_SHMLBA); 172062306a36Sopenharmony_ci if (err) 172162306a36Sopenharmony_ci return err; 172262306a36Sopenharmony_ci force_successful_syscall_return(); 172362306a36Sopenharmony_ci return (long)ret; 172462306a36Sopenharmony_ci} 172562306a36Sopenharmony_ci#endif 172662306a36Sopenharmony_ci 172762306a36Sopenharmony_ci/* 172862306a36Sopenharmony_ci * detach and kill segment if marked destroyed. 172962306a36Sopenharmony_ci * The work is done in shm_close. 173062306a36Sopenharmony_ci */ 173162306a36Sopenharmony_cilong ksys_shmdt(char __user *shmaddr) 173262306a36Sopenharmony_ci{ 173362306a36Sopenharmony_ci struct mm_struct *mm = current->mm; 173462306a36Sopenharmony_ci struct vm_area_struct *vma; 173562306a36Sopenharmony_ci unsigned long addr = (unsigned long)shmaddr; 173662306a36Sopenharmony_ci int retval = -EINVAL; 173762306a36Sopenharmony_ci#ifdef CONFIG_MMU 173862306a36Sopenharmony_ci loff_t size = 0; 173962306a36Sopenharmony_ci struct file *file; 174062306a36Sopenharmony_ci VMA_ITERATOR(vmi, mm, addr); 174162306a36Sopenharmony_ci#endif 174262306a36Sopenharmony_ci 174362306a36Sopenharmony_ci if (addr & ~PAGE_MASK) 174462306a36Sopenharmony_ci return retval; 174562306a36Sopenharmony_ci 174662306a36Sopenharmony_ci if (mmap_write_lock_killable(mm)) 174762306a36Sopenharmony_ci return -EINTR; 174862306a36Sopenharmony_ci 174962306a36Sopenharmony_ci /* 175062306a36Sopenharmony_ci * This function tries to be smart and unmap shm segments that 175162306a36Sopenharmony_ci * were modified by partial mlock or munmap calls: 175262306a36Sopenharmony_ci * - It first determines the size of the shm segment that should be 175362306a36Sopenharmony_ci * unmapped: It searches for a vma that is backed by shm and that 175462306a36Sopenharmony_ci * started at address shmaddr. It records it's size and then unmaps 175562306a36Sopenharmony_ci * it. 175662306a36Sopenharmony_ci * - Then it unmaps all shm vmas that started at shmaddr and that 175762306a36Sopenharmony_ci * are within the initially determined size and that are from the 175862306a36Sopenharmony_ci * same shm segment from which we determined the size. 175962306a36Sopenharmony_ci * Errors from do_munmap are ignored: the function only fails if 176062306a36Sopenharmony_ci * it's called with invalid parameters or if it's called to unmap 176162306a36Sopenharmony_ci * a part of a vma. Both calls in this function are for full vmas, 176262306a36Sopenharmony_ci * the parameters are directly copied from the vma itself and always 176362306a36Sopenharmony_ci * valid - therefore do_munmap cannot fail. (famous last words?) 176462306a36Sopenharmony_ci */ 176562306a36Sopenharmony_ci /* 176662306a36Sopenharmony_ci * If it had been mremap()'d, the starting address would not 176762306a36Sopenharmony_ci * match the usual checks anyway. So assume all vma's are 176862306a36Sopenharmony_ci * above the starting address given. 176962306a36Sopenharmony_ci */ 177062306a36Sopenharmony_ci 177162306a36Sopenharmony_ci#ifdef CONFIG_MMU 177262306a36Sopenharmony_ci for_each_vma(vmi, vma) { 177362306a36Sopenharmony_ci /* 177462306a36Sopenharmony_ci * Check if the starting address would match, i.e. it's 177562306a36Sopenharmony_ci * a fragment created by mprotect() and/or munmap(), or it 177662306a36Sopenharmony_ci * otherwise it starts at this address with no hassles. 177762306a36Sopenharmony_ci */ 177862306a36Sopenharmony_ci if ((vma->vm_ops == &shm_vm_ops) && 177962306a36Sopenharmony_ci (vma->vm_start - addr)/PAGE_SIZE == vma->vm_pgoff) { 178062306a36Sopenharmony_ci 178162306a36Sopenharmony_ci /* 178262306a36Sopenharmony_ci * Record the file of the shm segment being 178362306a36Sopenharmony_ci * unmapped. With mremap(), someone could place 178462306a36Sopenharmony_ci * page from another segment but with equal offsets 178562306a36Sopenharmony_ci * in the range we are unmapping. 178662306a36Sopenharmony_ci */ 178762306a36Sopenharmony_ci file = vma->vm_file; 178862306a36Sopenharmony_ci size = i_size_read(file_inode(vma->vm_file)); 178962306a36Sopenharmony_ci do_vma_munmap(&vmi, vma, vma->vm_start, vma->vm_end, 179062306a36Sopenharmony_ci NULL, false); 179162306a36Sopenharmony_ci /* 179262306a36Sopenharmony_ci * We discovered the size of the shm segment, so 179362306a36Sopenharmony_ci * break out of here and fall through to the next 179462306a36Sopenharmony_ci * loop that uses the size information to stop 179562306a36Sopenharmony_ci * searching for matching vma's. 179662306a36Sopenharmony_ci */ 179762306a36Sopenharmony_ci retval = 0; 179862306a36Sopenharmony_ci vma = vma_next(&vmi); 179962306a36Sopenharmony_ci break; 180062306a36Sopenharmony_ci } 180162306a36Sopenharmony_ci } 180262306a36Sopenharmony_ci 180362306a36Sopenharmony_ci /* 180462306a36Sopenharmony_ci * We need look no further than the maximum address a fragment 180562306a36Sopenharmony_ci * could possibly have landed at. Also cast things to loff_t to 180662306a36Sopenharmony_ci * prevent overflows and make comparisons vs. equal-width types. 180762306a36Sopenharmony_ci */ 180862306a36Sopenharmony_ci size = PAGE_ALIGN(size); 180962306a36Sopenharmony_ci while (vma && (loff_t)(vma->vm_end - addr) <= size) { 181062306a36Sopenharmony_ci /* finding a matching vma now does not alter retval */ 181162306a36Sopenharmony_ci if ((vma->vm_ops == &shm_vm_ops) && 181262306a36Sopenharmony_ci ((vma->vm_start - addr)/PAGE_SIZE == vma->vm_pgoff) && 181362306a36Sopenharmony_ci (vma->vm_file == file)) { 181462306a36Sopenharmony_ci do_vma_munmap(&vmi, vma, vma->vm_start, vma->vm_end, 181562306a36Sopenharmony_ci NULL, false); 181662306a36Sopenharmony_ci } 181762306a36Sopenharmony_ci 181862306a36Sopenharmony_ci vma = vma_next(&vmi); 181962306a36Sopenharmony_ci } 182062306a36Sopenharmony_ci 182162306a36Sopenharmony_ci#else /* CONFIG_MMU */ 182262306a36Sopenharmony_ci vma = vma_lookup(mm, addr); 182362306a36Sopenharmony_ci /* under NOMMU conditions, the exact address to be destroyed must be 182462306a36Sopenharmony_ci * given 182562306a36Sopenharmony_ci */ 182662306a36Sopenharmony_ci if (vma && vma->vm_start == addr && vma->vm_ops == &shm_vm_ops) { 182762306a36Sopenharmony_ci do_munmap(mm, vma->vm_start, vma->vm_end - vma->vm_start, NULL); 182862306a36Sopenharmony_ci retval = 0; 182962306a36Sopenharmony_ci } 183062306a36Sopenharmony_ci 183162306a36Sopenharmony_ci#endif 183262306a36Sopenharmony_ci 183362306a36Sopenharmony_ci mmap_write_unlock(mm); 183462306a36Sopenharmony_ci return retval; 183562306a36Sopenharmony_ci} 183662306a36Sopenharmony_ci 183762306a36Sopenharmony_ciSYSCALL_DEFINE1(shmdt, char __user *, shmaddr) 183862306a36Sopenharmony_ci{ 183962306a36Sopenharmony_ci return ksys_shmdt(shmaddr); 184062306a36Sopenharmony_ci} 184162306a36Sopenharmony_ci 184262306a36Sopenharmony_ci#ifdef CONFIG_PROC_FS 184362306a36Sopenharmony_cistatic int sysvipc_shm_proc_show(struct seq_file *s, void *it) 184462306a36Sopenharmony_ci{ 184562306a36Sopenharmony_ci struct pid_namespace *pid_ns = ipc_seq_pid_ns(s); 184662306a36Sopenharmony_ci struct user_namespace *user_ns = seq_user_ns(s); 184762306a36Sopenharmony_ci struct kern_ipc_perm *ipcp = it; 184862306a36Sopenharmony_ci struct shmid_kernel *shp; 184962306a36Sopenharmony_ci unsigned long rss = 0, swp = 0; 185062306a36Sopenharmony_ci 185162306a36Sopenharmony_ci shp = container_of(ipcp, struct shmid_kernel, shm_perm); 185262306a36Sopenharmony_ci shm_add_rss_swap(shp, &rss, &swp); 185362306a36Sopenharmony_ci 185462306a36Sopenharmony_ci#if BITS_PER_LONG <= 32 185562306a36Sopenharmony_ci#define SIZE_SPEC "%10lu" 185662306a36Sopenharmony_ci#else 185762306a36Sopenharmony_ci#define SIZE_SPEC "%21lu" 185862306a36Sopenharmony_ci#endif 185962306a36Sopenharmony_ci 186062306a36Sopenharmony_ci seq_printf(s, 186162306a36Sopenharmony_ci "%10d %10d %4o " SIZE_SPEC " %5u %5u " 186262306a36Sopenharmony_ci "%5lu %5u %5u %5u %5u %10llu %10llu %10llu " 186362306a36Sopenharmony_ci SIZE_SPEC " " SIZE_SPEC "\n", 186462306a36Sopenharmony_ci shp->shm_perm.key, 186562306a36Sopenharmony_ci shp->shm_perm.id, 186662306a36Sopenharmony_ci shp->shm_perm.mode, 186762306a36Sopenharmony_ci shp->shm_segsz, 186862306a36Sopenharmony_ci pid_nr_ns(shp->shm_cprid, pid_ns), 186962306a36Sopenharmony_ci pid_nr_ns(shp->shm_lprid, pid_ns), 187062306a36Sopenharmony_ci shp->shm_nattch, 187162306a36Sopenharmony_ci from_kuid_munged(user_ns, shp->shm_perm.uid), 187262306a36Sopenharmony_ci from_kgid_munged(user_ns, shp->shm_perm.gid), 187362306a36Sopenharmony_ci from_kuid_munged(user_ns, shp->shm_perm.cuid), 187462306a36Sopenharmony_ci from_kgid_munged(user_ns, shp->shm_perm.cgid), 187562306a36Sopenharmony_ci shp->shm_atim, 187662306a36Sopenharmony_ci shp->shm_dtim, 187762306a36Sopenharmony_ci shp->shm_ctim, 187862306a36Sopenharmony_ci rss * PAGE_SIZE, 187962306a36Sopenharmony_ci swp * PAGE_SIZE); 188062306a36Sopenharmony_ci 188162306a36Sopenharmony_ci return 0; 188262306a36Sopenharmony_ci} 188362306a36Sopenharmony_ci#endif 1884