# DF-0046 — PoC

`sem_wrap.c` — `semval` (u_short) overflow/wrap via the missing `SEMVMX`
upper-bound in `semop`/`SETVAL`/`semexit`, with wrap-to-0 breaking wait-for-zero
(`semop(0)`) synchronization semantics.

`fix_test.c` — supplementary test exercising the positive-op and SETVAL paths
(used to confirm the fix on the patched kernel).

## The bug

`SEMVMX` (32767) is exported to userland via `seminfo` and POSIX/SVID requires
`semop`/`SETVAL` to fail with `ERANGE` when an operation would make `semval`
exceed `SEMVMX`. The DragonFly kernel never enforces the upper bound at any of
the three write sites:

| Path        | Citation                       | Code                                  |
|-------------|--------------------------------|---------------------------------------|
| semop (+op) | `sys/kern/sysv_sem.c:848-854`  | `semptr->semval += sopptr->sem_op;`   |
| SETVAL      | `sys/kern/sysv_sem.c:530`      | `semptr->semval = real_arg.val;`      |
| semexit     | `sys/kern/sysv_sem.c:1139`     | `semptr->semval += adjval;`           |

The negative-`sem_op` branch (`:827`) has the lower-bound analogue
(`if (semptr->semval + sopptr->sem_op < 0)`), and the in-tree comment at `:160`
admits `"SEMVMX unused - user param"`. `semval` is `u_short` (`:40`), so large
positive ops wrap past 65535.

Consequences (demonstrated by `sem_wrap.c`):

1. **POSIX violation** — `semval` exceeds `SEMVMX` with no `ERANGE` returned
   (four `+32767` ops land `semval` at `131068 mod 65536 = 65532`).
2. **Wrap-to-0 breaks mutual exclusion** — `+32767 + +32767 + +2` wraps
   `semval` to exactly 0 (logical value 65536). A process blocked in
   `semop(semnum=0, op=0)` (wait-for-zero, `semzcnt`) is spuriously released
   even though the logical semaphore value is large and positive — broken
   SysV semaphore synchronization.

No kernel memory corruption (`semval` is a self-contained `u_short` field);
impact is IPC-state integrity / POSIX non-compliance / local DoS via broken
synchronization. No escalation path (no memory write primitive).

## Build & run (unprivileged)

```
cc -o sem_wrap sem_wrap.c
cc -o fix_test fix_test.c    # supplementary
./sem_wrap
```

## Expected output

### BUG present (unpatched `#0` kernel)
```
[1] semval after four +32767 ops = 65532  (SEMVMX=32767, POSIX would have ERANGE'd at op2)
    -> BUG CONFIRMED: semval 65532 > SEMVMX 32767 (no ERANGE)
[2] wait-for-zero child RELEASED after wrap-to-0 (semval logical=65536, u_short=0) -> BUG: spurious release, mutual exclusion broken
```

### FIXED kernel (`#1`, with `fix.diff` applied)
```
sem_wrap: op2 (POSIX says ERANGE; DragonFly allows): Result too large
```
(`fix_test` additionally confirms SETVAL also returns ERANGE for `val > SEMVMX`,
and normal small ops still work — no regression.)
