18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/ipc/shm.c 48c2ecf20Sopenharmony_ci * Copyright (C) 1992, 1993 Krishna Balasubramanian 58c2ecf20Sopenharmony_ci * Many improvements/fixes by Bruno Haible. 68c2ecf20Sopenharmony_ci * Replaced `struct shm_desc' by `struct vm_area_struct', July 1994. 78c2ecf20Sopenharmony_ci * Fixed the shm swap deallocation (shm_unuse()), August 1998 Andrea Arcangeli. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * /proc/sysvipc/shm support (c) 1999 Dragos Acostachioaie <dragos@iname.com> 108c2ecf20Sopenharmony_ci * BIGMEM support, Andrea Arcangeli <andrea@suse.de> 118c2ecf20Sopenharmony_ci * SMP thread shm, Jean-Luc Boyard <jean-luc.boyard@siemens.fr> 128c2ecf20Sopenharmony_ci * HIGHMEM support, Ingo Molnar <mingo@redhat.com> 138c2ecf20Sopenharmony_ci * Make shmmax, shmall, shmmni sysctl'able, Christoph Rohland <cr@sap.com> 148c2ecf20Sopenharmony_ci * Shared /dev/zero support, Kanoj Sarcar <kanoj@sgi.com> 158c2ecf20Sopenharmony_ci * Move the mm functionality over to mm/shmem.c, Christoph Rohland <cr@sap.com> 168c2ecf20Sopenharmony_ci * 178c2ecf20Sopenharmony_ci * support for audit of ipc object properties and permission changes 188c2ecf20Sopenharmony_ci * Dustin Kirkland <dustin.kirkland@us.ibm.com> 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * namespaces support 218c2ecf20Sopenharmony_ci * OpenVZ, SWsoft Inc. 228c2ecf20Sopenharmony_ci * Pavel Emelianov <xemul@openvz.org> 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * Better ipc lock (kern_ipc_perm.lock) handling 258c2ecf20Sopenharmony_ci * Davidlohr Bueso <davidlohr.bueso@hp.com>, June 2013. 268c2ecf20Sopenharmony_ci */ 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include <linux/slab.h> 298c2ecf20Sopenharmony_ci#include <linux/mm.h> 308c2ecf20Sopenharmony_ci#include <linux/hugetlb.h> 318c2ecf20Sopenharmony_ci#include <linux/shm.h> 328c2ecf20Sopenharmony_ci#include <linux/init.h> 338c2ecf20Sopenharmony_ci#include <linux/file.h> 348c2ecf20Sopenharmony_ci#include <linux/mman.h> 358c2ecf20Sopenharmony_ci#include <linux/shmem_fs.h> 368c2ecf20Sopenharmony_ci#include <linux/security.h> 378c2ecf20Sopenharmony_ci#include <linux/syscalls.h> 388c2ecf20Sopenharmony_ci#include <linux/audit.h> 398c2ecf20Sopenharmony_ci#include <linux/capability.h> 408c2ecf20Sopenharmony_ci#include <linux/ptrace.h> 418c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 428c2ecf20Sopenharmony_ci#include <linux/rwsem.h> 438c2ecf20Sopenharmony_ci#include <linux/nsproxy.h> 448c2ecf20Sopenharmony_ci#include <linux/mount.h> 458c2ecf20Sopenharmony_ci#include <linux/ipc_namespace.h> 468c2ecf20Sopenharmony_ci#include <linux/rhashtable.h> 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci#include "util.h" 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistruct shmid_kernel /* private to the kernel */ 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci struct kern_ipc_perm shm_perm; 558c2ecf20Sopenharmony_ci struct file *shm_file; 568c2ecf20Sopenharmony_ci unsigned long shm_nattch; 578c2ecf20Sopenharmony_ci unsigned long shm_segsz; 588c2ecf20Sopenharmony_ci time64_t shm_atim; 598c2ecf20Sopenharmony_ci time64_t shm_dtim; 608c2ecf20Sopenharmony_ci time64_t shm_ctim; 618c2ecf20Sopenharmony_ci struct pid *shm_cprid; 628c2ecf20Sopenharmony_ci struct pid *shm_lprid; 638c2ecf20Sopenharmony_ci struct user_struct *mlock_user; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci /* 668c2ecf20Sopenharmony_ci * The task created the shm object, for 678c2ecf20Sopenharmony_ci * task_lock(shp->shm_creator) 688c2ecf20Sopenharmony_ci */ 698c2ecf20Sopenharmony_ci struct task_struct *shm_creator; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci /* 728c2ecf20Sopenharmony_ci * List by creator. task_lock(->shm_creator) required for read/write. 738c2ecf20Sopenharmony_ci * If list_empty(), then the creator is dead already. 748c2ecf20Sopenharmony_ci */ 758c2ecf20Sopenharmony_ci struct list_head shm_clist; 768c2ecf20Sopenharmony_ci struct ipc_namespace *ns; 778c2ecf20Sopenharmony_ci} __randomize_layout; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci/* shm_mode upper byte flags */ 808c2ecf20Sopenharmony_ci#define SHM_DEST 01000 /* segment will be destroyed on last detach */ 818c2ecf20Sopenharmony_ci#define SHM_LOCKED 02000 /* segment will not be swapped */ 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistruct shm_file_data { 848c2ecf20Sopenharmony_ci int id; 858c2ecf20Sopenharmony_ci struct ipc_namespace *ns; 868c2ecf20Sopenharmony_ci struct file *file; 878c2ecf20Sopenharmony_ci const struct vm_operations_struct *vm_ops; 888c2ecf20Sopenharmony_ci}; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci#define shm_file_data(file) (*((struct shm_file_data **)&(file)->private_data)) 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic const struct file_operations shm_file_operations; 938c2ecf20Sopenharmony_cistatic const struct vm_operations_struct shm_vm_ops; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci#define shm_ids(ns) ((ns)->ids[IPC_SHM_IDS]) 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci#define shm_unlock(shp) \ 988c2ecf20Sopenharmony_ci ipc_unlock(&(shp)->shm_perm) 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic int newseg(struct ipc_namespace *, struct ipc_params *); 1018c2ecf20Sopenharmony_cistatic void shm_open(struct vm_area_struct *vma); 1028c2ecf20Sopenharmony_cistatic void shm_close(struct vm_area_struct *vma); 1038c2ecf20Sopenharmony_cistatic void shm_destroy(struct ipc_namespace *ns, struct shmid_kernel *shp); 1048c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS 1058c2ecf20Sopenharmony_cistatic int sysvipc_shm_proc_show(struct seq_file *s, void *it); 1068c2ecf20Sopenharmony_ci#endif 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_civoid shm_init_ns(struct ipc_namespace *ns) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci ns->shm_ctlmax = SHMMAX; 1118c2ecf20Sopenharmony_ci ns->shm_ctlall = SHMALL; 1128c2ecf20Sopenharmony_ci ns->shm_ctlmni = SHMMNI; 1138c2ecf20Sopenharmony_ci ns->shm_rmid_forced = 0; 1148c2ecf20Sopenharmony_ci ns->shm_tot = 0; 1158c2ecf20Sopenharmony_ci ipc_init_ids(&shm_ids(ns)); 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci/* 1198c2ecf20Sopenharmony_ci * Called with shm_ids.rwsem (writer) and the shp structure locked. 1208c2ecf20Sopenharmony_ci * Only shm_ids.rwsem remains locked on exit. 1218c2ecf20Sopenharmony_ci */ 1228c2ecf20Sopenharmony_cistatic void do_shm_rmid(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci struct shmid_kernel *shp; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci shp = container_of(ipcp, struct shmid_kernel, shm_perm); 1278c2ecf20Sopenharmony_ci WARN_ON(ns != shp->ns); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci if (shp->shm_nattch) { 1308c2ecf20Sopenharmony_ci shp->shm_perm.mode |= SHM_DEST; 1318c2ecf20Sopenharmony_ci /* Do not find it any more */ 1328c2ecf20Sopenharmony_ci ipc_set_key_private(&shm_ids(ns), &shp->shm_perm); 1338c2ecf20Sopenharmony_ci shm_unlock(shp); 1348c2ecf20Sopenharmony_ci } else 1358c2ecf20Sopenharmony_ci shm_destroy(ns, shp); 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci#ifdef CONFIG_IPC_NS 1398c2ecf20Sopenharmony_civoid shm_exit_ns(struct ipc_namespace *ns) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci free_ipcs(ns, &shm_ids(ns), do_shm_rmid); 1428c2ecf20Sopenharmony_ci idr_destroy(&ns->ids[IPC_SHM_IDS].ipcs_idr); 1438c2ecf20Sopenharmony_ci rhashtable_destroy(&ns->ids[IPC_SHM_IDS].key_ht); 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci#endif 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic int __init ipc_ns_init(void) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci shm_init_ns(&init_ipc_ns); 1508c2ecf20Sopenharmony_ci return 0; 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cipure_initcall(ipc_ns_init); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_civoid __init shm_init(void) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci ipc_init_proc_interface("sysvipc/shm", 1588c2ecf20Sopenharmony_ci#if BITS_PER_LONG <= 32 1598c2ecf20Sopenharmony_ci " key shmid perms size cpid lpid nattch uid gid cuid cgid atime dtime ctime rss swap\n", 1608c2ecf20Sopenharmony_ci#else 1618c2ecf20Sopenharmony_ci " key shmid perms size cpid lpid nattch uid gid cuid cgid atime dtime ctime rss swap\n", 1628c2ecf20Sopenharmony_ci#endif 1638c2ecf20Sopenharmony_ci IPC_SHM_IDS, sysvipc_shm_proc_show); 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic inline struct shmid_kernel *shm_obtain_object(struct ipc_namespace *ns, int id) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci struct kern_ipc_perm *ipcp = ipc_obtain_object_idr(&shm_ids(ns), id); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci if (IS_ERR(ipcp)) 1718c2ecf20Sopenharmony_ci return ERR_CAST(ipcp); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci return container_of(ipcp, struct shmid_kernel, shm_perm); 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic inline struct shmid_kernel *shm_obtain_object_check(struct ipc_namespace *ns, int id) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci struct kern_ipc_perm *ipcp = ipc_obtain_object_check(&shm_ids(ns), id); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci if (IS_ERR(ipcp)) 1818c2ecf20Sopenharmony_ci return ERR_CAST(ipcp); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci return container_of(ipcp, struct shmid_kernel, shm_perm); 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci/* 1878c2ecf20Sopenharmony_ci * shm_lock_(check_) routines are called in the paths where the rwsem 1888c2ecf20Sopenharmony_ci * is not necessarily held. 1898c2ecf20Sopenharmony_ci */ 1908c2ecf20Sopenharmony_cistatic inline struct shmid_kernel *shm_lock(struct ipc_namespace *ns, int id) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci struct kern_ipc_perm *ipcp; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci rcu_read_lock(); 1958c2ecf20Sopenharmony_ci ipcp = ipc_obtain_object_idr(&shm_ids(ns), id); 1968c2ecf20Sopenharmony_ci if (IS_ERR(ipcp)) 1978c2ecf20Sopenharmony_ci goto err; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci ipc_lock_object(ipcp); 2008c2ecf20Sopenharmony_ci /* 2018c2ecf20Sopenharmony_ci * ipc_rmid() may have already freed the ID while ipc_lock_object() 2028c2ecf20Sopenharmony_ci * was spinning: here verify that the structure is still valid. 2038c2ecf20Sopenharmony_ci * Upon races with RMID, return -EIDRM, thus indicating that 2048c2ecf20Sopenharmony_ci * the ID points to a removed identifier. 2058c2ecf20Sopenharmony_ci */ 2068c2ecf20Sopenharmony_ci if (ipc_valid_object(ipcp)) { 2078c2ecf20Sopenharmony_ci /* return a locked ipc object upon success */ 2088c2ecf20Sopenharmony_ci return container_of(ipcp, struct shmid_kernel, shm_perm); 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci ipc_unlock_object(ipcp); 2128c2ecf20Sopenharmony_ci ipcp = ERR_PTR(-EIDRM); 2138c2ecf20Sopenharmony_cierr: 2148c2ecf20Sopenharmony_ci rcu_read_unlock(); 2158c2ecf20Sopenharmony_ci /* 2168c2ecf20Sopenharmony_ci * Callers of shm_lock() must validate the status of the returned ipc 2178c2ecf20Sopenharmony_ci * object pointer and error out as appropriate. 2188c2ecf20Sopenharmony_ci */ 2198c2ecf20Sopenharmony_ci return ERR_CAST(ipcp); 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic inline void shm_lock_by_ptr(struct shmid_kernel *ipcp) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci rcu_read_lock(); 2258c2ecf20Sopenharmony_ci ipc_lock_object(&ipcp->shm_perm); 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistatic void shm_rcu_free(struct rcu_head *head) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci struct kern_ipc_perm *ptr = container_of(head, struct kern_ipc_perm, 2318c2ecf20Sopenharmony_ci rcu); 2328c2ecf20Sopenharmony_ci struct shmid_kernel *shp = container_of(ptr, struct shmid_kernel, 2338c2ecf20Sopenharmony_ci shm_perm); 2348c2ecf20Sopenharmony_ci security_shm_free(&shp->shm_perm); 2358c2ecf20Sopenharmony_ci kvfree(shp); 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci/* 2398c2ecf20Sopenharmony_ci * It has to be called with shp locked. 2408c2ecf20Sopenharmony_ci * It must be called before ipc_rmid() 2418c2ecf20Sopenharmony_ci */ 2428c2ecf20Sopenharmony_cistatic inline void shm_clist_rm(struct shmid_kernel *shp) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci struct task_struct *creator; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci /* ensure that shm_creator does not disappear */ 2478c2ecf20Sopenharmony_ci rcu_read_lock(); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci /* 2508c2ecf20Sopenharmony_ci * A concurrent exit_shm may do a list_del_init() as well. 2518c2ecf20Sopenharmony_ci * Just do nothing if exit_shm already did the work 2528c2ecf20Sopenharmony_ci */ 2538c2ecf20Sopenharmony_ci if (!list_empty(&shp->shm_clist)) { 2548c2ecf20Sopenharmony_ci /* 2558c2ecf20Sopenharmony_ci * shp->shm_creator is guaranteed to be valid *only* 2568c2ecf20Sopenharmony_ci * if shp->shm_clist is not empty. 2578c2ecf20Sopenharmony_ci */ 2588c2ecf20Sopenharmony_ci creator = shp->shm_creator; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci task_lock(creator); 2618c2ecf20Sopenharmony_ci /* 2628c2ecf20Sopenharmony_ci * list_del_init() is a nop if the entry was already removed 2638c2ecf20Sopenharmony_ci * from the list. 2648c2ecf20Sopenharmony_ci */ 2658c2ecf20Sopenharmony_ci list_del_init(&shp->shm_clist); 2668c2ecf20Sopenharmony_ci task_unlock(creator); 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci rcu_read_unlock(); 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic inline void shm_rmid(struct shmid_kernel *s) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci shm_clist_rm(s); 2748c2ecf20Sopenharmony_ci ipc_rmid(&shm_ids(s->ns), &s->shm_perm); 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_cistatic int __shm_open(struct vm_area_struct *vma) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci struct file *file = vma->vm_file; 2818c2ecf20Sopenharmony_ci struct shm_file_data *sfd = shm_file_data(file); 2828c2ecf20Sopenharmony_ci struct shmid_kernel *shp; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci shp = shm_lock(sfd->ns, sfd->id); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci if (IS_ERR(shp)) 2878c2ecf20Sopenharmony_ci return PTR_ERR(shp); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci if (shp->shm_file != sfd->file) { 2908c2ecf20Sopenharmony_ci /* ID was reused */ 2918c2ecf20Sopenharmony_ci shm_unlock(shp); 2928c2ecf20Sopenharmony_ci return -EINVAL; 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci shp->shm_atim = ktime_get_real_seconds(); 2968c2ecf20Sopenharmony_ci ipc_update_pid(&shp->shm_lprid, task_tgid(current)); 2978c2ecf20Sopenharmony_ci shp->shm_nattch++; 2988c2ecf20Sopenharmony_ci shm_unlock(shp); 2998c2ecf20Sopenharmony_ci return 0; 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci/* This is called by fork, once for every shm attach. */ 3038c2ecf20Sopenharmony_cistatic void shm_open(struct vm_area_struct *vma) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci int err = __shm_open(vma); 3068c2ecf20Sopenharmony_ci /* 3078c2ecf20Sopenharmony_ci * We raced in the idr lookup or with shm_destroy(). 3088c2ecf20Sopenharmony_ci * Either way, the ID is busted. 3098c2ecf20Sopenharmony_ci */ 3108c2ecf20Sopenharmony_ci WARN_ON_ONCE(err); 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci/* 3148c2ecf20Sopenharmony_ci * shm_destroy - free the struct shmid_kernel 3158c2ecf20Sopenharmony_ci * 3168c2ecf20Sopenharmony_ci * @ns: namespace 3178c2ecf20Sopenharmony_ci * @shp: struct to free 3188c2ecf20Sopenharmony_ci * 3198c2ecf20Sopenharmony_ci * It has to be called with shp and shm_ids.rwsem (writer) locked, 3208c2ecf20Sopenharmony_ci * but returns with shp unlocked and freed. 3218c2ecf20Sopenharmony_ci */ 3228c2ecf20Sopenharmony_cistatic void shm_destroy(struct ipc_namespace *ns, struct shmid_kernel *shp) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci struct file *shm_file; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci shm_file = shp->shm_file; 3278c2ecf20Sopenharmony_ci shp->shm_file = NULL; 3288c2ecf20Sopenharmony_ci ns->shm_tot -= (shp->shm_segsz + PAGE_SIZE - 1) >> PAGE_SHIFT; 3298c2ecf20Sopenharmony_ci shm_rmid(shp); 3308c2ecf20Sopenharmony_ci shm_unlock(shp); 3318c2ecf20Sopenharmony_ci if (!is_file_hugepages(shm_file)) 3328c2ecf20Sopenharmony_ci shmem_lock(shm_file, 0, shp->mlock_user); 3338c2ecf20Sopenharmony_ci else if (shp->mlock_user) 3348c2ecf20Sopenharmony_ci user_shm_unlock(i_size_read(file_inode(shm_file)), 3358c2ecf20Sopenharmony_ci shp->mlock_user); 3368c2ecf20Sopenharmony_ci fput(shm_file); 3378c2ecf20Sopenharmony_ci ipc_update_pid(&shp->shm_cprid, NULL); 3388c2ecf20Sopenharmony_ci ipc_update_pid(&shp->shm_lprid, NULL); 3398c2ecf20Sopenharmony_ci ipc_rcu_putref(&shp->shm_perm, shm_rcu_free); 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci/* 3438c2ecf20Sopenharmony_ci * shm_may_destroy - identifies whether shm segment should be destroyed now 3448c2ecf20Sopenharmony_ci * 3458c2ecf20Sopenharmony_ci * Returns true if and only if there are no active users of the segment and 3468c2ecf20Sopenharmony_ci * one of the following is true: 3478c2ecf20Sopenharmony_ci * 3488c2ecf20Sopenharmony_ci * 1) shmctl(id, IPC_RMID, NULL) was called for this shp 3498c2ecf20Sopenharmony_ci * 3508c2ecf20Sopenharmony_ci * 2) sysctl kernel.shm_rmid_forced is set to 1. 3518c2ecf20Sopenharmony_ci */ 3528c2ecf20Sopenharmony_cistatic bool shm_may_destroy(struct shmid_kernel *shp) 3538c2ecf20Sopenharmony_ci{ 3548c2ecf20Sopenharmony_ci return (shp->shm_nattch == 0) && 3558c2ecf20Sopenharmony_ci (shp->ns->shm_rmid_forced || 3568c2ecf20Sopenharmony_ci (shp->shm_perm.mode & SHM_DEST)); 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci/* 3608c2ecf20Sopenharmony_ci * remove the attach descriptor vma. 3618c2ecf20Sopenharmony_ci * free memory for segment if it is marked destroyed. 3628c2ecf20Sopenharmony_ci * The descriptor has already been removed from the current->mm->mmap list 3638c2ecf20Sopenharmony_ci * and will later be kfree()d. 3648c2ecf20Sopenharmony_ci */ 3658c2ecf20Sopenharmony_cistatic void shm_close(struct vm_area_struct *vma) 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci struct file *file = vma->vm_file; 3688c2ecf20Sopenharmony_ci struct shm_file_data *sfd = shm_file_data(file); 3698c2ecf20Sopenharmony_ci struct shmid_kernel *shp; 3708c2ecf20Sopenharmony_ci struct ipc_namespace *ns = sfd->ns; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci down_write(&shm_ids(ns).rwsem); 3738c2ecf20Sopenharmony_ci /* remove from the list of attaches of the shm segment */ 3748c2ecf20Sopenharmony_ci shp = shm_lock(ns, sfd->id); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci /* 3778c2ecf20Sopenharmony_ci * We raced in the idr lookup or with shm_destroy(). 3788c2ecf20Sopenharmony_ci * Either way, the ID is busted. 3798c2ecf20Sopenharmony_ci */ 3808c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(IS_ERR(shp))) 3818c2ecf20Sopenharmony_ci goto done; /* no-op */ 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci ipc_update_pid(&shp->shm_lprid, task_tgid(current)); 3848c2ecf20Sopenharmony_ci shp->shm_dtim = ktime_get_real_seconds(); 3858c2ecf20Sopenharmony_ci shp->shm_nattch--; 3868c2ecf20Sopenharmony_ci if (shm_may_destroy(shp)) 3878c2ecf20Sopenharmony_ci shm_destroy(ns, shp); 3888c2ecf20Sopenharmony_ci else 3898c2ecf20Sopenharmony_ci shm_unlock(shp); 3908c2ecf20Sopenharmony_cidone: 3918c2ecf20Sopenharmony_ci up_write(&shm_ids(ns).rwsem); 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci/* Called with ns->shm_ids(ns).rwsem locked */ 3958c2ecf20Sopenharmony_cistatic int shm_try_destroy_orphaned(int id, void *p, void *data) 3968c2ecf20Sopenharmony_ci{ 3978c2ecf20Sopenharmony_ci struct ipc_namespace *ns = data; 3988c2ecf20Sopenharmony_ci struct kern_ipc_perm *ipcp = p; 3998c2ecf20Sopenharmony_ci struct shmid_kernel *shp = container_of(ipcp, struct shmid_kernel, shm_perm); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci /* 4028c2ecf20Sopenharmony_ci * We want to destroy segments without users and with already 4038c2ecf20Sopenharmony_ci * exit'ed originating process. 4048c2ecf20Sopenharmony_ci * 4058c2ecf20Sopenharmony_ci * As shp->* are changed under rwsem, it's safe to skip shp locking. 4068c2ecf20Sopenharmony_ci */ 4078c2ecf20Sopenharmony_ci if (!list_empty(&shp->shm_clist)) 4088c2ecf20Sopenharmony_ci return 0; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci if (shm_may_destroy(shp)) { 4118c2ecf20Sopenharmony_ci shm_lock_by_ptr(shp); 4128c2ecf20Sopenharmony_ci shm_destroy(ns, shp); 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci return 0; 4158c2ecf20Sopenharmony_ci} 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_civoid shm_destroy_orphaned(struct ipc_namespace *ns) 4188c2ecf20Sopenharmony_ci{ 4198c2ecf20Sopenharmony_ci down_write(&shm_ids(ns).rwsem); 4208c2ecf20Sopenharmony_ci if (shm_ids(ns).in_use) 4218c2ecf20Sopenharmony_ci idr_for_each(&shm_ids(ns).ipcs_idr, &shm_try_destroy_orphaned, ns); 4228c2ecf20Sopenharmony_ci up_write(&shm_ids(ns).rwsem); 4238c2ecf20Sopenharmony_ci} 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci/* Locking assumes this will only be called with task == current */ 4268c2ecf20Sopenharmony_civoid exit_shm(struct task_struct *task) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci for (;;) { 4298c2ecf20Sopenharmony_ci struct shmid_kernel *shp; 4308c2ecf20Sopenharmony_ci struct ipc_namespace *ns; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci task_lock(task); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci if (list_empty(&task->sysvshm.shm_clist)) { 4358c2ecf20Sopenharmony_ci task_unlock(task); 4368c2ecf20Sopenharmony_ci break; 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci shp = list_first_entry(&task->sysvshm.shm_clist, struct shmid_kernel, 4408c2ecf20Sopenharmony_ci shm_clist); 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci /* 4438c2ecf20Sopenharmony_ci * 1) Get pointer to the ipc namespace. It is worth to say 4448c2ecf20Sopenharmony_ci * that this pointer is guaranteed to be valid because 4458c2ecf20Sopenharmony_ci * shp lifetime is always shorter than namespace lifetime 4468c2ecf20Sopenharmony_ci * in which shp lives. 4478c2ecf20Sopenharmony_ci * We taken task_lock it means that shp won't be freed. 4488c2ecf20Sopenharmony_ci */ 4498c2ecf20Sopenharmony_ci ns = shp->ns; 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci /* 4528c2ecf20Sopenharmony_ci * 2) If kernel.shm_rmid_forced is not set then only keep track of 4538c2ecf20Sopenharmony_ci * which shmids are orphaned, so that a later set of the sysctl 4548c2ecf20Sopenharmony_ci * can clean them up. 4558c2ecf20Sopenharmony_ci */ 4568c2ecf20Sopenharmony_ci if (!ns->shm_rmid_forced) 4578c2ecf20Sopenharmony_ci goto unlink_continue; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci /* 4608c2ecf20Sopenharmony_ci * 3) get a reference to the namespace. 4618c2ecf20Sopenharmony_ci * The refcount could be already 0. If it is 0, then 4628c2ecf20Sopenharmony_ci * the shm objects will be free by free_ipc_work(). 4638c2ecf20Sopenharmony_ci */ 4648c2ecf20Sopenharmony_ci ns = get_ipc_ns_not_zero(ns); 4658c2ecf20Sopenharmony_ci if (!ns) { 4668c2ecf20Sopenharmony_ciunlink_continue: 4678c2ecf20Sopenharmony_ci list_del_init(&shp->shm_clist); 4688c2ecf20Sopenharmony_ci task_unlock(task); 4698c2ecf20Sopenharmony_ci continue; 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci /* 4738c2ecf20Sopenharmony_ci * 4) get a reference to shp. 4748c2ecf20Sopenharmony_ci * This cannot fail: shm_clist_rm() is called before 4758c2ecf20Sopenharmony_ci * ipc_rmid(), thus the refcount cannot be 0. 4768c2ecf20Sopenharmony_ci */ 4778c2ecf20Sopenharmony_ci WARN_ON(!ipc_rcu_getref(&shp->shm_perm)); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci /* 4808c2ecf20Sopenharmony_ci * 5) unlink the shm segment from the list of segments 4818c2ecf20Sopenharmony_ci * created by current. 4828c2ecf20Sopenharmony_ci * This must be done last. After unlinking, 4838c2ecf20Sopenharmony_ci * only the refcounts obtained above prevent IPC_RMID 4848c2ecf20Sopenharmony_ci * from destroying the segment or the namespace. 4858c2ecf20Sopenharmony_ci */ 4868c2ecf20Sopenharmony_ci list_del_init(&shp->shm_clist); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci task_unlock(task); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci /* 4918c2ecf20Sopenharmony_ci * 6) we have all references 4928c2ecf20Sopenharmony_ci * Thus lock & if needed destroy shp. 4938c2ecf20Sopenharmony_ci */ 4948c2ecf20Sopenharmony_ci down_write(&shm_ids(ns).rwsem); 4958c2ecf20Sopenharmony_ci shm_lock_by_ptr(shp); 4968c2ecf20Sopenharmony_ci /* 4978c2ecf20Sopenharmony_ci * rcu_read_lock was implicitly taken in shm_lock_by_ptr, it's 4988c2ecf20Sopenharmony_ci * safe to call ipc_rcu_putref here 4998c2ecf20Sopenharmony_ci */ 5008c2ecf20Sopenharmony_ci ipc_rcu_putref(&shp->shm_perm, shm_rcu_free); 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci if (ipc_valid_object(&shp->shm_perm)) { 5038c2ecf20Sopenharmony_ci if (shm_may_destroy(shp)) 5048c2ecf20Sopenharmony_ci shm_destroy(ns, shp); 5058c2ecf20Sopenharmony_ci else 5068c2ecf20Sopenharmony_ci shm_unlock(shp); 5078c2ecf20Sopenharmony_ci } else { 5088c2ecf20Sopenharmony_ci /* 5098c2ecf20Sopenharmony_ci * Someone else deleted the shp from namespace 5108c2ecf20Sopenharmony_ci * idr/kht while we have waited. 5118c2ecf20Sopenharmony_ci * Just unlock and continue. 5128c2ecf20Sopenharmony_ci */ 5138c2ecf20Sopenharmony_ci shm_unlock(shp); 5148c2ecf20Sopenharmony_ci } 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci up_write(&shm_ids(ns).rwsem); 5178c2ecf20Sopenharmony_ci put_ipc_ns(ns); /* paired with get_ipc_ns_not_zero */ 5188c2ecf20Sopenharmony_ci } 5198c2ecf20Sopenharmony_ci} 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_cistatic vm_fault_t shm_fault(struct vm_fault *vmf) 5228c2ecf20Sopenharmony_ci{ 5238c2ecf20Sopenharmony_ci struct file *file = vmf->vma->vm_file; 5248c2ecf20Sopenharmony_ci struct shm_file_data *sfd = shm_file_data(file); 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci return sfd->vm_ops->fault(vmf); 5278c2ecf20Sopenharmony_ci} 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_cistatic int shm_split(struct vm_area_struct *vma, unsigned long addr) 5308c2ecf20Sopenharmony_ci{ 5318c2ecf20Sopenharmony_ci struct file *file = vma->vm_file; 5328c2ecf20Sopenharmony_ci struct shm_file_data *sfd = shm_file_data(file); 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci if (sfd->vm_ops->split) 5358c2ecf20Sopenharmony_ci return sfd->vm_ops->split(vma, addr); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci return 0; 5388c2ecf20Sopenharmony_ci} 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_cistatic unsigned long shm_pagesize(struct vm_area_struct *vma) 5418c2ecf20Sopenharmony_ci{ 5428c2ecf20Sopenharmony_ci struct file *file = vma->vm_file; 5438c2ecf20Sopenharmony_ci struct shm_file_data *sfd = shm_file_data(file); 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci if (sfd->vm_ops->pagesize) 5468c2ecf20Sopenharmony_ci return sfd->vm_ops->pagesize(vma); 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci return PAGE_SIZE; 5498c2ecf20Sopenharmony_ci} 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci#ifdef CONFIG_NUMA 5528c2ecf20Sopenharmony_cistatic int shm_set_policy(struct vm_area_struct *vma, struct mempolicy *new) 5538c2ecf20Sopenharmony_ci{ 5548c2ecf20Sopenharmony_ci struct file *file = vma->vm_file; 5558c2ecf20Sopenharmony_ci struct shm_file_data *sfd = shm_file_data(file); 5568c2ecf20Sopenharmony_ci int err = 0; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci if (sfd->vm_ops->set_policy) 5598c2ecf20Sopenharmony_ci err = sfd->vm_ops->set_policy(vma, new); 5608c2ecf20Sopenharmony_ci return err; 5618c2ecf20Sopenharmony_ci} 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_cistatic struct mempolicy *shm_get_policy(struct vm_area_struct *vma, 5648c2ecf20Sopenharmony_ci unsigned long addr) 5658c2ecf20Sopenharmony_ci{ 5668c2ecf20Sopenharmony_ci struct file *file = vma->vm_file; 5678c2ecf20Sopenharmony_ci struct shm_file_data *sfd = shm_file_data(file); 5688c2ecf20Sopenharmony_ci struct mempolicy *pol = NULL; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci if (sfd->vm_ops->get_policy) 5718c2ecf20Sopenharmony_ci pol = sfd->vm_ops->get_policy(vma, addr); 5728c2ecf20Sopenharmony_ci else if (vma->vm_policy) 5738c2ecf20Sopenharmony_ci pol = vma->vm_policy; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci return pol; 5768c2ecf20Sopenharmony_ci} 5778c2ecf20Sopenharmony_ci#endif 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_cistatic int shm_mmap(struct file *file, struct vm_area_struct *vma) 5808c2ecf20Sopenharmony_ci{ 5818c2ecf20Sopenharmony_ci struct shm_file_data *sfd = shm_file_data(file); 5828c2ecf20Sopenharmony_ci int ret; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci /* 5858c2ecf20Sopenharmony_ci * In case of remap_file_pages() emulation, the file can represent an 5868c2ecf20Sopenharmony_ci * IPC ID that was removed, and possibly even reused by another shm 5878c2ecf20Sopenharmony_ci * segment already. Propagate this case as an error to caller. 5888c2ecf20Sopenharmony_ci */ 5898c2ecf20Sopenharmony_ci ret = __shm_open(vma); 5908c2ecf20Sopenharmony_ci if (ret) 5918c2ecf20Sopenharmony_ci return ret; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci ret = call_mmap(sfd->file, vma); 5948c2ecf20Sopenharmony_ci if (ret) { 5958c2ecf20Sopenharmony_ci shm_close(vma); 5968c2ecf20Sopenharmony_ci return ret; 5978c2ecf20Sopenharmony_ci } 5988c2ecf20Sopenharmony_ci sfd->vm_ops = vma->vm_ops; 5998c2ecf20Sopenharmony_ci#ifdef CONFIG_MMU 6008c2ecf20Sopenharmony_ci WARN_ON(!sfd->vm_ops->fault); 6018c2ecf20Sopenharmony_ci#endif 6028c2ecf20Sopenharmony_ci vma->vm_ops = &shm_vm_ops; 6038c2ecf20Sopenharmony_ci return 0; 6048c2ecf20Sopenharmony_ci} 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_cistatic int shm_release(struct inode *ino, struct file *file) 6078c2ecf20Sopenharmony_ci{ 6088c2ecf20Sopenharmony_ci struct shm_file_data *sfd = shm_file_data(file); 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci put_ipc_ns(sfd->ns); 6118c2ecf20Sopenharmony_ci fput(sfd->file); 6128c2ecf20Sopenharmony_ci shm_file_data(file) = NULL; 6138c2ecf20Sopenharmony_ci kfree(sfd); 6148c2ecf20Sopenharmony_ci return 0; 6158c2ecf20Sopenharmony_ci} 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_cistatic int shm_fsync(struct file *file, loff_t start, loff_t end, int datasync) 6188c2ecf20Sopenharmony_ci{ 6198c2ecf20Sopenharmony_ci struct shm_file_data *sfd = shm_file_data(file); 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci if (!sfd->file->f_op->fsync) 6228c2ecf20Sopenharmony_ci return -EINVAL; 6238c2ecf20Sopenharmony_ci return sfd->file->f_op->fsync(sfd->file, start, end, datasync); 6248c2ecf20Sopenharmony_ci} 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_cistatic long shm_fallocate(struct file *file, int mode, loff_t offset, 6278c2ecf20Sopenharmony_ci loff_t len) 6288c2ecf20Sopenharmony_ci{ 6298c2ecf20Sopenharmony_ci struct shm_file_data *sfd = shm_file_data(file); 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci if (!sfd->file->f_op->fallocate) 6328c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 6338c2ecf20Sopenharmony_ci return sfd->file->f_op->fallocate(file, mode, offset, len); 6348c2ecf20Sopenharmony_ci} 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_cistatic unsigned long shm_get_unmapped_area(struct file *file, 6378c2ecf20Sopenharmony_ci unsigned long addr, unsigned long len, unsigned long pgoff, 6388c2ecf20Sopenharmony_ci unsigned long flags) 6398c2ecf20Sopenharmony_ci{ 6408c2ecf20Sopenharmony_ci struct shm_file_data *sfd = shm_file_data(file); 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci return sfd->file->f_op->get_unmapped_area(sfd->file, addr, len, 6438c2ecf20Sopenharmony_ci pgoff, flags); 6448c2ecf20Sopenharmony_ci} 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_cistatic const struct file_operations shm_file_operations = { 6478c2ecf20Sopenharmony_ci .mmap = shm_mmap, 6488c2ecf20Sopenharmony_ci .fsync = shm_fsync, 6498c2ecf20Sopenharmony_ci .release = shm_release, 6508c2ecf20Sopenharmony_ci .get_unmapped_area = shm_get_unmapped_area, 6518c2ecf20Sopenharmony_ci .llseek = noop_llseek, 6528c2ecf20Sopenharmony_ci .fallocate = shm_fallocate, 6538c2ecf20Sopenharmony_ci}; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci/* 6568c2ecf20Sopenharmony_ci * shm_file_operations_huge is now identical to shm_file_operations, 6578c2ecf20Sopenharmony_ci * but we keep it distinct for the sake of is_file_shm_hugepages(). 6588c2ecf20Sopenharmony_ci */ 6598c2ecf20Sopenharmony_cistatic const struct file_operations shm_file_operations_huge = { 6608c2ecf20Sopenharmony_ci .mmap = shm_mmap, 6618c2ecf20Sopenharmony_ci .fsync = shm_fsync, 6628c2ecf20Sopenharmony_ci .release = shm_release, 6638c2ecf20Sopenharmony_ci .get_unmapped_area = shm_get_unmapped_area, 6648c2ecf20Sopenharmony_ci .llseek = noop_llseek, 6658c2ecf20Sopenharmony_ci .fallocate = shm_fallocate, 6668c2ecf20Sopenharmony_ci}; 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_cibool is_file_shm_hugepages(struct file *file) 6698c2ecf20Sopenharmony_ci{ 6708c2ecf20Sopenharmony_ci return file->f_op == &shm_file_operations_huge; 6718c2ecf20Sopenharmony_ci} 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_cistatic const struct vm_operations_struct shm_vm_ops = { 6748c2ecf20Sopenharmony_ci .open = shm_open, /* callback for a new vm-area open */ 6758c2ecf20Sopenharmony_ci .close = shm_close, /* callback for when the vm-area is released */ 6768c2ecf20Sopenharmony_ci .fault = shm_fault, 6778c2ecf20Sopenharmony_ci .split = shm_split, 6788c2ecf20Sopenharmony_ci .pagesize = shm_pagesize, 6798c2ecf20Sopenharmony_ci#if defined(CONFIG_NUMA) 6808c2ecf20Sopenharmony_ci .set_policy = shm_set_policy, 6818c2ecf20Sopenharmony_ci .get_policy = shm_get_policy, 6828c2ecf20Sopenharmony_ci#endif 6838c2ecf20Sopenharmony_ci}; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci/** 6868c2ecf20Sopenharmony_ci * newseg - Create a new shared memory segment 6878c2ecf20Sopenharmony_ci * @ns: namespace 6888c2ecf20Sopenharmony_ci * @params: ptr to the structure that contains key, size and shmflg 6898c2ecf20Sopenharmony_ci * 6908c2ecf20Sopenharmony_ci * Called with shm_ids.rwsem held as a writer. 6918c2ecf20Sopenharmony_ci */ 6928c2ecf20Sopenharmony_cistatic int newseg(struct ipc_namespace *ns, struct ipc_params *params) 6938c2ecf20Sopenharmony_ci{ 6948c2ecf20Sopenharmony_ci key_t key = params->key; 6958c2ecf20Sopenharmony_ci int shmflg = params->flg; 6968c2ecf20Sopenharmony_ci size_t size = params->u.size; 6978c2ecf20Sopenharmony_ci int error; 6988c2ecf20Sopenharmony_ci struct shmid_kernel *shp; 6998c2ecf20Sopenharmony_ci size_t numpages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT; 7008c2ecf20Sopenharmony_ci struct file *file; 7018c2ecf20Sopenharmony_ci char name[13]; 7028c2ecf20Sopenharmony_ci vm_flags_t acctflag = 0; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci if (size < SHMMIN || size > ns->shm_ctlmax) 7058c2ecf20Sopenharmony_ci return -EINVAL; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci if (numpages << PAGE_SHIFT < size) 7088c2ecf20Sopenharmony_ci return -ENOSPC; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci if (ns->shm_tot + numpages < ns->shm_tot || 7118c2ecf20Sopenharmony_ci ns->shm_tot + numpages > ns->shm_ctlall) 7128c2ecf20Sopenharmony_ci return -ENOSPC; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci shp = kvmalloc(sizeof(*shp), GFP_KERNEL_ACCOUNT); 7158c2ecf20Sopenharmony_ci if (unlikely(!shp)) 7168c2ecf20Sopenharmony_ci return -ENOMEM; 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci shp->shm_perm.key = key; 7198c2ecf20Sopenharmony_ci shp->shm_perm.mode = (shmflg & S_IRWXUGO); 7208c2ecf20Sopenharmony_ci shp->mlock_user = NULL; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci shp->shm_perm.security = NULL; 7238c2ecf20Sopenharmony_ci error = security_shm_alloc(&shp->shm_perm); 7248c2ecf20Sopenharmony_ci if (error) { 7258c2ecf20Sopenharmony_ci kvfree(shp); 7268c2ecf20Sopenharmony_ci return error; 7278c2ecf20Sopenharmony_ci } 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci sprintf(name, "SYSV%08x", key); 7308c2ecf20Sopenharmony_ci if (shmflg & SHM_HUGETLB) { 7318c2ecf20Sopenharmony_ci struct hstate *hs; 7328c2ecf20Sopenharmony_ci size_t hugesize; 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci hs = hstate_sizelog((shmflg >> SHM_HUGE_SHIFT) & SHM_HUGE_MASK); 7358c2ecf20Sopenharmony_ci if (!hs) { 7368c2ecf20Sopenharmony_ci error = -EINVAL; 7378c2ecf20Sopenharmony_ci goto no_file; 7388c2ecf20Sopenharmony_ci } 7398c2ecf20Sopenharmony_ci hugesize = ALIGN(size, huge_page_size(hs)); 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci /* hugetlb_file_setup applies strict accounting */ 7428c2ecf20Sopenharmony_ci if (shmflg & SHM_NORESERVE) 7438c2ecf20Sopenharmony_ci acctflag = VM_NORESERVE; 7448c2ecf20Sopenharmony_ci file = hugetlb_file_setup(name, hugesize, acctflag, 7458c2ecf20Sopenharmony_ci &shp->mlock_user, HUGETLB_SHMFS_INODE, 7468c2ecf20Sopenharmony_ci (shmflg >> SHM_HUGE_SHIFT) & SHM_HUGE_MASK); 7478c2ecf20Sopenharmony_ci } else { 7488c2ecf20Sopenharmony_ci /* 7498c2ecf20Sopenharmony_ci * Do not allow no accounting for OVERCOMMIT_NEVER, even 7508c2ecf20Sopenharmony_ci * if it's asked for. 7518c2ecf20Sopenharmony_ci */ 7528c2ecf20Sopenharmony_ci if ((shmflg & SHM_NORESERVE) && 7538c2ecf20Sopenharmony_ci sysctl_overcommit_memory != OVERCOMMIT_NEVER) 7548c2ecf20Sopenharmony_ci acctflag = VM_NORESERVE; 7558c2ecf20Sopenharmony_ci file = shmem_kernel_file_setup(name, size, acctflag); 7568c2ecf20Sopenharmony_ci } 7578c2ecf20Sopenharmony_ci error = PTR_ERR(file); 7588c2ecf20Sopenharmony_ci if (IS_ERR(file)) 7598c2ecf20Sopenharmony_ci goto no_file; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci shp->shm_cprid = get_pid(task_tgid(current)); 7628c2ecf20Sopenharmony_ci shp->shm_lprid = NULL; 7638c2ecf20Sopenharmony_ci shp->shm_atim = shp->shm_dtim = 0; 7648c2ecf20Sopenharmony_ci shp->shm_ctim = ktime_get_real_seconds(); 7658c2ecf20Sopenharmony_ci shp->shm_segsz = size; 7668c2ecf20Sopenharmony_ci shp->shm_nattch = 0; 7678c2ecf20Sopenharmony_ci shp->shm_file = file; 7688c2ecf20Sopenharmony_ci shp->shm_creator = current; 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci /* ipc_addid() locks shp upon success. */ 7718c2ecf20Sopenharmony_ci error = ipc_addid(&shm_ids(ns), &shp->shm_perm, ns->shm_ctlmni); 7728c2ecf20Sopenharmony_ci if (error < 0) 7738c2ecf20Sopenharmony_ci goto no_id; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci shp->ns = ns; 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci task_lock(current); 7788c2ecf20Sopenharmony_ci list_add(&shp->shm_clist, ¤t->sysvshm.shm_clist); 7798c2ecf20Sopenharmony_ci task_unlock(current); 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci /* 7828c2ecf20Sopenharmony_ci * shmid gets reported as "inode#" in /proc/pid/maps. 7838c2ecf20Sopenharmony_ci * proc-ps tools use this. Changing this will break them. 7848c2ecf20Sopenharmony_ci */ 7858c2ecf20Sopenharmony_ci file_inode(file)->i_ino = shp->shm_perm.id; 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci ns->shm_tot += numpages; 7888c2ecf20Sopenharmony_ci error = shp->shm_perm.id; 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci ipc_unlock_object(&shp->shm_perm); 7918c2ecf20Sopenharmony_ci rcu_read_unlock(); 7928c2ecf20Sopenharmony_ci return error; 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_cino_id: 7958c2ecf20Sopenharmony_ci ipc_update_pid(&shp->shm_cprid, NULL); 7968c2ecf20Sopenharmony_ci ipc_update_pid(&shp->shm_lprid, NULL); 7978c2ecf20Sopenharmony_ci if (is_file_hugepages(file) && shp->mlock_user) 7988c2ecf20Sopenharmony_ci user_shm_unlock(size, shp->mlock_user); 7998c2ecf20Sopenharmony_ci fput(file); 8008c2ecf20Sopenharmony_ci ipc_rcu_putref(&shp->shm_perm, shm_rcu_free); 8018c2ecf20Sopenharmony_ci return error; 8028c2ecf20Sopenharmony_cino_file: 8038c2ecf20Sopenharmony_ci call_rcu(&shp->shm_perm.rcu, shm_rcu_free); 8048c2ecf20Sopenharmony_ci return error; 8058c2ecf20Sopenharmony_ci} 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci/* 8088c2ecf20Sopenharmony_ci * Called with shm_ids.rwsem and ipcp locked. 8098c2ecf20Sopenharmony_ci */ 8108c2ecf20Sopenharmony_cistatic int shm_more_checks(struct kern_ipc_perm *ipcp, struct ipc_params *params) 8118c2ecf20Sopenharmony_ci{ 8128c2ecf20Sopenharmony_ci struct shmid_kernel *shp; 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci shp = container_of(ipcp, struct shmid_kernel, shm_perm); 8158c2ecf20Sopenharmony_ci if (shp->shm_segsz < params->u.size) 8168c2ecf20Sopenharmony_ci return -EINVAL; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci return 0; 8198c2ecf20Sopenharmony_ci} 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_cilong ksys_shmget(key_t key, size_t size, int shmflg) 8228c2ecf20Sopenharmony_ci{ 8238c2ecf20Sopenharmony_ci struct ipc_namespace *ns; 8248c2ecf20Sopenharmony_ci static const struct ipc_ops shm_ops = { 8258c2ecf20Sopenharmony_ci .getnew = newseg, 8268c2ecf20Sopenharmony_ci .associate = security_shm_associate, 8278c2ecf20Sopenharmony_ci .more_checks = shm_more_checks, 8288c2ecf20Sopenharmony_ci }; 8298c2ecf20Sopenharmony_ci struct ipc_params shm_params; 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci ns = current->nsproxy->ipc_ns; 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci shm_params.key = key; 8348c2ecf20Sopenharmony_ci shm_params.flg = shmflg; 8358c2ecf20Sopenharmony_ci shm_params.u.size = size; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci return ipcget(ns, &shm_ids(ns), &shm_ops, &shm_params); 8388c2ecf20Sopenharmony_ci} 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ciSYSCALL_DEFINE3(shmget, key_t, key, size_t, size, int, shmflg) 8418c2ecf20Sopenharmony_ci{ 8428c2ecf20Sopenharmony_ci return ksys_shmget(key, size, shmflg); 8438c2ecf20Sopenharmony_ci} 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_cistatic inline unsigned long copy_shmid_to_user(void __user *buf, struct shmid64_ds *in, int version) 8468c2ecf20Sopenharmony_ci{ 8478c2ecf20Sopenharmony_ci switch (version) { 8488c2ecf20Sopenharmony_ci case IPC_64: 8498c2ecf20Sopenharmony_ci return copy_to_user(buf, in, sizeof(*in)); 8508c2ecf20Sopenharmony_ci case IPC_OLD: 8518c2ecf20Sopenharmony_ci { 8528c2ecf20Sopenharmony_ci struct shmid_ds out; 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci memset(&out, 0, sizeof(out)); 8558c2ecf20Sopenharmony_ci ipc64_perm_to_ipc_perm(&in->shm_perm, &out.shm_perm); 8568c2ecf20Sopenharmony_ci out.shm_segsz = in->shm_segsz; 8578c2ecf20Sopenharmony_ci out.shm_atime = in->shm_atime; 8588c2ecf20Sopenharmony_ci out.shm_dtime = in->shm_dtime; 8598c2ecf20Sopenharmony_ci out.shm_ctime = in->shm_ctime; 8608c2ecf20Sopenharmony_ci out.shm_cpid = in->shm_cpid; 8618c2ecf20Sopenharmony_ci out.shm_lpid = in->shm_lpid; 8628c2ecf20Sopenharmony_ci out.shm_nattch = in->shm_nattch; 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci return copy_to_user(buf, &out, sizeof(out)); 8658c2ecf20Sopenharmony_ci } 8668c2ecf20Sopenharmony_ci default: 8678c2ecf20Sopenharmony_ci return -EINVAL; 8688c2ecf20Sopenharmony_ci } 8698c2ecf20Sopenharmony_ci} 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_cistatic inline unsigned long 8728c2ecf20Sopenharmony_cicopy_shmid_from_user(struct shmid64_ds *out, void __user *buf, int version) 8738c2ecf20Sopenharmony_ci{ 8748c2ecf20Sopenharmony_ci switch (version) { 8758c2ecf20Sopenharmony_ci case IPC_64: 8768c2ecf20Sopenharmony_ci if (copy_from_user(out, buf, sizeof(*out))) 8778c2ecf20Sopenharmony_ci return -EFAULT; 8788c2ecf20Sopenharmony_ci return 0; 8798c2ecf20Sopenharmony_ci case IPC_OLD: 8808c2ecf20Sopenharmony_ci { 8818c2ecf20Sopenharmony_ci struct shmid_ds tbuf_old; 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci if (copy_from_user(&tbuf_old, buf, sizeof(tbuf_old))) 8848c2ecf20Sopenharmony_ci return -EFAULT; 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci out->shm_perm.uid = tbuf_old.shm_perm.uid; 8878c2ecf20Sopenharmony_ci out->shm_perm.gid = tbuf_old.shm_perm.gid; 8888c2ecf20Sopenharmony_ci out->shm_perm.mode = tbuf_old.shm_perm.mode; 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci return 0; 8918c2ecf20Sopenharmony_ci } 8928c2ecf20Sopenharmony_ci default: 8938c2ecf20Sopenharmony_ci return -EINVAL; 8948c2ecf20Sopenharmony_ci } 8958c2ecf20Sopenharmony_ci} 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_cistatic inline unsigned long copy_shminfo_to_user(void __user *buf, struct shminfo64 *in, int version) 8988c2ecf20Sopenharmony_ci{ 8998c2ecf20Sopenharmony_ci switch (version) { 9008c2ecf20Sopenharmony_ci case IPC_64: 9018c2ecf20Sopenharmony_ci return copy_to_user(buf, in, sizeof(*in)); 9028c2ecf20Sopenharmony_ci case IPC_OLD: 9038c2ecf20Sopenharmony_ci { 9048c2ecf20Sopenharmony_ci struct shminfo out; 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci if (in->shmmax > INT_MAX) 9078c2ecf20Sopenharmony_ci out.shmmax = INT_MAX; 9088c2ecf20Sopenharmony_ci else 9098c2ecf20Sopenharmony_ci out.shmmax = (int)in->shmmax; 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci out.shmmin = in->shmmin; 9128c2ecf20Sopenharmony_ci out.shmmni = in->shmmni; 9138c2ecf20Sopenharmony_ci out.shmseg = in->shmseg; 9148c2ecf20Sopenharmony_ci out.shmall = in->shmall; 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci return copy_to_user(buf, &out, sizeof(out)); 9178c2ecf20Sopenharmony_ci } 9188c2ecf20Sopenharmony_ci default: 9198c2ecf20Sopenharmony_ci return -EINVAL; 9208c2ecf20Sopenharmony_ci } 9218c2ecf20Sopenharmony_ci} 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci/* 9248c2ecf20Sopenharmony_ci * Calculate and add used RSS and swap pages of a shm. 9258c2ecf20Sopenharmony_ci * Called with shm_ids.rwsem held as a reader 9268c2ecf20Sopenharmony_ci */ 9278c2ecf20Sopenharmony_cistatic void shm_add_rss_swap(struct shmid_kernel *shp, 9288c2ecf20Sopenharmony_ci unsigned long *rss_add, unsigned long *swp_add) 9298c2ecf20Sopenharmony_ci{ 9308c2ecf20Sopenharmony_ci struct inode *inode; 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci inode = file_inode(shp->shm_file); 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci if (is_file_hugepages(shp->shm_file)) { 9358c2ecf20Sopenharmony_ci struct address_space *mapping = inode->i_mapping; 9368c2ecf20Sopenharmony_ci struct hstate *h = hstate_file(shp->shm_file); 9378c2ecf20Sopenharmony_ci *rss_add += pages_per_huge_page(h) * mapping->nrpages; 9388c2ecf20Sopenharmony_ci } else { 9398c2ecf20Sopenharmony_ci#ifdef CONFIG_SHMEM 9408c2ecf20Sopenharmony_ci struct shmem_inode_info *info = SHMEM_I(inode); 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci spin_lock_irq(&info->lock); 9438c2ecf20Sopenharmony_ci *rss_add += inode->i_mapping->nrpages; 9448c2ecf20Sopenharmony_ci *swp_add += info->swapped; 9458c2ecf20Sopenharmony_ci spin_unlock_irq(&info->lock); 9468c2ecf20Sopenharmony_ci#else 9478c2ecf20Sopenharmony_ci *rss_add += inode->i_mapping->nrpages; 9488c2ecf20Sopenharmony_ci#endif 9498c2ecf20Sopenharmony_ci } 9508c2ecf20Sopenharmony_ci} 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci/* 9538c2ecf20Sopenharmony_ci * Called with shm_ids.rwsem held as a reader 9548c2ecf20Sopenharmony_ci */ 9558c2ecf20Sopenharmony_cistatic void shm_get_stat(struct ipc_namespace *ns, unsigned long *rss, 9568c2ecf20Sopenharmony_ci unsigned long *swp) 9578c2ecf20Sopenharmony_ci{ 9588c2ecf20Sopenharmony_ci int next_id; 9598c2ecf20Sopenharmony_ci int total, in_use; 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci *rss = 0; 9628c2ecf20Sopenharmony_ci *swp = 0; 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci in_use = shm_ids(ns).in_use; 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci for (total = 0, next_id = 0; total < in_use; next_id++) { 9678c2ecf20Sopenharmony_ci struct kern_ipc_perm *ipc; 9688c2ecf20Sopenharmony_ci struct shmid_kernel *shp; 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci ipc = idr_find(&shm_ids(ns).ipcs_idr, next_id); 9718c2ecf20Sopenharmony_ci if (ipc == NULL) 9728c2ecf20Sopenharmony_ci continue; 9738c2ecf20Sopenharmony_ci shp = container_of(ipc, struct shmid_kernel, shm_perm); 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci shm_add_rss_swap(shp, rss, swp); 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci total++; 9788c2ecf20Sopenharmony_ci } 9798c2ecf20Sopenharmony_ci} 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci/* 9828c2ecf20Sopenharmony_ci * This function handles some shmctl commands which require the rwsem 9838c2ecf20Sopenharmony_ci * to be held in write mode. 9848c2ecf20Sopenharmony_ci * NOTE: no locks must be held, the rwsem is taken inside this function. 9858c2ecf20Sopenharmony_ci */ 9868c2ecf20Sopenharmony_cistatic int shmctl_down(struct ipc_namespace *ns, int shmid, int cmd, 9878c2ecf20Sopenharmony_ci struct shmid64_ds *shmid64) 9888c2ecf20Sopenharmony_ci{ 9898c2ecf20Sopenharmony_ci struct kern_ipc_perm *ipcp; 9908c2ecf20Sopenharmony_ci struct shmid_kernel *shp; 9918c2ecf20Sopenharmony_ci int err; 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci down_write(&shm_ids(ns).rwsem); 9948c2ecf20Sopenharmony_ci rcu_read_lock(); 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci ipcp = ipcctl_obtain_check(ns, &shm_ids(ns), shmid, cmd, 9978c2ecf20Sopenharmony_ci &shmid64->shm_perm, 0); 9988c2ecf20Sopenharmony_ci if (IS_ERR(ipcp)) { 9998c2ecf20Sopenharmony_ci err = PTR_ERR(ipcp); 10008c2ecf20Sopenharmony_ci goto out_unlock1; 10018c2ecf20Sopenharmony_ci } 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci shp = container_of(ipcp, struct shmid_kernel, shm_perm); 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci err = security_shm_shmctl(&shp->shm_perm, cmd); 10068c2ecf20Sopenharmony_ci if (err) 10078c2ecf20Sopenharmony_ci goto out_unlock1; 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci switch (cmd) { 10108c2ecf20Sopenharmony_ci case IPC_RMID: 10118c2ecf20Sopenharmony_ci ipc_lock_object(&shp->shm_perm); 10128c2ecf20Sopenharmony_ci /* do_shm_rmid unlocks the ipc object and rcu */ 10138c2ecf20Sopenharmony_ci do_shm_rmid(ns, ipcp); 10148c2ecf20Sopenharmony_ci goto out_up; 10158c2ecf20Sopenharmony_ci case IPC_SET: 10168c2ecf20Sopenharmony_ci ipc_lock_object(&shp->shm_perm); 10178c2ecf20Sopenharmony_ci err = ipc_update_perm(&shmid64->shm_perm, ipcp); 10188c2ecf20Sopenharmony_ci if (err) 10198c2ecf20Sopenharmony_ci goto out_unlock0; 10208c2ecf20Sopenharmony_ci shp->shm_ctim = ktime_get_real_seconds(); 10218c2ecf20Sopenharmony_ci break; 10228c2ecf20Sopenharmony_ci default: 10238c2ecf20Sopenharmony_ci err = -EINVAL; 10248c2ecf20Sopenharmony_ci goto out_unlock1; 10258c2ecf20Sopenharmony_ci } 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ciout_unlock0: 10288c2ecf20Sopenharmony_ci ipc_unlock_object(&shp->shm_perm); 10298c2ecf20Sopenharmony_ciout_unlock1: 10308c2ecf20Sopenharmony_ci rcu_read_unlock(); 10318c2ecf20Sopenharmony_ciout_up: 10328c2ecf20Sopenharmony_ci up_write(&shm_ids(ns).rwsem); 10338c2ecf20Sopenharmony_ci return err; 10348c2ecf20Sopenharmony_ci} 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_cistatic int shmctl_ipc_info(struct ipc_namespace *ns, 10378c2ecf20Sopenharmony_ci struct shminfo64 *shminfo) 10388c2ecf20Sopenharmony_ci{ 10398c2ecf20Sopenharmony_ci int err = security_shm_shmctl(NULL, IPC_INFO); 10408c2ecf20Sopenharmony_ci if (!err) { 10418c2ecf20Sopenharmony_ci memset(shminfo, 0, sizeof(*shminfo)); 10428c2ecf20Sopenharmony_ci shminfo->shmmni = shminfo->shmseg = ns->shm_ctlmni; 10438c2ecf20Sopenharmony_ci shminfo->shmmax = ns->shm_ctlmax; 10448c2ecf20Sopenharmony_ci shminfo->shmall = ns->shm_ctlall; 10458c2ecf20Sopenharmony_ci shminfo->shmmin = SHMMIN; 10468c2ecf20Sopenharmony_ci down_read(&shm_ids(ns).rwsem); 10478c2ecf20Sopenharmony_ci err = ipc_get_maxidx(&shm_ids(ns)); 10488c2ecf20Sopenharmony_ci up_read(&shm_ids(ns).rwsem); 10498c2ecf20Sopenharmony_ci if (err < 0) 10508c2ecf20Sopenharmony_ci err = 0; 10518c2ecf20Sopenharmony_ci } 10528c2ecf20Sopenharmony_ci return err; 10538c2ecf20Sopenharmony_ci} 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_cistatic int shmctl_shm_info(struct ipc_namespace *ns, 10568c2ecf20Sopenharmony_ci struct shm_info *shm_info) 10578c2ecf20Sopenharmony_ci{ 10588c2ecf20Sopenharmony_ci int err = security_shm_shmctl(NULL, SHM_INFO); 10598c2ecf20Sopenharmony_ci if (!err) { 10608c2ecf20Sopenharmony_ci memset(shm_info, 0, sizeof(*shm_info)); 10618c2ecf20Sopenharmony_ci down_read(&shm_ids(ns).rwsem); 10628c2ecf20Sopenharmony_ci shm_info->used_ids = shm_ids(ns).in_use; 10638c2ecf20Sopenharmony_ci shm_get_stat(ns, &shm_info->shm_rss, &shm_info->shm_swp); 10648c2ecf20Sopenharmony_ci shm_info->shm_tot = ns->shm_tot; 10658c2ecf20Sopenharmony_ci shm_info->swap_attempts = 0; 10668c2ecf20Sopenharmony_ci shm_info->swap_successes = 0; 10678c2ecf20Sopenharmony_ci err = ipc_get_maxidx(&shm_ids(ns)); 10688c2ecf20Sopenharmony_ci up_read(&shm_ids(ns).rwsem); 10698c2ecf20Sopenharmony_ci if (err < 0) 10708c2ecf20Sopenharmony_ci err = 0; 10718c2ecf20Sopenharmony_ci } 10728c2ecf20Sopenharmony_ci return err; 10738c2ecf20Sopenharmony_ci} 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_cistatic int shmctl_stat(struct ipc_namespace *ns, int shmid, 10768c2ecf20Sopenharmony_ci int cmd, struct shmid64_ds *tbuf) 10778c2ecf20Sopenharmony_ci{ 10788c2ecf20Sopenharmony_ci struct shmid_kernel *shp; 10798c2ecf20Sopenharmony_ci int err; 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci memset(tbuf, 0, sizeof(*tbuf)); 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci rcu_read_lock(); 10848c2ecf20Sopenharmony_ci if (cmd == SHM_STAT || cmd == SHM_STAT_ANY) { 10858c2ecf20Sopenharmony_ci shp = shm_obtain_object(ns, shmid); 10868c2ecf20Sopenharmony_ci if (IS_ERR(shp)) { 10878c2ecf20Sopenharmony_ci err = PTR_ERR(shp); 10888c2ecf20Sopenharmony_ci goto out_unlock; 10898c2ecf20Sopenharmony_ci } 10908c2ecf20Sopenharmony_ci } else { /* IPC_STAT */ 10918c2ecf20Sopenharmony_ci shp = shm_obtain_object_check(ns, shmid); 10928c2ecf20Sopenharmony_ci if (IS_ERR(shp)) { 10938c2ecf20Sopenharmony_ci err = PTR_ERR(shp); 10948c2ecf20Sopenharmony_ci goto out_unlock; 10958c2ecf20Sopenharmony_ci } 10968c2ecf20Sopenharmony_ci } 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci /* 10998c2ecf20Sopenharmony_ci * Semantically SHM_STAT_ANY ought to be identical to 11008c2ecf20Sopenharmony_ci * that functionality provided by the /proc/sysvipc/ 11018c2ecf20Sopenharmony_ci * interface. As such, only audit these calls and 11028c2ecf20Sopenharmony_ci * do not do traditional S_IRUGO permission checks on 11038c2ecf20Sopenharmony_ci * the ipc object. 11048c2ecf20Sopenharmony_ci */ 11058c2ecf20Sopenharmony_ci if (cmd == SHM_STAT_ANY) 11068c2ecf20Sopenharmony_ci audit_ipc_obj(&shp->shm_perm); 11078c2ecf20Sopenharmony_ci else { 11088c2ecf20Sopenharmony_ci err = -EACCES; 11098c2ecf20Sopenharmony_ci if (ipcperms(ns, &shp->shm_perm, S_IRUGO)) 11108c2ecf20Sopenharmony_ci goto out_unlock; 11118c2ecf20Sopenharmony_ci } 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci err = security_shm_shmctl(&shp->shm_perm, cmd); 11148c2ecf20Sopenharmony_ci if (err) 11158c2ecf20Sopenharmony_ci goto out_unlock; 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci ipc_lock_object(&shp->shm_perm); 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci if (!ipc_valid_object(&shp->shm_perm)) { 11208c2ecf20Sopenharmony_ci ipc_unlock_object(&shp->shm_perm); 11218c2ecf20Sopenharmony_ci err = -EIDRM; 11228c2ecf20Sopenharmony_ci goto out_unlock; 11238c2ecf20Sopenharmony_ci } 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci kernel_to_ipc64_perm(&shp->shm_perm, &tbuf->shm_perm); 11268c2ecf20Sopenharmony_ci tbuf->shm_segsz = shp->shm_segsz; 11278c2ecf20Sopenharmony_ci tbuf->shm_atime = shp->shm_atim; 11288c2ecf20Sopenharmony_ci tbuf->shm_dtime = shp->shm_dtim; 11298c2ecf20Sopenharmony_ci tbuf->shm_ctime = shp->shm_ctim; 11308c2ecf20Sopenharmony_ci#ifndef CONFIG_64BIT 11318c2ecf20Sopenharmony_ci tbuf->shm_atime_high = shp->shm_atim >> 32; 11328c2ecf20Sopenharmony_ci tbuf->shm_dtime_high = shp->shm_dtim >> 32; 11338c2ecf20Sopenharmony_ci tbuf->shm_ctime_high = shp->shm_ctim >> 32; 11348c2ecf20Sopenharmony_ci#endif 11358c2ecf20Sopenharmony_ci tbuf->shm_cpid = pid_vnr(shp->shm_cprid); 11368c2ecf20Sopenharmony_ci tbuf->shm_lpid = pid_vnr(shp->shm_lprid); 11378c2ecf20Sopenharmony_ci tbuf->shm_nattch = shp->shm_nattch; 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci if (cmd == IPC_STAT) { 11408c2ecf20Sopenharmony_ci /* 11418c2ecf20Sopenharmony_ci * As defined in SUS: 11428c2ecf20Sopenharmony_ci * Return 0 on success 11438c2ecf20Sopenharmony_ci */ 11448c2ecf20Sopenharmony_ci err = 0; 11458c2ecf20Sopenharmony_ci } else { 11468c2ecf20Sopenharmony_ci /* 11478c2ecf20Sopenharmony_ci * SHM_STAT and SHM_STAT_ANY (both Linux specific) 11488c2ecf20Sopenharmony_ci * Return the full id, including the sequence number 11498c2ecf20Sopenharmony_ci */ 11508c2ecf20Sopenharmony_ci err = shp->shm_perm.id; 11518c2ecf20Sopenharmony_ci } 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci ipc_unlock_object(&shp->shm_perm); 11548c2ecf20Sopenharmony_ciout_unlock: 11558c2ecf20Sopenharmony_ci rcu_read_unlock(); 11568c2ecf20Sopenharmony_ci return err; 11578c2ecf20Sopenharmony_ci} 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_cistatic int shmctl_do_lock(struct ipc_namespace *ns, int shmid, int cmd) 11608c2ecf20Sopenharmony_ci{ 11618c2ecf20Sopenharmony_ci struct shmid_kernel *shp; 11628c2ecf20Sopenharmony_ci struct file *shm_file; 11638c2ecf20Sopenharmony_ci int err; 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci rcu_read_lock(); 11668c2ecf20Sopenharmony_ci shp = shm_obtain_object_check(ns, shmid); 11678c2ecf20Sopenharmony_ci if (IS_ERR(shp)) { 11688c2ecf20Sopenharmony_ci err = PTR_ERR(shp); 11698c2ecf20Sopenharmony_ci goto out_unlock1; 11708c2ecf20Sopenharmony_ci } 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci audit_ipc_obj(&(shp->shm_perm)); 11738c2ecf20Sopenharmony_ci err = security_shm_shmctl(&shp->shm_perm, cmd); 11748c2ecf20Sopenharmony_ci if (err) 11758c2ecf20Sopenharmony_ci goto out_unlock1; 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci ipc_lock_object(&shp->shm_perm); 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_ci /* check if shm_destroy() is tearing down shp */ 11808c2ecf20Sopenharmony_ci if (!ipc_valid_object(&shp->shm_perm)) { 11818c2ecf20Sopenharmony_ci err = -EIDRM; 11828c2ecf20Sopenharmony_ci goto out_unlock0; 11838c2ecf20Sopenharmony_ci } 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci if (!ns_capable(ns->user_ns, CAP_IPC_LOCK)) { 11868c2ecf20Sopenharmony_ci kuid_t euid = current_euid(); 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci if (!uid_eq(euid, shp->shm_perm.uid) && 11898c2ecf20Sopenharmony_ci !uid_eq(euid, shp->shm_perm.cuid)) { 11908c2ecf20Sopenharmony_ci err = -EPERM; 11918c2ecf20Sopenharmony_ci goto out_unlock0; 11928c2ecf20Sopenharmony_ci } 11938c2ecf20Sopenharmony_ci if (cmd == SHM_LOCK && !rlimit(RLIMIT_MEMLOCK)) { 11948c2ecf20Sopenharmony_ci err = -EPERM; 11958c2ecf20Sopenharmony_ci goto out_unlock0; 11968c2ecf20Sopenharmony_ci } 11978c2ecf20Sopenharmony_ci } 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci shm_file = shp->shm_file; 12008c2ecf20Sopenharmony_ci if (is_file_hugepages(shm_file)) 12018c2ecf20Sopenharmony_ci goto out_unlock0; 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci if (cmd == SHM_LOCK) { 12048c2ecf20Sopenharmony_ci struct user_struct *user = current_user(); 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci err = shmem_lock(shm_file, 1, user); 12078c2ecf20Sopenharmony_ci if (!err && !(shp->shm_perm.mode & SHM_LOCKED)) { 12088c2ecf20Sopenharmony_ci shp->shm_perm.mode |= SHM_LOCKED; 12098c2ecf20Sopenharmony_ci shp->mlock_user = user; 12108c2ecf20Sopenharmony_ci } 12118c2ecf20Sopenharmony_ci goto out_unlock0; 12128c2ecf20Sopenharmony_ci } 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci /* SHM_UNLOCK */ 12158c2ecf20Sopenharmony_ci if (!(shp->shm_perm.mode & SHM_LOCKED)) 12168c2ecf20Sopenharmony_ci goto out_unlock0; 12178c2ecf20Sopenharmony_ci shmem_lock(shm_file, 0, shp->mlock_user); 12188c2ecf20Sopenharmony_ci shp->shm_perm.mode &= ~SHM_LOCKED; 12198c2ecf20Sopenharmony_ci shp->mlock_user = NULL; 12208c2ecf20Sopenharmony_ci get_file(shm_file); 12218c2ecf20Sopenharmony_ci ipc_unlock_object(&shp->shm_perm); 12228c2ecf20Sopenharmony_ci rcu_read_unlock(); 12238c2ecf20Sopenharmony_ci shmem_unlock_mapping(shm_file->f_mapping); 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci fput(shm_file); 12268c2ecf20Sopenharmony_ci return err; 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ciout_unlock0: 12298c2ecf20Sopenharmony_ci ipc_unlock_object(&shp->shm_perm); 12308c2ecf20Sopenharmony_ciout_unlock1: 12318c2ecf20Sopenharmony_ci rcu_read_unlock(); 12328c2ecf20Sopenharmony_ci return err; 12338c2ecf20Sopenharmony_ci} 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_cistatic long ksys_shmctl(int shmid, int cmd, struct shmid_ds __user *buf, int version) 12368c2ecf20Sopenharmony_ci{ 12378c2ecf20Sopenharmony_ci int err; 12388c2ecf20Sopenharmony_ci struct ipc_namespace *ns; 12398c2ecf20Sopenharmony_ci struct shmid64_ds sem64; 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci if (cmd < 0 || shmid < 0) 12428c2ecf20Sopenharmony_ci return -EINVAL; 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci ns = current->nsproxy->ipc_ns; 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci switch (cmd) { 12478c2ecf20Sopenharmony_ci case IPC_INFO: { 12488c2ecf20Sopenharmony_ci struct shminfo64 shminfo; 12498c2ecf20Sopenharmony_ci err = shmctl_ipc_info(ns, &shminfo); 12508c2ecf20Sopenharmony_ci if (err < 0) 12518c2ecf20Sopenharmony_ci return err; 12528c2ecf20Sopenharmony_ci if (copy_shminfo_to_user(buf, &shminfo, version)) 12538c2ecf20Sopenharmony_ci err = -EFAULT; 12548c2ecf20Sopenharmony_ci return err; 12558c2ecf20Sopenharmony_ci } 12568c2ecf20Sopenharmony_ci case SHM_INFO: { 12578c2ecf20Sopenharmony_ci struct shm_info shm_info; 12588c2ecf20Sopenharmony_ci err = shmctl_shm_info(ns, &shm_info); 12598c2ecf20Sopenharmony_ci if (err < 0) 12608c2ecf20Sopenharmony_ci return err; 12618c2ecf20Sopenharmony_ci if (copy_to_user(buf, &shm_info, sizeof(shm_info))) 12628c2ecf20Sopenharmony_ci err = -EFAULT; 12638c2ecf20Sopenharmony_ci return err; 12648c2ecf20Sopenharmony_ci } 12658c2ecf20Sopenharmony_ci case SHM_STAT: 12668c2ecf20Sopenharmony_ci case SHM_STAT_ANY: 12678c2ecf20Sopenharmony_ci case IPC_STAT: { 12688c2ecf20Sopenharmony_ci err = shmctl_stat(ns, shmid, cmd, &sem64); 12698c2ecf20Sopenharmony_ci if (err < 0) 12708c2ecf20Sopenharmony_ci return err; 12718c2ecf20Sopenharmony_ci if (copy_shmid_to_user(buf, &sem64, version)) 12728c2ecf20Sopenharmony_ci err = -EFAULT; 12738c2ecf20Sopenharmony_ci return err; 12748c2ecf20Sopenharmony_ci } 12758c2ecf20Sopenharmony_ci case IPC_SET: 12768c2ecf20Sopenharmony_ci if (copy_shmid_from_user(&sem64, buf, version)) 12778c2ecf20Sopenharmony_ci return -EFAULT; 12788c2ecf20Sopenharmony_ci fallthrough; 12798c2ecf20Sopenharmony_ci case IPC_RMID: 12808c2ecf20Sopenharmony_ci return shmctl_down(ns, shmid, cmd, &sem64); 12818c2ecf20Sopenharmony_ci case SHM_LOCK: 12828c2ecf20Sopenharmony_ci case SHM_UNLOCK: 12838c2ecf20Sopenharmony_ci return shmctl_do_lock(ns, shmid, cmd); 12848c2ecf20Sopenharmony_ci default: 12858c2ecf20Sopenharmony_ci return -EINVAL; 12868c2ecf20Sopenharmony_ci } 12878c2ecf20Sopenharmony_ci} 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ciSYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf) 12908c2ecf20Sopenharmony_ci{ 12918c2ecf20Sopenharmony_ci return ksys_shmctl(shmid, cmd, buf, IPC_64); 12928c2ecf20Sopenharmony_ci} 12938c2ecf20Sopenharmony_ci 12948c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_WANT_IPC_PARSE_VERSION 12958c2ecf20Sopenharmony_cilong ksys_old_shmctl(int shmid, int cmd, struct shmid_ds __user *buf) 12968c2ecf20Sopenharmony_ci{ 12978c2ecf20Sopenharmony_ci int version = ipc_parse_version(&cmd); 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci return ksys_shmctl(shmid, cmd, buf, version); 13008c2ecf20Sopenharmony_ci} 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ciSYSCALL_DEFINE3(old_shmctl, int, shmid, int, cmd, struct shmid_ds __user *, buf) 13038c2ecf20Sopenharmony_ci{ 13048c2ecf20Sopenharmony_ci return ksys_old_shmctl(shmid, cmd, buf); 13058c2ecf20Sopenharmony_ci} 13068c2ecf20Sopenharmony_ci#endif 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_cistruct compat_shmid_ds { 13118c2ecf20Sopenharmony_ci struct compat_ipc_perm shm_perm; 13128c2ecf20Sopenharmony_ci int shm_segsz; 13138c2ecf20Sopenharmony_ci old_time32_t shm_atime; 13148c2ecf20Sopenharmony_ci old_time32_t shm_dtime; 13158c2ecf20Sopenharmony_ci old_time32_t shm_ctime; 13168c2ecf20Sopenharmony_ci compat_ipc_pid_t shm_cpid; 13178c2ecf20Sopenharmony_ci compat_ipc_pid_t shm_lpid; 13188c2ecf20Sopenharmony_ci unsigned short shm_nattch; 13198c2ecf20Sopenharmony_ci unsigned short shm_unused; 13208c2ecf20Sopenharmony_ci compat_uptr_t shm_unused2; 13218c2ecf20Sopenharmony_ci compat_uptr_t shm_unused3; 13228c2ecf20Sopenharmony_ci}; 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_cistruct compat_shminfo64 { 13258c2ecf20Sopenharmony_ci compat_ulong_t shmmax; 13268c2ecf20Sopenharmony_ci compat_ulong_t shmmin; 13278c2ecf20Sopenharmony_ci compat_ulong_t shmmni; 13288c2ecf20Sopenharmony_ci compat_ulong_t shmseg; 13298c2ecf20Sopenharmony_ci compat_ulong_t shmall; 13308c2ecf20Sopenharmony_ci compat_ulong_t __unused1; 13318c2ecf20Sopenharmony_ci compat_ulong_t __unused2; 13328c2ecf20Sopenharmony_ci compat_ulong_t __unused3; 13338c2ecf20Sopenharmony_ci compat_ulong_t __unused4; 13348c2ecf20Sopenharmony_ci}; 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_cistruct compat_shm_info { 13378c2ecf20Sopenharmony_ci compat_int_t used_ids; 13388c2ecf20Sopenharmony_ci compat_ulong_t shm_tot, shm_rss, shm_swp; 13398c2ecf20Sopenharmony_ci compat_ulong_t swap_attempts, swap_successes; 13408c2ecf20Sopenharmony_ci}; 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_cistatic int copy_compat_shminfo_to_user(void __user *buf, struct shminfo64 *in, 13438c2ecf20Sopenharmony_ci int version) 13448c2ecf20Sopenharmony_ci{ 13458c2ecf20Sopenharmony_ci if (in->shmmax > INT_MAX) 13468c2ecf20Sopenharmony_ci in->shmmax = INT_MAX; 13478c2ecf20Sopenharmony_ci if (version == IPC_64) { 13488c2ecf20Sopenharmony_ci struct compat_shminfo64 info; 13498c2ecf20Sopenharmony_ci memset(&info, 0, sizeof(info)); 13508c2ecf20Sopenharmony_ci info.shmmax = in->shmmax; 13518c2ecf20Sopenharmony_ci info.shmmin = in->shmmin; 13528c2ecf20Sopenharmony_ci info.shmmni = in->shmmni; 13538c2ecf20Sopenharmony_ci info.shmseg = in->shmseg; 13548c2ecf20Sopenharmony_ci info.shmall = in->shmall; 13558c2ecf20Sopenharmony_ci return copy_to_user(buf, &info, sizeof(info)); 13568c2ecf20Sopenharmony_ci } else { 13578c2ecf20Sopenharmony_ci struct shminfo info; 13588c2ecf20Sopenharmony_ci memset(&info, 0, sizeof(info)); 13598c2ecf20Sopenharmony_ci info.shmmax = in->shmmax; 13608c2ecf20Sopenharmony_ci info.shmmin = in->shmmin; 13618c2ecf20Sopenharmony_ci info.shmmni = in->shmmni; 13628c2ecf20Sopenharmony_ci info.shmseg = in->shmseg; 13638c2ecf20Sopenharmony_ci info.shmall = in->shmall; 13648c2ecf20Sopenharmony_ci return copy_to_user(buf, &info, sizeof(info)); 13658c2ecf20Sopenharmony_ci } 13668c2ecf20Sopenharmony_ci} 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_cistatic int put_compat_shm_info(struct shm_info *ip, 13698c2ecf20Sopenharmony_ci struct compat_shm_info __user *uip) 13708c2ecf20Sopenharmony_ci{ 13718c2ecf20Sopenharmony_ci struct compat_shm_info info; 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci memset(&info, 0, sizeof(info)); 13748c2ecf20Sopenharmony_ci info.used_ids = ip->used_ids; 13758c2ecf20Sopenharmony_ci info.shm_tot = ip->shm_tot; 13768c2ecf20Sopenharmony_ci info.shm_rss = ip->shm_rss; 13778c2ecf20Sopenharmony_ci info.shm_swp = ip->shm_swp; 13788c2ecf20Sopenharmony_ci info.swap_attempts = ip->swap_attempts; 13798c2ecf20Sopenharmony_ci info.swap_successes = ip->swap_successes; 13808c2ecf20Sopenharmony_ci return copy_to_user(uip, &info, sizeof(info)); 13818c2ecf20Sopenharmony_ci} 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_cistatic int copy_compat_shmid_to_user(void __user *buf, struct shmid64_ds *in, 13848c2ecf20Sopenharmony_ci int version) 13858c2ecf20Sopenharmony_ci{ 13868c2ecf20Sopenharmony_ci if (version == IPC_64) { 13878c2ecf20Sopenharmony_ci struct compat_shmid64_ds v; 13888c2ecf20Sopenharmony_ci memset(&v, 0, sizeof(v)); 13898c2ecf20Sopenharmony_ci to_compat_ipc64_perm(&v.shm_perm, &in->shm_perm); 13908c2ecf20Sopenharmony_ci v.shm_atime = lower_32_bits(in->shm_atime); 13918c2ecf20Sopenharmony_ci v.shm_atime_high = upper_32_bits(in->shm_atime); 13928c2ecf20Sopenharmony_ci v.shm_dtime = lower_32_bits(in->shm_dtime); 13938c2ecf20Sopenharmony_ci v.shm_dtime_high = upper_32_bits(in->shm_dtime); 13948c2ecf20Sopenharmony_ci v.shm_ctime = lower_32_bits(in->shm_ctime); 13958c2ecf20Sopenharmony_ci v.shm_ctime_high = upper_32_bits(in->shm_ctime); 13968c2ecf20Sopenharmony_ci v.shm_segsz = in->shm_segsz; 13978c2ecf20Sopenharmony_ci v.shm_nattch = in->shm_nattch; 13988c2ecf20Sopenharmony_ci v.shm_cpid = in->shm_cpid; 13998c2ecf20Sopenharmony_ci v.shm_lpid = in->shm_lpid; 14008c2ecf20Sopenharmony_ci return copy_to_user(buf, &v, sizeof(v)); 14018c2ecf20Sopenharmony_ci } else { 14028c2ecf20Sopenharmony_ci struct compat_shmid_ds v; 14038c2ecf20Sopenharmony_ci memset(&v, 0, sizeof(v)); 14048c2ecf20Sopenharmony_ci to_compat_ipc_perm(&v.shm_perm, &in->shm_perm); 14058c2ecf20Sopenharmony_ci v.shm_perm.key = in->shm_perm.key; 14068c2ecf20Sopenharmony_ci v.shm_atime = in->shm_atime; 14078c2ecf20Sopenharmony_ci v.shm_dtime = in->shm_dtime; 14088c2ecf20Sopenharmony_ci v.shm_ctime = in->shm_ctime; 14098c2ecf20Sopenharmony_ci v.shm_segsz = in->shm_segsz; 14108c2ecf20Sopenharmony_ci v.shm_nattch = in->shm_nattch; 14118c2ecf20Sopenharmony_ci v.shm_cpid = in->shm_cpid; 14128c2ecf20Sopenharmony_ci v.shm_lpid = in->shm_lpid; 14138c2ecf20Sopenharmony_ci return copy_to_user(buf, &v, sizeof(v)); 14148c2ecf20Sopenharmony_ci } 14158c2ecf20Sopenharmony_ci} 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_cistatic int copy_compat_shmid_from_user(struct shmid64_ds *out, void __user *buf, 14188c2ecf20Sopenharmony_ci int version) 14198c2ecf20Sopenharmony_ci{ 14208c2ecf20Sopenharmony_ci memset(out, 0, sizeof(*out)); 14218c2ecf20Sopenharmony_ci if (version == IPC_64) { 14228c2ecf20Sopenharmony_ci struct compat_shmid64_ds __user *p = buf; 14238c2ecf20Sopenharmony_ci return get_compat_ipc64_perm(&out->shm_perm, &p->shm_perm); 14248c2ecf20Sopenharmony_ci } else { 14258c2ecf20Sopenharmony_ci struct compat_shmid_ds __user *p = buf; 14268c2ecf20Sopenharmony_ci return get_compat_ipc_perm(&out->shm_perm, &p->shm_perm); 14278c2ecf20Sopenharmony_ci } 14288c2ecf20Sopenharmony_ci} 14298c2ecf20Sopenharmony_ci 14308c2ecf20Sopenharmony_cistatic long compat_ksys_shmctl(int shmid, int cmd, void __user *uptr, int version) 14318c2ecf20Sopenharmony_ci{ 14328c2ecf20Sopenharmony_ci struct ipc_namespace *ns; 14338c2ecf20Sopenharmony_ci struct shmid64_ds sem64; 14348c2ecf20Sopenharmony_ci int err; 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_ci ns = current->nsproxy->ipc_ns; 14378c2ecf20Sopenharmony_ci 14388c2ecf20Sopenharmony_ci if (cmd < 0 || shmid < 0) 14398c2ecf20Sopenharmony_ci return -EINVAL; 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_ci switch (cmd) { 14428c2ecf20Sopenharmony_ci case IPC_INFO: { 14438c2ecf20Sopenharmony_ci struct shminfo64 shminfo; 14448c2ecf20Sopenharmony_ci err = shmctl_ipc_info(ns, &shminfo); 14458c2ecf20Sopenharmony_ci if (err < 0) 14468c2ecf20Sopenharmony_ci return err; 14478c2ecf20Sopenharmony_ci if (copy_compat_shminfo_to_user(uptr, &shminfo, version)) 14488c2ecf20Sopenharmony_ci err = -EFAULT; 14498c2ecf20Sopenharmony_ci return err; 14508c2ecf20Sopenharmony_ci } 14518c2ecf20Sopenharmony_ci case SHM_INFO: { 14528c2ecf20Sopenharmony_ci struct shm_info shm_info; 14538c2ecf20Sopenharmony_ci err = shmctl_shm_info(ns, &shm_info); 14548c2ecf20Sopenharmony_ci if (err < 0) 14558c2ecf20Sopenharmony_ci return err; 14568c2ecf20Sopenharmony_ci if (put_compat_shm_info(&shm_info, uptr)) 14578c2ecf20Sopenharmony_ci err = -EFAULT; 14588c2ecf20Sopenharmony_ci return err; 14598c2ecf20Sopenharmony_ci } 14608c2ecf20Sopenharmony_ci case IPC_STAT: 14618c2ecf20Sopenharmony_ci case SHM_STAT_ANY: 14628c2ecf20Sopenharmony_ci case SHM_STAT: 14638c2ecf20Sopenharmony_ci err = shmctl_stat(ns, shmid, cmd, &sem64); 14648c2ecf20Sopenharmony_ci if (err < 0) 14658c2ecf20Sopenharmony_ci return err; 14668c2ecf20Sopenharmony_ci if (copy_compat_shmid_to_user(uptr, &sem64, version)) 14678c2ecf20Sopenharmony_ci err = -EFAULT; 14688c2ecf20Sopenharmony_ci return err; 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_ci case IPC_SET: 14718c2ecf20Sopenharmony_ci if (copy_compat_shmid_from_user(&sem64, uptr, version)) 14728c2ecf20Sopenharmony_ci return -EFAULT; 14738c2ecf20Sopenharmony_ci fallthrough; 14748c2ecf20Sopenharmony_ci case IPC_RMID: 14758c2ecf20Sopenharmony_ci return shmctl_down(ns, shmid, cmd, &sem64); 14768c2ecf20Sopenharmony_ci case SHM_LOCK: 14778c2ecf20Sopenharmony_ci case SHM_UNLOCK: 14788c2ecf20Sopenharmony_ci return shmctl_do_lock(ns, shmid, cmd); 14798c2ecf20Sopenharmony_ci default: 14808c2ecf20Sopenharmony_ci return -EINVAL; 14818c2ecf20Sopenharmony_ci } 14828c2ecf20Sopenharmony_ci return err; 14838c2ecf20Sopenharmony_ci} 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_ciCOMPAT_SYSCALL_DEFINE3(shmctl, int, shmid, int, cmd, void __user *, uptr) 14868c2ecf20Sopenharmony_ci{ 14878c2ecf20Sopenharmony_ci return compat_ksys_shmctl(shmid, cmd, uptr, IPC_64); 14888c2ecf20Sopenharmony_ci} 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_WANT_COMPAT_IPC_PARSE_VERSION 14918c2ecf20Sopenharmony_cilong compat_ksys_old_shmctl(int shmid, int cmd, void __user *uptr) 14928c2ecf20Sopenharmony_ci{ 14938c2ecf20Sopenharmony_ci int version = compat_ipc_parse_version(&cmd); 14948c2ecf20Sopenharmony_ci 14958c2ecf20Sopenharmony_ci return compat_ksys_shmctl(shmid, cmd, uptr, version); 14968c2ecf20Sopenharmony_ci} 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_ciCOMPAT_SYSCALL_DEFINE3(old_shmctl, int, shmid, int, cmd, void __user *, uptr) 14998c2ecf20Sopenharmony_ci{ 15008c2ecf20Sopenharmony_ci return compat_ksys_old_shmctl(shmid, cmd, uptr); 15018c2ecf20Sopenharmony_ci} 15028c2ecf20Sopenharmony_ci#endif 15038c2ecf20Sopenharmony_ci#endif 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci/* 15068c2ecf20Sopenharmony_ci * Fix shmaddr, allocate descriptor, map shm, add attach descriptor to lists. 15078c2ecf20Sopenharmony_ci * 15088c2ecf20Sopenharmony_ci * NOTE! Despite the name, this is NOT a direct system call entrypoint. The 15098c2ecf20Sopenharmony_ci * "raddr" thing points to kernel space, and there has to be a wrapper around 15108c2ecf20Sopenharmony_ci * this. 15118c2ecf20Sopenharmony_ci */ 15128c2ecf20Sopenharmony_cilong do_shmat(int shmid, char __user *shmaddr, int shmflg, 15138c2ecf20Sopenharmony_ci ulong *raddr, unsigned long shmlba) 15148c2ecf20Sopenharmony_ci{ 15158c2ecf20Sopenharmony_ci struct shmid_kernel *shp; 15168c2ecf20Sopenharmony_ci unsigned long addr = (unsigned long)shmaddr; 15178c2ecf20Sopenharmony_ci unsigned long size; 15188c2ecf20Sopenharmony_ci struct file *file, *base; 15198c2ecf20Sopenharmony_ci int err; 15208c2ecf20Sopenharmony_ci unsigned long flags = MAP_SHARED; 15218c2ecf20Sopenharmony_ci unsigned long prot; 15228c2ecf20Sopenharmony_ci int acc_mode; 15238c2ecf20Sopenharmony_ci struct ipc_namespace *ns; 15248c2ecf20Sopenharmony_ci struct shm_file_data *sfd; 15258c2ecf20Sopenharmony_ci int f_flags; 15268c2ecf20Sopenharmony_ci unsigned long populate = 0; 15278c2ecf20Sopenharmony_ci 15288c2ecf20Sopenharmony_ci err = -EINVAL; 15298c2ecf20Sopenharmony_ci if (shmid < 0) 15308c2ecf20Sopenharmony_ci goto out; 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_ci if (addr) { 15338c2ecf20Sopenharmony_ci if (addr & (shmlba - 1)) { 15348c2ecf20Sopenharmony_ci if (shmflg & SHM_RND) { 15358c2ecf20Sopenharmony_ci addr &= ~(shmlba - 1); /* round down */ 15368c2ecf20Sopenharmony_ci 15378c2ecf20Sopenharmony_ci /* 15388c2ecf20Sopenharmony_ci * Ensure that the round-down is non-nil 15398c2ecf20Sopenharmony_ci * when remapping. This can happen for 15408c2ecf20Sopenharmony_ci * cases when addr < shmlba. 15418c2ecf20Sopenharmony_ci */ 15428c2ecf20Sopenharmony_ci if (!addr && (shmflg & SHM_REMAP)) 15438c2ecf20Sopenharmony_ci goto out; 15448c2ecf20Sopenharmony_ci } else 15458c2ecf20Sopenharmony_ci#ifndef __ARCH_FORCE_SHMLBA 15468c2ecf20Sopenharmony_ci if (addr & ~PAGE_MASK) 15478c2ecf20Sopenharmony_ci#endif 15488c2ecf20Sopenharmony_ci goto out; 15498c2ecf20Sopenharmony_ci } 15508c2ecf20Sopenharmony_ci 15518c2ecf20Sopenharmony_ci flags |= MAP_FIXED; 15528c2ecf20Sopenharmony_ci } else if ((shmflg & SHM_REMAP)) 15538c2ecf20Sopenharmony_ci goto out; 15548c2ecf20Sopenharmony_ci 15558c2ecf20Sopenharmony_ci if (shmflg & SHM_RDONLY) { 15568c2ecf20Sopenharmony_ci prot = PROT_READ; 15578c2ecf20Sopenharmony_ci acc_mode = S_IRUGO; 15588c2ecf20Sopenharmony_ci f_flags = O_RDONLY; 15598c2ecf20Sopenharmony_ci } else { 15608c2ecf20Sopenharmony_ci prot = PROT_READ | PROT_WRITE; 15618c2ecf20Sopenharmony_ci acc_mode = S_IRUGO | S_IWUGO; 15628c2ecf20Sopenharmony_ci f_flags = O_RDWR; 15638c2ecf20Sopenharmony_ci } 15648c2ecf20Sopenharmony_ci if (shmflg & SHM_EXEC) { 15658c2ecf20Sopenharmony_ci prot |= PROT_EXEC; 15668c2ecf20Sopenharmony_ci acc_mode |= S_IXUGO; 15678c2ecf20Sopenharmony_ci } 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_ci /* 15708c2ecf20Sopenharmony_ci * We cannot rely on the fs check since SYSV IPC does have an 15718c2ecf20Sopenharmony_ci * additional creator id... 15728c2ecf20Sopenharmony_ci */ 15738c2ecf20Sopenharmony_ci ns = current->nsproxy->ipc_ns; 15748c2ecf20Sopenharmony_ci rcu_read_lock(); 15758c2ecf20Sopenharmony_ci shp = shm_obtain_object_check(ns, shmid); 15768c2ecf20Sopenharmony_ci if (IS_ERR(shp)) { 15778c2ecf20Sopenharmony_ci err = PTR_ERR(shp); 15788c2ecf20Sopenharmony_ci goto out_unlock; 15798c2ecf20Sopenharmony_ci } 15808c2ecf20Sopenharmony_ci 15818c2ecf20Sopenharmony_ci err = -EACCES; 15828c2ecf20Sopenharmony_ci if (ipcperms(ns, &shp->shm_perm, acc_mode)) 15838c2ecf20Sopenharmony_ci goto out_unlock; 15848c2ecf20Sopenharmony_ci 15858c2ecf20Sopenharmony_ci err = security_shm_shmat(&shp->shm_perm, shmaddr, shmflg); 15868c2ecf20Sopenharmony_ci if (err) 15878c2ecf20Sopenharmony_ci goto out_unlock; 15888c2ecf20Sopenharmony_ci 15898c2ecf20Sopenharmony_ci ipc_lock_object(&shp->shm_perm); 15908c2ecf20Sopenharmony_ci 15918c2ecf20Sopenharmony_ci /* check if shm_destroy() is tearing down shp */ 15928c2ecf20Sopenharmony_ci if (!ipc_valid_object(&shp->shm_perm)) { 15938c2ecf20Sopenharmony_ci ipc_unlock_object(&shp->shm_perm); 15948c2ecf20Sopenharmony_ci err = -EIDRM; 15958c2ecf20Sopenharmony_ci goto out_unlock; 15968c2ecf20Sopenharmony_ci } 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_ci /* 15998c2ecf20Sopenharmony_ci * We need to take a reference to the real shm file to prevent the 16008c2ecf20Sopenharmony_ci * pointer from becoming stale in cases where the lifetime of the outer 16018c2ecf20Sopenharmony_ci * file extends beyond that of the shm segment. It's not usually 16028c2ecf20Sopenharmony_ci * possible, but it can happen during remap_file_pages() emulation as 16038c2ecf20Sopenharmony_ci * that unmaps the memory, then does ->mmap() via file reference only. 16048c2ecf20Sopenharmony_ci * We'll deny the ->mmap() if the shm segment was since removed, but to 16058c2ecf20Sopenharmony_ci * detect shm ID reuse we need to compare the file pointers. 16068c2ecf20Sopenharmony_ci */ 16078c2ecf20Sopenharmony_ci base = get_file(shp->shm_file); 16088c2ecf20Sopenharmony_ci shp->shm_nattch++; 16098c2ecf20Sopenharmony_ci size = i_size_read(file_inode(base)); 16108c2ecf20Sopenharmony_ci ipc_unlock_object(&shp->shm_perm); 16118c2ecf20Sopenharmony_ci rcu_read_unlock(); 16128c2ecf20Sopenharmony_ci 16138c2ecf20Sopenharmony_ci err = -ENOMEM; 16148c2ecf20Sopenharmony_ci sfd = kzalloc(sizeof(*sfd), GFP_KERNEL); 16158c2ecf20Sopenharmony_ci if (!sfd) { 16168c2ecf20Sopenharmony_ci fput(base); 16178c2ecf20Sopenharmony_ci goto out_nattch; 16188c2ecf20Sopenharmony_ci } 16198c2ecf20Sopenharmony_ci 16208c2ecf20Sopenharmony_ci file = alloc_file_clone(base, f_flags, 16218c2ecf20Sopenharmony_ci is_file_hugepages(base) ? 16228c2ecf20Sopenharmony_ci &shm_file_operations_huge : 16238c2ecf20Sopenharmony_ci &shm_file_operations); 16248c2ecf20Sopenharmony_ci err = PTR_ERR(file); 16258c2ecf20Sopenharmony_ci if (IS_ERR(file)) { 16268c2ecf20Sopenharmony_ci kfree(sfd); 16278c2ecf20Sopenharmony_ci fput(base); 16288c2ecf20Sopenharmony_ci goto out_nattch; 16298c2ecf20Sopenharmony_ci } 16308c2ecf20Sopenharmony_ci 16318c2ecf20Sopenharmony_ci sfd->id = shp->shm_perm.id; 16328c2ecf20Sopenharmony_ci sfd->ns = get_ipc_ns(ns); 16338c2ecf20Sopenharmony_ci sfd->file = base; 16348c2ecf20Sopenharmony_ci sfd->vm_ops = NULL; 16358c2ecf20Sopenharmony_ci file->private_data = sfd; 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_ci err = security_mmap_file(file, prot, flags); 16388c2ecf20Sopenharmony_ci if (err) 16398c2ecf20Sopenharmony_ci goto out_fput; 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_ci if (mmap_write_lock_killable(current->mm)) { 16428c2ecf20Sopenharmony_ci err = -EINTR; 16438c2ecf20Sopenharmony_ci goto out_fput; 16448c2ecf20Sopenharmony_ci } 16458c2ecf20Sopenharmony_ci 16468c2ecf20Sopenharmony_ci if (addr && !(shmflg & SHM_REMAP)) { 16478c2ecf20Sopenharmony_ci err = -EINVAL; 16488c2ecf20Sopenharmony_ci if (addr + size < addr) 16498c2ecf20Sopenharmony_ci goto invalid; 16508c2ecf20Sopenharmony_ci 16518c2ecf20Sopenharmony_ci if (find_vma_intersection(current->mm, addr, addr + size)) 16528c2ecf20Sopenharmony_ci goto invalid; 16538c2ecf20Sopenharmony_ci } 16548c2ecf20Sopenharmony_ci 16558c2ecf20Sopenharmony_ci addr = do_mmap(file, addr, size, prot, flags, 0, &populate, NULL); 16568c2ecf20Sopenharmony_ci *raddr = addr; 16578c2ecf20Sopenharmony_ci err = 0; 16588c2ecf20Sopenharmony_ci if (IS_ERR_VALUE(addr)) 16598c2ecf20Sopenharmony_ci err = (long)addr; 16608c2ecf20Sopenharmony_ciinvalid: 16618c2ecf20Sopenharmony_ci mmap_write_unlock(current->mm); 16628c2ecf20Sopenharmony_ci if (populate) 16638c2ecf20Sopenharmony_ci mm_populate(addr, populate); 16648c2ecf20Sopenharmony_ci 16658c2ecf20Sopenharmony_ciout_fput: 16668c2ecf20Sopenharmony_ci fput(file); 16678c2ecf20Sopenharmony_ci 16688c2ecf20Sopenharmony_ciout_nattch: 16698c2ecf20Sopenharmony_ci down_write(&shm_ids(ns).rwsem); 16708c2ecf20Sopenharmony_ci shp = shm_lock(ns, shmid); 16718c2ecf20Sopenharmony_ci shp->shm_nattch--; 16728c2ecf20Sopenharmony_ci 16738c2ecf20Sopenharmony_ci if (shm_may_destroy(shp)) 16748c2ecf20Sopenharmony_ci shm_destroy(ns, shp); 16758c2ecf20Sopenharmony_ci else 16768c2ecf20Sopenharmony_ci shm_unlock(shp); 16778c2ecf20Sopenharmony_ci up_write(&shm_ids(ns).rwsem); 16788c2ecf20Sopenharmony_ci return err; 16798c2ecf20Sopenharmony_ci 16808c2ecf20Sopenharmony_ciout_unlock: 16818c2ecf20Sopenharmony_ci rcu_read_unlock(); 16828c2ecf20Sopenharmony_ciout: 16838c2ecf20Sopenharmony_ci return err; 16848c2ecf20Sopenharmony_ci} 16858c2ecf20Sopenharmony_ci 16868c2ecf20Sopenharmony_ciSYSCALL_DEFINE3(shmat, int, shmid, char __user *, shmaddr, int, shmflg) 16878c2ecf20Sopenharmony_ci{ 16888c2ecf20Sopenharmony_ci unsigned long ret; 16898c2ecf20Sopenharmony_ci long err; 16908c2ecf20Sopenharmony_ci 16918c2ecf20Sopenharmony_ci err = do_shmat(shmid, shmaddr, shmflg, &ret, SHMLBA); 16928c2ecf20Sopenharmony_ci if (err) 16938c2ecf20Sopenharmony_ci return err; 16948c2ecf20Sopenharmony_ci force_successful_syscall_return(); 16958c2ecf20Sopenharmony_ci return (long)ret; 16968c2ecf20Sopenharmony_ci} 16978c2ecf20Sopenharmony_ci 16988c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 16998c2ecf20Sopenharmony_ci 17008c2ecf20Sopenharmony_ci#ifndef COMPAT_SHMLBA 17018c2ecf20Sopenharmony_ci#define COMPAT_SHMLBA SHMLBA 17028c2ecf20Sopenharmony_ci#endif 17038c2ecf20Sopenharmony_ci 17048c2ecf20Sopenharmony_ciCOMPAT_SYSCALL_DEFINE3(shmat, int, shmid, compat_uptr_t, shmaddr, int, shmflg) 17058c2ecf20Sopenharmony_ci{ 17068c2ecf20Sopenharmony_ci unsigned long ret; 17078c2ecf20Sopenharmony_ci long err; 17088c2ecf20Sopenharmony_ci 17098c2ecf20Sopenharmony_ci err = do_shmat(shmid, compat_ptr(shmaddr), shmflg, &ret, COMPAT_SHMLBA); 17108c2ecf20Sopenharmony_ci if (err) 17118c2ecf20Sopenharmony_ci return err; 17128c2ecf20Sopenharmony_ci force_successful_syscall_return(); 17138c2ecf20Sopenharmony_ci return (long)ret; 17148c2ecf20Sopenharmony_ci} 17158c2ecf20Sopenharmony_ci#endif 17168c2ecf20Sopenharmony_ci 17178c2ecf20Sopenharmony_ci/* 17188c2ecf20Sopenharmony_ci * detach and kill segment if marked destroyed. 17198c2ecf20Sopenharmony_ci * The work is done in shm_close. 17208c2ecf20Sopenharmony_ci */ 17218c2ecf20Sopenharmony_cilong ksys_shmdt(char __user *shmaddr) 17228c2ecf20Sopenharmony_ci{ 17238c2ecf20Sopenharmony_ci struct mm_struct *mm = current->mm; 17248c2ecf20Sopenharmony_ci struct vm_area_struct *vma; 17258c2ecf20Sopenharmony_ci unsigned long addr = (unsigned long)shmaddr; 17268c2ecf20Sopenharmony_ci int retval = -EINVAL; 17278c2ecf20Sopenharmony_ci#ifdef CONFIG_MMU 17288c2ecf20Sopenharmony_ci loff_t size = 0; 17298c2ecf20Sopenharmony_ci struct file *file; 17308c2ecf20Sopenharmony_ci struct vm_area_struct *next; 17318c2ecf20Sopenharmony_ci#endif 17328c2ecf20Sopenharmony_ci 17338c2ecf20Sopenharmony_ci if (addr & ~PAGE_MASK) 17348c2ecf20Sopenharmony_ci return retval; 17358c2ecf20Sopenharmony_ci 17368c2ecf20Sopenharmony_ci if (mmap_write_lock_killable(mm)) 17378c2ecf20Sopenharmony_ci return -EINTR; 17388c2ecf20Sopenharmony_ci 17398c2ecf20Sopenharmony_ci /* 17408c2ecf20Sopenharmony_ci * This function tries to be smart and unmap shm segments that 17418c2ecf20Sopenharmony_ci * were modified by partial mlock or munmap calls: 17428c2ecf20Sopenharmony_ci * - It first determines the size of the shm segment that should be 17438c2ecf20Sopenharmony_ci * unmapped: It searches for a vma that is backed by shm and that 17448c2ecf20Sopenharmony_ci * started at address shmaddr. It records it's size and then unmaps 17458c2ecf20Sopenharmony_ci * it. 17468c2ecf20Sopenharmony_ci * - Then it unmaps all shm vmas that started at shmaddr and that 17478c2ecf20Sopenharmony_ci * are within the initially determined size and that are from the 17488c2ecf20Sopenharmony_ci * same shm segment from which we determined the size. 17498c2ecf20Sopenharmony_ci * Errors from do_munmap are ignored: the function only fails if 17508c2ecf20Sopenharmony_ci * it's called with invalid parameters or if it's called to unmap 17518c2ecf20Sopenharmony_ci * a part of a vma. Both calls in this function are for full vmas, 17528c2ecf20Sopenharmony_ci * the parameters are directly copied from the vma itself and always 17538c2ecf20Sopenharmony_ci * valid - therefore do_munmap cannot fail. (famous last words?) 17548c2ecf20Sopenharmony_ci */ 17558c2ecf20Sopenharmony_ci /* 17568c2ecf20Sopenharmony_ci * If it had been mremap()'d, the starting address would not 17578c2ecf20Sopenharmony_ci * match the usual checks anyway. So assume all vma's are 17588c2ecf20Sopenharmony_ci * above the starting address given. 17598c2ecf20Sopenharmony_ci */ 17608c2ecf20Sopenharmony_ci vma = find_vma(mm, addr); 17618c2ecf20Sopenharmony_ci 17628c2ecf20Sopenharmony_ci#ifdef CONFIG_MMU 17638c2ecf20Sopenharmony_ci while (vma) { 17648c2ecf20Sopenharmony_ci next = vma->vm_next; 17658c2ecf20Sopenharmony_ci 17668c2ecf20Sopenharmony_ci /* 17678c2ecf20Sopenharmony_ci * Check if the starting address would match, i.e. it's 17688c2ecf20Sopenharmony_ci * a fragment created by mprotect() and/or munmap(), or it 17698c2ecf20Sopenharmony_ci * otherwise it starts at this address with no hassles. 17708c2ecf20Sopenharmony_ci */ 17718c2ecf20Sopenharmony_ci if ((vma->vm_ops == &shm_vm_ops) && 17728c2ecf20Sopenharmony_ci (vma->vm_start - addr)/PAGE_SIZE == vma->vm_pgoff) { 17738c2ecf20Sopenharmony_ci 17748c2ecf20Sopenharmony_ci /* 17758c2ecf20Sopenharmony_ci * Record the file of the shm segment being 17768c2ecf20Sopenharmony_ci * unmapped. With mremap(), someone could place 17778c2ecf20Sopenharmony_ci * page from another segment but with equal offsets 17788c2ecf20Sopenharmony_ci * in the range we are unmapping. 17798c2ecf20Sopenharmony_ci */ 17808c2ecf20Sopenharmony_ci file = vma->vm_file; 17818c2ecf20Sopenharmony_ci size = i_size_read(file_inode(vma->vm_file)); 17828c2ecf20Sopenharmony_ci do_munmap(mm, vma->vm_start, vma->vm_end - vma->vm_start, NULL); 17838c2ecf20Sopenharmony_ci /* 17848c2ecf20Sopenharmony_ci * We discovered the size of the shm segment, so 17858c2ecf20Sopenharmony_ci * break out of here and fall through to the next 17868c2ecf20Sopenharmony_ci * loop that uses the size information to stop 17878c2ecf20Sopenharmony_ci * searching for matching vma's. 17888c2ecf20Sopenharmony_ci */ 17898c2ecf20Sopenharmony_ci retval = 0; 17908c2ecf20Sopenharmony_ci vma = next; 17918c2ecf20Sopenharmony_ci break; 17928c2ecf20Sopenharmony_ci } 17938c2ecf20Sopenharmony_ci vma = next; 17948c2ecf20Sopenharmony_ci } 17958c2ecf20Sopenharmony_ci 17968c2ecf20Sopenharmony_ci /* 17978c2ecf20Sopenharmony_ci * We need look no further than the maximum address a fragment 17988c2ecf20Sopenharmony_ci * could possibly have landed at. Also cast things to loff_t to 17998c2ecf20Sopenharmony_ci * prevent overflows and make comparisons vs. equal-width types. 18008c2ecf20Sopenharmony_ci */ 18018c2ecf20Sopenharmony_ci size = PAGE_ALIGN(size); 18028c2ecf20Sopenharmony_ci while (vma && (loff_t)(vma->vm_end - addr) <= size) { 18038c2ecf20Sopenharmony_ci next = vma->vm_next; 18048c2ecf20Sopenharmony_ci 18058c2ecf20Sopenharmony_ci /* finding a matching vma now does not alter retval */ 18068c2ecf20Sopenharmony_ci if ((vma->vm_ops == &shm_vm_ops) && 18078c2ecf20Sopenharmony_ci ((vma->vm_start - addr)/PAGE_SIZE == vma->vm_pgoff) && 18088c2ecf20Sopenharmony_ci (vma->vm_file == file)) 18098c2ecf20Sopenharmony_ci do_munmap(mm, vma->vm_start, vma->vm_end - vma->vm_start, NULL); 18108c2ecf20Sopenharmony_ci vma = next; 18118c2ecf20Sopenharmony_ci } 18128c2ecf20Sopenharmony_ci 18138c2ecf20Sopenharmony_ci#else /* CONFIG_MMU */ 18148c2ecf20Sopenharmony_ci /* under NOMMU conditions, the exact address to be destroyed must be 18158c2ecf20Sopenharmony_ci * given 18168c2ecf20Sopenharmony_ci */ 18178c2ecf20Sopenharmony_ci if (vma && vma->vm_start == addr && vma->vm_ops == &shm_vm_ops) { 18188c2ecf20Sopenharmony_ci do_munmap(mm, vma->vm_start, vma->vm_end - vma->vm_start, NULL); 18198c2ecf20Sopenharmony_ci retval = 0; 18208c2ecf20Sopenharmony_ci } 18218c2ecf20Sopenharmony_ci 18228c2ecf20Sopenharmony_ci#endif 18238c2ecf20Sopenharmony_ci 18248c2ecf20Sopenharmony_ci mmap_write_unlock(mm); 18258c2ecf20Sopenharmony_ci return retval; 18268c2ecf20Sopenharmony_ci} 18278c2ecf20Sopenharmony_ci 18288c2ecf20Sopenharmony_ciSYSCALL_DEFINE1(shmdt, char __user *, shmaddr) 18298c2ecf20Sopenharmony_ci{ 18308c2ecf20Sopenharmony_ci return ksys_shmdt(shmaddr); 18318c2ecf20Sopenharmony_ci} 18328c2ecf20Sopenharmony_ci 18338c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_FS 18348c2ecf20Sopenharmony_cistatic int sysvipc_shm_proc_show(struct seq_file *s, void *it) 18358c2ecf20Sopenharmony_ci{ 18368c2ecf20Sopenharmony_ci struct pid_namespace *pid_ns = ipc_seq_pid_ns(s); 18378c2ecf20Sopenharmony_ci struct user_namespace *user_ns = seq_user_ns(s); 18388c2ecf20Sopenharmony_ci struct kern_ipc_perm *ipcp = it; 18398c2ecf20Sopenharmony_ci struct shmid_kernel *shp; 18408c2ecf20Sopenharmony_ci unsigned long rss = 0, swp = 0; 18418c2ecf20Sopenharmony_ci 18428c2ecf20Sopenharmony_ci shp = container_of(ipcp, struct shmid_kernel, shm_perm); 18438c2ecf20Sopenharmony_ci shm_add_rss_swap(shp, &rss, &swp); 18448c2ecf20Sopenharmony_ci 18458c2ecf20Sopenharmony_ci#if BITS_PER_LONG <= 32 18468c2ecf20Sopenharmony_ci#define SIZE_SPEC "%10lu" 18478c2ecf20Sopenharmony_ci#else 18488c2ecf20Sopenharmony_ci#define SIZE_SPEC "%21lu" 18498c2ecf20Sopenharmony_ci#endif 18508c2ecf20Sopenharmony_ci 18518c2ecf20Sopenharmony_ci seq_printf(s, 18528c2ecf20Sopenharmony_ci "%10d %10d %4o " SIZE_SPEC " %5u %5u " 18538c2ecf20Sopenharmony_ci "%5lu %5u %5u %5u %5u %10llu %10llu %10llu " 18548c2ecf20Sopenharmony_ci SIZE_SPEC " " SIZE_SPEC "\n", 18558c2ecf20Sopenharmony_ci shp->shm_perm.key, 18568c2ecf20Sopenharmony_ci shp->shm_perm.id, 18578c2ecf20Sopenharmony_ci shp->shm_perm.mode, 18588c2ecf20Sopenharmony_ci shp->shm_segsz, 18598c2ecf20Sopenharmony_ci pid_nr_ns(shp->shm_cprid, pid_ns), 18608c2ecf20Sopenharmony_ci pid_nr_ns(shp->shm_lprid, pid_ns), 18618c2ecf20Sopenharmony_ci shp->shm_nattch, 18628c2ecf20Sopenharmony_ci from_kuid_munged(user_ns, shp->shm_perm.uid), 18638c2ecf20Sopenharmony_ci from_kgid_munged(user_ns, shp->shm_perm.gid), 18648c2ecf20Sopenharmony_ci from_kuid_munged(user_ns, shp->shm_perm.cuid), 18658c2ecf20Sopenharmony_ci from_kgid_munged(user_ns, shp->shm_perm.cgid), 18668c2ecf20Sopenharmony_ci shp->shm_atim, 18678c2ecf20Sopenharmony_ci shp->shm_dtim, 18688c2ecf20Sopenharmony_ci shp->shm_ctim, 18698c2ecf20Sopenharmony_ci rss * PAGE_SIZE, 18708c2ecf20Sopenharmony_ci swp * PAGE_SIZE); 18718c2ecf20Sopenharmony_ci 18728c2ecf20Sopenharmony_ci return 0; 18738c2ecf20Sopenharmony_ci} 18748c2ecf20Sopenharmony_ci#endif 1875