DF-2784 / adjval_wrap.c
/* * DF-2784 - un_adjval (short) SEMAEM overflow in semundo_adjust(). * * sys/kern/sysv_sem.c:244 accumulates undo adjustments into a 16-bit * signed field with no SEMAEM (kern.ipc.semaem = 16384) enforcement. * Two +30000 SEM_UNDO ops make un_adjval wrap from -60000 to +5536. * At process exit, semexit() (sysv_sem.c:1133-1140) then takes the * *positive* branch and skips the "clamp to 0" logic, leaving the * semaphore at a wrong value instead of 0. * * child: +30000 UNDO, +30000 UNDO, -30000 (no undo) -> semval 30000, * un_adjval stored as +5536 (wrapped) * exit: adjval >= 0 -> semval += 5536 -> 35536 * correct:adjval < 0, |adjval| 60000 > semval 30000 -> clamp to 0 * * Success criterion: final GETVAL == 35536 (bug) instead of 0. */ #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> #include <sys/wait.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> union semun_u { int val; struct semid_ds *buf; unsigned short *array; }; static union semun_u u; int main(void) { int id, st, v; pid_t pid; id = semget(IPC_PRIVATE, 1, 0600); if (id < 0) { perror("semget"); exit(1); } pid = fork(); if (pid == 0) { struct sembuf up[2] = { { 0, 30000, SEM_UNDO }, { 0, 30000, SEM_UNDO }, }; struct sembuf down = { 0, -30000, 0 }; if (semop(id, up, 2) < 0) { if (errno == ERANGE) { printf("child : semop rejected with ERANGE " "(SEMAEM enforced)\n"); _exit(42); /* fixed behavior */ } perror("semop(up x2)"); _exit(9); } if (semop(id, &down, 1) < 0) { perror("semop(down)"); _exit(9); } printf("child : semval before exit = %d (undo owed: -60000)\n", semctl(id, 0, GETVAL, u)); fflush(stdout); _exit(0); /* semexit() applies wrapped adjval */ } waitpid(pid, &st, 0); if (WIFEXITED(st) && WEXITSTATUS(st) == 42) { printf("parent: child rejected at semop (ERANGE) - " "SEMAEM enforcement active, no corruption\n"); semctl(id, 0, IPC_RMID, u); return 0; } v = semctl(id, 0, GETVAL, u); printf("parent: semval after child exit = %d\n", v); printf("expected (POSIX clamp semantics): 0\n"); if (v == 35536) printf("BUG REPRODUCED: un_adjval wrapped to +5536, " "positive branch skipped the clamp\n"); else if (v == 0) printf("correct behavior observed\n"); else printf("unexpected value: %d\n", v); semctl(id, 0, IPC_RMID, u); return (v == 35536) ? 0 : 1; } |