Skip to content

Commit c61284e

Browse files
manfred-colorfutorvalds
authored andcommitted
ipc/sem.c: bugfix for semop() not reporting successful operation
The last change to improve the scalability moved the actual wake-up out of the section that is protected by spin_lock(sma->sem_perm.lock). This means that IN_WAKEUP can be in queue.status even when the spinlock is acquired by the current task. Thus the same loop that is performed when queue.status is read without the spinlock acquired must be performed when the spinlock is acquired. Thanks to [email protected] for noticing lack of the memory barrier. Addresses https://bugzilla.kernel.org/show_bug.cgi?id=16255 [[email protected]: clean up kerneldoc, checkpatch warning and whitespace] Signed-off-by: Manfred Spraul <[email protected]> Reported-by: Luca Tettamanti <[email protected]> Tested-by: Luca Tettamanti <[email protected]> Reported-by: Christoph Lameter <[email protected]> Cc: Maciej Rutecki <[email protected]> Cc: KAMEZAWA Hiroyuki <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 19f0f0a commit c61284e

File tree

1 file changed

+39
-7
lines changed

1 file changed

+39
-7
lines changed

ipc/sem.c

+39-7
Original file line numberDiff line numberDiff line change
@@ -1256,6 +1256,33 @@ static struct sem_undo *find_alloc_undo(struct ipc_namespace *ns, int semid)
12561256
return un;
12571257
}
12581258

1259+
1260+
/**
1261+
* get_queue_result - Retrieve the result code from sem_queue
1262+
* @q: Pointer to queue structure
1263+
*
1264+
* Retrieve the return code from the pending queue. If IN_WAKEUP is found in
1265+
* q->status, then we must loop until the value is replaced with the final
1266+
* value: This may happen if a task is woken up by an unrelated event (e.g.
1267+
* signal) and in parallel the task is woken up by another task because it got
1268+
* the requested semaphores.
1269+
*
1270+
* The function can be called with or without holding the semaphore spinlock.
1271+
*/
1272+
static int get_queue_result(struct sem_queue *q)
1273+
{
1274+
int error;
1275+
1276+
error = q->status;
1277+
while (unlikely(error == IN_WAKEUP)) {
1278+
cpu_relax();
1279+
error = q->status;
1280+
}
1281+
1282+
return error;
1283+
}
1284+
1285+
12591286
SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
12601287
unsigned, nsops, const struct timespec __user *, timeout)
12611288
{
@@ -1409,15 +1436,18 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
14091436
else
14101437
schedule();
14111438

1412-
error = queue.status;
1413-
while(unlikely(error == IN_WAKEUP)) {
1414-
cpu_relax();
1415-
error = queue.status;
1416-
}
1439+
error = get_queue_result(&queue);
14171440

14181441
if (error != -EINTR) {
14191442
/* fast path: update_queue already obtained all requested
1420-
* resources */
1443+
* resources.
1444+
* Perform a smp_mb(): User space could assume that semop()
1445+
* is a memory barrier: Without the mb(), the cpu could
1446+
* speculatively read in user space stale data that was
1447+
* overwritten by the previous owner of the semaphore.
1448+
*/
1449+
smp_mb();
1450+
14211451
goto out_free;
14221452
}
14231453

@@ -1427,10 +1457,12 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
14271457
goto out_free;
14281458
}
14291459

1460+
error = get_queue_result(&queue);
1461+
14301462
/*
14311463
* If queue.status != -EINTR we are woken up by another process
14321464
*/
1433-
error = queue.status;
1465+
14341466
if (error != -EINTR) {
14351467
goto out_unlock_free;
14361468
}

0 commit comments

Comments
 (0)