Skip to content

Commit bdec014

Browse files
arndbRussell King (Oracle)
authored andcommitted
ARM: 9114/1: oabi-compat: rework sys_semtimedop emulation
sys_oabi_semtimedop() is one of the last users of set_fs() on Arm. To remove this one, expose the internal code of the actual implementation that operates on a kernel pointer and call it directly after copying. There should be no measurable impact on the normal execution of this function, and it makes the overly long function a little shorter, which may help readability. While reworking the oabi version, make it behave a little more like the native one, using kvmalloc_array() and restructure the code flow in a similar way. The naming of __do_semtimedop() is not very good, I hope someone can come up with a better name. One regression was spotted by kernel test robot <[email protected]> and fixed before the first mailing list submission. Acked-by: Christoph Hellwig <[email protected]> Signed-off-by: Arnd Bergmann <[email protected]> Signed-off-by: Russell King (Oracle) <[email protected]>
1 parent 249dbe7 commit bdec014

File tree

3 files changed

+99
-48
lines changed

3 files changed

+99
-48
lines changed

arch/arm/kernel/sys_oabi-compat.c

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
#include <linux/socket.h>
8181
#include <linux/net.h>
8282
#include <linux/ipc.h>
83+
#include <linux/ipc_namespace.h>
8384
#include <linux/uaccess.h>
8485
#include <linux/slab.h>
8586

@@ -302,46 +303,52 @@ struct oabi_sembuf {
302303
unsigned short __pad;
303304
};
304305

306+
#define sc_semopm sem_ctls[2]
307+
308+
#ifdef CONFIG_SYSVIPC
305309
asmlinkage long sys_oabi_semtimedop(int semid,
306310
struct oabi_sembuf __user *tsops,
307311
unsigned nsops,
308312
const struct old_timespec32 __user *timeout)
309313
{
314+
struct ipc_namespace *ns;
310315
struct sembuf *sops;
311-
struct old_timespec32 local_timeout;
312316
long err;
313317
int i;
314318

319+
ns = current->nsproxy->ipc_ns;
320+
if (nsops > ns->sc_semopm)
321+
return -E2BIG;
315322
if (nsops < 1 || nsops > SEMOPM)
316323
return -EINVAL;
317-
if (!access_ok(tsops, sizeof(*tsops) * nsops))
318-
return -EFAULT;
319-
sops = kmalloc_array(nsops, sizeof(*sops), GFP_KERNEL);
324+
sops = kvmalloc_array(nsops, sizeof(*sops), GFP_KERNEL);
320325
if (!sops)
321326
return -ENOMEM;
322327
err = 0;
323328
for (i = 0; i < nsops; i++) {
324329
struct oabi_sembuf osb;
325-
err |= __copy_from_user(&osb, tsops, sizeof(osb));
330+
err |= copy_from_user(&osb, tsops, sizeof(osb));
326331
sops[i].sem_num = osb.sem_num;
327332
sops[i].sem_op = osb.sem_op;
328333
sops[i].sem_flg = osb.sem_flg;
329334
tsops++;
330335
}
331-
if (timeout) {
332-
/* copy this as well before changing domain protection */
333-
err |= copy_from_user(&local_timeout, timeout, sizeof(*timeout));
334-
timeout = &local_timeout;
335-
}
336336
if (err) {
337337
err = -EFAULT;
338-
} else {
339-
mm_segment_t fs = get_fs();
340-
set_fs(KERNEL_DS);
341-
err = sys_semtimedop_time32(semid, sops, nsops, timeout);
342-
set_fs(fs);
338+
goto out;
339+
}
340+
341+
if (timeout) {
342+
struct timespec64 ts;
343+
err = get_old_timespec32(&ts, timeout);
344+
if (err)
345+
goto out;
346+
err = __do_semtimedop(semid, sops, nsops, &ts, ns);
347+
goto out;
343348
}
344-
kfree(sops);
349+
err = __do_semtimedop(semid, sops, nsops, NULL, ns);
350+
out:
351+
kvfree(sops);
345352
return err;
346353
}
347354

@@ -368,6 +375,27 @@ asmlinkage int sys_oabi_ipc(uint call, int first, int second, int third,
368375
return sys_ipc(call, first, second, third, ptr, fifth);
369376
}
370377
}
378+
#else
379+
asmlinkage long sys_oabi_semtimedop(int semid,
380+
struct oabi_sembuf __user *tsops,
381+
unsigned nsops,
382+
const struct old_timespec32 __user *timeout)
383+
{
384+
return -ENOSYS;
385+
}
386+
387+
asmlinkage long sys_oabi_semop(int semid, struct oabi_sembuf __user *tsops,
388+
unsigned nsops)
389+
{
390+
return -ENOSYS;
391+
}
392+
393+
asmlinkage int sys_oabi_ipc(uint call, int first, int second, int third,
394+
void __user *ptr, long fifth)
395+
{
396+
return -ENOSYS;
397+
}
398+
#endif
371399

372400
asmlinkage long sys_oabi_bind(int fd, struct sockaddr __user *addr, int addrlen)
373401
{

include/linux/syscalls.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1373,6 +1373,9 @@ long ksys_old_shmctl(int shmid, int cmd, struct shmid_ds __user *buf);
13731373
long compat_ksys_semtimedop(int semid, struct sembuf __user *tsems,
13741374
unsigned int nsops,
13751375
const struct old_timespec32 __user *timeout);
1376+
long __do_semtimedop(int semid, struct sembuf *tsems, unsigned int nsops,
1377+
const struct timespec64 *timeout,
1378+
struct ipc_namespace *ns);
13761379

13771380
int __sys_getsockopt(int fd, int level, int optname, char __user *optval,
13781381
int __user *optlen);

ipc/sem.c

Lines changed: 52 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1984,46 +1984,34 @@ static struct sem_undo *find_alloc_undo(struct ipc_namespace *ns, int semid)
19841984
return un;
19851985
}
19861986

1987-
static long do_semtimedop(int semid, struct sembuf __user *tsops,
1988-
unsigned nsops, const struct timespec64 *timeout)
1987+
long __do_semtimedop(int semid, struct sembuf *sops,
1988+
unsigned nsops, const struct timespec64 *timeout,
1989+
struct ipc_namespace *ns)
19891990
{
19901991
int error = -EINVAL;
19911992
struct sem_array *sma;
1992-
struct sembuf fast_sops[SEMOPM_FAST];
1993-
struct sembuf *sops = fast_sops, *sop;
1993+
struct sembuf *sop;
19941994
struct sem_undo *un;
19951995
int max, locknum;
19961996
bool undos = false, alter = false, dupsop = false;
19971997
struct sem_queue queue;
19981998
unsigned long dup = 0, jiffies_left = 0;
1999-
struct ipc_namespace *ns;
2000-
2001-
ns = current->nsproxy->ipc_ns;
20021999

20032000
if (nsops < 1 || semid < 0)
20042001
return -EINVAL;
20052002
if (nsops > ns->sc_semopm)
20062003
return -E2BIG;
2007-
if (nsops > SEMOPM_FAST) {
2008-
sops = kvmalloc_array(nsops, sizeof(*sops), GFP_KERNEL);
2009-
if (sops == NULL)
2010-
return -ENOMEM;
2011-
}
2012-
2013-
if (copy_from_user(sops, tsops, nsops * sizeof(*tsops))) {
2014-
error = -EFAULT;
2015-
goto out_free;
2016-
}
20172004

20182005
if (timeout) {
20192006
if (timeout->tv_sec < 0 || timeout->tv_nsec < 0 ||
20202007
timeout->tv_nsec >= 1000000000L) {
20212008
error = -EINVAL;
2022-
goto out_free;
2009+
goto out;
20232010
}
20242011
jiffies_left = timespec64_to_jiffies(timeout);
20252012
}
20262013

2014+
20272015
max = 0;
20282016
for (sop = sops; sop < sops + nsops; sop++) {
20292017
unsigned long mask = 1ULL << ((sop->sem_num) % BITS_PER_LONG);
@@ -2052,7 +2040,7 @@ static long do_semtimedop(int semid, struct sembuf __user *tsops,
20522040
un = find_alloc_undo(ns, semid);
20532041
if (IS_ERR(un)) {
20542042
error = PTR_ERR(un);
2055-
goto out_free;
2043+
goto out;
20562044
}
20572045
} else {
20582046
un = NULL;
@@ -2063,25 +2051,25 @@ static long do_semtimedop(int semid, struct sembuf __user *tsops,
20632051
if (IS_ERR(sma)) {
20642052
rcu_read_unlock();
20652053
error = PTR_ERR(sma);
2066-
goto out_free;
2054+
goto out;
20672055
}
20682056

20692057
error = -EFBIG;
20702058
if (max >= sma->sem_nsems) {
20712059
rcu_read_unlock();
2072-
goto out_free;
2060+
goto out;
20732061
}
20742062

20752063
error = -EACCES;
20762064
if (ipcperms(ns, &sma->sem_perm, alter ? S_IWUGO : S_IRUGO)) {
20772065
rcu_read_unlock();
2078-
goto out_free;
2066+
goto out;
20792067
}
20802068

20812069
error = security_sem_semop(&sma->sem_perm, sops, nsops, alter);
20822070
if (error) {
20832071
rcu_read_unlock();
2084-
goto out_free;
2072+
goto out;
20852073
}
20862074

20872075
error = -EIDRM;
@@ -2095,7 +2083,7 @@ static long do_semtimedop(int semid, struct sembuf __user *tsops,
20952083
* entangled here and why it's RMID race safe on comments at sem_lock()
20962084
*/
20972085
if (!ipc_valid_object(&sma->sem_perm))
2098-
goto out_unlock_free;
2086+
goto out_unlock;
20992087
/*
21002088
* semid identifiers are not unique - find_alloc_undo may have
21012089
* allocated an undo structure, it was invalidated by an RMID
@@ -2104,7 +2092,7 @@ static long do_semtimedop(int semid, struct sembuf __user *tsops,
21042092
* "un" itself is guaranteed by rcu.
21052093
*/
21062094
if (un && un->semid == -1)
2107-
goto out_unlock_free;
2095+
goto out_unlock;
21082096

21092097
queue.sops = sops;
21102098
queue.nsops = nsops;
@@ -2130,10 +2118,10 @@ static long do_semtimedop(int semid, struct sembuf __user *tsops,
21302118
rcu_read_unlock();
21312119
wake_up_q(&wake_q);
21322120

2133-
goto out_free;
2121+
goto out;
21342122
}
21352123
if (error < 0) /* non-blocking error path */
2136-
goto out_unlock_free;
2124+
goto out_unlock;
21372125

21382126
/*
21392127
* We need to sleep on this operation, so we put the current
@@ -2198,14 +2186,14 @@ static long do_semtimedop(int semid, struct sembuf __user *tsops,
21982186
if (error != -EINTR) {
21992187
/* see SEM_BARRIER_2 for purpose/pairing */
22002188
smp_acquire__after_ctrl_dep();
2201-
goto out_free;
2189+
goto out;
22022190
}
22032191

22042192
rcu_read_lock();
22052193
locknum = sem_lock(sma, sops, nsops);
22062194

22072195
if (!ipc_valid_object(&sma->sem_perm))
2208-
goto out_unlock_free;
2196+
goto out_unlock;
22092197

22102198
/*
22112199
* No necessity for any barrier: We are protect by sem_lock()
@@ -2217,7 +2205,7 @@ static long do_semtimedop(int semid, struct sembuf __user *tsops,
22172205
* Leave without unlink_queue(), but with sem_unlock().
22182206
*/
22192207
if (error != -EINTR)
2220-
goto out_unlock_free;
2208+
goto out_unlock;
22212209

22222210
/*
22232211
* If an interrupt occurred we have to clean up the queue.
@@ -2228,13 +2216,45 @@ static long do_semtimedop(int semid, struct sembuf __user *tsops,
22282216

22292217
unlink_queue(sma, &queue);
22302218

2231-
out_unlock_free:
2219+
out_unlock:
22322220
sem_unlock(sma, locknum);
22332221
rcu_read_unlock();
2222+
out:
2223+
return error;
2224+
}
2225+
2226+
static long do_semtimedop(int semid, struct sembuf __user *tsops,
2227+
unsigned nsops, const struct timespec64 *timeout)
2228+
{
2229+
struct sembuf fast_sops[SEMOPM_FAST];
2230+
struct sembuf *sops = fast_sops;
2231+
struct ipc_namespace *ns;
2232+
int ret;
2233+
2234+
ns = current->nsproxy->ipc_ns;
2235+
if (nsops > ns->sc_semopm)
2236+
return -E2BIG;
2237+
if (nsops < 1)
2238+
return -EINVAL;
2239+
2240+
if (nsops > SEMOPM_FAST) {
2241+
sops = kvmalloc_array(nsops, sizeof(*sops), GFP_KERNEL);
2242+
if (sops == NULL)
2243+
return -ENOMEM;
2244+
}
2245+
2246+
if (copy_from_user(sops, tsops, nsops * sizeof(*tsops))) {
2247+
ret = -EFAULT;
2248+
goto out_free;
2249+
}
2250+
2251+
ret = __do_semtimedop(semid, sops, nsops, timeout, ns);
2252+
22342253
out_free:
22352254
if (sops != fast_sops)
22362255
kvfree(sops);
2237-
return error;
2256+
2257+
return ret;
22382258
}
22392259

22402260
long ksys_semtimedop(int semid, struct sembuf __user *tsops,

0 commit comments

Comments
 (0)