xref: /openbmc/linux/ipc/syscall.c (revision e0f6d1a5)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * sys_ipc() is the old de-multiplexer for the SysV IPC calls.
4  *
5  * This is really horribly ugly, and new architectures should just wire up
6  * the individual syscalls instead.
7  */
8 #include <linux/unistd.h>
9 #include <linux/syscalls.h>
10 #include <linux/security.h>
11 #include <linux/ipc_namespace.h>
12 #include "util.h"
13 
14 #ifdef __ARCH_WANT_SYS_IPC
15 #include <linux/errno.h>
16 #include <linux/ipc.h>
17 #include <linux/shm.h>
18 #include <linux/uaccess.h>
19 
20 SYSCALL_DEFINE6(ipc, unsigned int, call, int, first, unsigned long, second,
21 		unsigned long, third, void __user *, ptr, long, fifth)
22 {
23 	int version, ret;
24 
25 	version = call >> 16; /* hack for backward compatibility */
26 	call &= 0xffff;
27 
28 	switch (call) {
29 	case SEMOP:
30 		return ksys_semtimedop(first, (struct sembuf __user *)ptr,
31 				       second, NULL);
32 	case SEMTIMEDOP:
33 		return ksys_semtimedop(first, (struct sembuf __user *)ptr,
34 				       second,
35 				       (const struct timespec __user *)fifth);
36 
37 	case SEMGET:
38 		return ksys_semget(first, second, third);
39 	case SEMCTL: {
40 		unsigned long arg;
41 		if (!ptr)
42 			return -EINVAL;
43 		if (get_user(arg, (unsigned long __user *) ptr))
44 			return -EFAULT;
45 		return ksys_semctl(first, second, third, arg);
46 	}
47 
48 	case MSGSND:
49 		return ksys_msgsnd(first, (struct msgbuf __user *) ptr,
50 				  second, third);
51 	case MSGRCV:
52 		switch (version) {
53 		case 0: {
54 			struct ipc_kludge tmp;
55 			if (!ptr)
56 				return -EINVAL;
57 
58 			if (copy_from_user(&tmp,
59 					   (struct ipc_kludge __user *) ptr,
60 					   sizeof(tmp)))
61 				return -EFAULT;
62 			return ksys_msgrcv(first, tmp.msgp, second,
63 					   tmp.msgtyp, third);
64 		}
65 		default:
66 			return ksys_msgrcv(first,
67 					   (struct msgbuf __user *) ptr,
68 					   second, fifth, third);
69 		}
70 	case MSGGET:
71 		return ksys_msgget((key_t) first, second);
72 	case MSGCTL:
73 		return ksys_msgctl(first, second,
74 				   (struct msqid_ds __user *)ptr);
75 
76 	case SHMAT:
77 		switch (version) {
78 		default: {
79 			unsigned long raddr;
80 			ret = do_shmat(first, (char __user *)ptr,
81 				       second, &raddr, SHMLBA);
82 			if (ret)
83 				return ret;
84 			return put_user(raddr, (unsigned long __user *) third);
85 		}
86 		case 1:
87 			/*
88 			 * This was the entry point for kernel-originating calls
89 			 * from iBCS2 in 2.2 days.
90 			 */
91 			return -EINVAL;
92 		}
93 	case SHMDT:
94 		return ksys_shmdt((char __user *)ptr);
95 	case SHMGET:
96 		return ksys_shmget(first, second, third);
97 	case SHMCTL:
98 		return ksys_shmctl(first, second,
99 				   (struct shmid_ds __user *) ptr);
100 	default:
101 		return -ENOSYS;
102 	}
103 }
104 #endif
105 
106 #ifdef CONFIG_COMPAT
107 #include <linux/compat.h>
108 
109 #ifndef COMPAT_SHMLBA
110 #define COMPAT_SHMLBA	SHMLBA
111 #endif
112 
113 struct compat_ipc_kludge {
114 	compat_uptr_t msgp;
115 	compat_long_t msgtyp;
116 };
117 
118 #ifdef CONFIG_ARCH_WANT_OLD_COMPAT_IPC
119 COMPAT_SYSCALL_DEFINE6(ipc, u32, call, int, first, int, second,
120 	u32, third, compat_uptr_t, ptr, u32, fifth)
121 {
122 	int version;
123 	u32 pad;
124 
125 	version = call >> 16; /* hack for backward compatibility */
126 	call &= 0xffff;
127 
128 	switch (call) {
129 	case SEMOP:
130 		/* struct sembuf is the same on 32 and 64bit :)) */
131 		return ksys_semtimedop(first, compat_ptr(ptr), second, NULL);
132 	case SEMTIMEDOP:
133 		return compat_ksys_semtimedop(first, compat_ptr(ptr), second,
134 						compat_ptr(fifth));
135 	case SEMGET:
136 		return ksys_semget(first, second, third);
137 	case SEMCTL:
138 		if (!ptr)
139 			return -EINVAL;
140 		if (get_user(pad, (u32 __user *) compat_ptr(ptr)))
141 			return -EFAULT;
142 		return compat_ksys_semctl(first, second, third, pad);
143 
144 	case MSGSND:
145 		return compat_ksys_msgsnd(first, ptr, second, third);
146 
147 	case MSGRCV: {
148 		void __user *uptr = compat_ptr(ptr);
149 
150 		if (first < 0 || second < 0)
151 			return -EINVAL;
152 
153 		if (!version) {
154 			struct compat_ipc_kludge ipck;
155 			if (!uptr)
156 				return -EINVAL;
157 			if (copy_from_user(&ipck, uptr, sizeof(ipck)))
158 				return -EFAULT;
159 			return compat_ksys_msgrcv(first, ipck.msgp, second,
160 						 ipck.msgtyp, third);
161 		}
162 		return compat_ksys_msgrcv(first, ptr, second, fifth, third);
163 	}
164 	case MSGGET:
165 		return ksys_msgget(first, second);
166 	case MSGCTL:
167 		return compat_ksys_msgctl(first, second, compat_ptr(ptr));
168 
169 	case SHMAT: {
170 		int err;
171 		unsigned long raddr;
172 
173 		if (version == 1)
174 			return -EINVAL;
175 		err = do_shmat(first, compat_ptr(ptr), second, &raddr,
176 			       COMPAT_SHMLBA);
177 		if (err < 0)
178 			return err;
179 		return put_user(raddr, (compat_ulong_t __user *)compat_ptr(third));
180 	}
181 	case SHMDT:
182 		return ksys_shmdt(compat_ptr(ptr));
183 	case SHMGET:
184 		return ksys_shmget(first, (unsigned int)second, third);
185 	case SHMCTL:
186 		return compat_ksys_shmctl(first, second, compat_ptr(ptr));
187 	}
188 
189 	return -ENOSYS;
190 }
191 #endif
192 #endif
193