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, &current->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