aboutsummaryrefslogtreecommitdiffstats
path: root/ipc
diff options
context:
space:
mode:
authorPetr Mladek <pmladek@suse.cz>2014-01-27 20:07:00 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2014-01-28 00:02:39 -0500
commit78f5009cc35eb5e52d276a046d90ee2f41b60f8c (patch)
treeea7a64dd1bf5b00e5c6c1bb4c709ac51aa3f7675 /ipc
parent729abd2ba7007cc2be9e77718ba52d0866d3f60f (diff)
ipc/sem.c: avoid overflow of semop undo (semadj) value
When trying to understand semop code, I found a small mistake in the check for semadj (undo) value overflow. The new undo value is not stored immediately and next potential checks are done against the old value. The failing scenario is not much practical. One semop call has to do more operations on the same semaphore. Also semval and semadj must have different values, so there has to be some operations without SEM_UNDO flag. For example: struct sembuf depositor_op[1]; struct sembuf collector_op[2]; depositor_op[0].sem_num = 0; depositor_op[0].sem_op = 20000; depositor_op[0].sem_flg = 0; collector_op[0].sem_num = 0; collector_op[0].sem_op = -10000; collector_op[0].sem_flg = SEM_UNDO; collector_op[1].sem_num = 0; collector_op[1].sem_op = -10000; collector_op[1].sem_flg = SEM_UNDO; if (semop(semid, depositor_op, 1) == -1) { perror("Failed to do 1st deposit"); return 1; } if (semop(semid, collector_op, 2) == -1) { perror("Failed to do 1st collect"); return 1; } if (semop(semid, depositor_op, 1) == -1) { perror("Failed to do 2nd deposit"); return 1; } if (semop(semid, collector_op, 2) == -1) { perror("Failed to do 2nd collect"); return 1; } return 0; It passes without error now but the semadj value has overflown in the 2nd collector operation. [akpm@linux-foundation.org: restore lessened scope of local `undo'] [davidlohr@hp.com: correct header comment for perform_atomic_semop] Signed-off-by: Petr Mladek <pmladek@suse.cz> Acked-by: Davidlohr Bueso <davidlohr@hp.com> Acked-by: Manfred Spraul <manfred@colorfullife.com> Cc: Jiri Kosina <jkosina@suse.cz> Signed-off-by: Davidlohr Bueso <davidlohr@hp.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'ipc')
-rw-r--r--ipc/sem.c24
1 files changed, 13 insertions, 11 deletions
diff --git a/ipc/sem.c b/ipc/sem.c
index db9d241af133..cc9ac35b793c 100644
--- a/ipc/sem.c
+++ b/ipc/sem.c
@@ -584,10 +584,11 @@ SYSCALL_DEFINE3(semget, key_t, key, int, nsems, int, semflg)
584 return ipcget(ns, &sem_ids(ns), &sem_ops, &sem_params); 584 return ipcget(ns, &sem_ids(ns), &sem_ops, &sem_params);
585} 585}
586 586
587/** perform_atomic_semop - Perform (if possible) a semaphore operation 587/**
588 * perform_atomic_semop - Perform (if possible) a semaphore operation
588 * @sma: semaphore array 589 * @sma: semaphore array
589 * @sops: array with operations that should be checked 590 * @sops: array with operations that should be checked
590 * @nsems: number of sops 591 * @nsops: number of operations
591 * @un: undo array 592 * @un: undo array
592 * @pid: pid that did the change 593 * @pid: pid that did the change
593 * 594 *
@@ -595,7 +596,6 @@ SYSCALL_DEFINE3(semget, key_t, key, int, nsems, int, semflg)
595 * Returns 1 if the operation is impossible, the caller must sleep. 596 * Returns 1 if the operation is impossible, the caller must sleep.
596 * Negative values are error codes. 597 * Negative values are error codes.
597 */ 598 */
598
599static int perform_atomic_semop(struct sem_array *sma, struct sembuf *sops, 599static int perform_atomic_semop(struct sem_array *sma, struct sembuf *sops,
600 int nsops, struct sem_undo *un, int pid) 600 int nsops, struct sem_undo *un, int pid)
601{ 601{
@@ -607,7 +607,7 @@ static int perform_atomic_semop(struct sem_array *sma, struct sembuf *sops,
607 curr = sma->sem_base + sop->sem_num; 607 curr = sma->sem_base + sop->sem_num;
608 sem_op = sop->sem_op; 608 sem_op = sop->sem_op;
609 result = curr->semval; 609 result = curr->semval;
610 610
611 if (!sem_op && result) 611 if (!sem_op && result)
612 goto would_block; 612 goto would_block;
613 613
@@ -616,25 +616,24 @@ static int perform_atomic_semop(struct sem_array *sma, struct sembuf *sops,
616 goto would_block; 616 goto would_block;
617 if (result > SEMVMX) 617 if (result > SEMVMX)
618 goto out_of_range; 618 goto out_of_range;
619
619 if (sop->sem_flg & SEM_UNDO) { 620 if (sop->sem_flg & SEM_UNDO) {
620 int undo = un->semadj[sop->sem_num] - sem_op; 621 int undo = un->semadj[sop->sem_num] - sem_op;
621 /* 622 /* Exceeding the undo range is an error. */
622 * Exceeding the undo range is an error.
623 */
624 if (undo < (-SEMAEM - 1) || undo > SEMAEM) 623 if (undo < (-SEMAEM - 1) || undo > SEMAEM)
625 goto out_of_range; 624 goto out_of_range;
625 un->semadj[sop->sem_num] = undo;
626 } 626 }
627
627 curr->semval = result; 628 curr->semval = result;
628 } 629 }
629 630
630 sop--; 631 sop--;
631 while (sop >= sops) { 632 while (sop >= sops) {
632 sma->sem_base[sop->sem_num].sempid = pid; 633 sma->sem_base[sop->sem_num].sempid = pid;
633 if (sop->sem_flg & SEM_UNDO)
634 un->semadj[sop->sem_num] -= sop->sem_op;
635 sop--; 634 sop--;
636 } 635 }
637 636
638 return 0; 637 return 0;
639 638
640out_of_range: 639out_of_range:
@@ -650,7 +649,10 @@ would_block:
650undo: 649undo:
651 sop--; 650 sop--;
652 while (sop >= sops) { 651 while (sop >= sops) {
653 sma->sem_base[sop->sem_num].semval -= sop->sem_op; 652 sem_op = sop->sem_op;
653 sma->sem_base[sop->sem_num].semval -= sem_op;
654 if (sop->sem_flg & SEM_UNDO)
655 un->semadj[sop->sem_num] += sem_op;
654 sop--; 656 sop--;
655 } 657 }
656 658