# DF-2937 — sched_setscheduler() returns success (0) for invalid policies; POSIX-required EINVAL missing

## What

`sys_sched_setscheduler()` (`sys/kern/kern_p1003_1b.c:250-276`) passes the
user-supplied `policy` integer to `ksched_setscheduler()`
(`sys/kern/kern_sched.c:160-198`) without validating it. The callee's
`switch (policy)` handles only `SCHED_RR`, `SCHED_FIFO`, `SCHED_OTHER` and has
**no `default:` case**, so any other policy value (0, 4711, -1, INT_MAX, …)
falls through the switch and the function returns `e == 0` — **success** —
while doing nothing.

POSIX.1b (sched_setscheduler, _POSIX_PRIORITY_SCHEDULING) requires `[EINVAL]`
for a policy value other than SCHED_FIFO, SCHED_RR, SCHED_OTHER.

## Reachability / privilege gate

* Reaching the callee requires passing `p31b_proc()`
  (`sys/kern/kern_p1003_1b.c:136-167`), whose `CAN_AFFECT` gate
  (`sys/kern/kern_p1003_1b.c:80`) is `((cr)->cr_uid == 0)` — **root-only,
  including for pid==0 (self)**. Verified empirically: as uid 1001 every
  pid-taking sched(2) call returns EPERM, even `sched_getscheduler(0)`
  (see `run.unpriv.log`).
* Therefore the defect is only triggerable by an effective-uid-0 caller —
  an API-contract / robustness defect (silent no-op success), not an
  unprivileged exploit. Severity: Info.

## Why it still matters

A privileged tool (RT tuner, daemon supervisor) that requests a policy
change with a bad constant gets `rc=0` and believes the change happened.
The probe demonstrates the concrete hazard: after a *successful*
`sched_setscheduler(0, SCHED_RR, {0})` the calling process is real-time;
a subsequent `sched_setscheduler(0, <garbage>, {…})` "succeeds" and the
process **stays at real-time priority** with the caller believing it
reverted to normal (see `run.root.log`: `sched_getscheduler(self)` still
returns SCHED_RR=3 after the bogus-policy "success").
Linux-compiled static binaries are also affected by the constant-space
collision (Linux SCHED_OTHER==0 → silent no-op on DragonFly).

## Reproduce

```
scp findings/poc/DF-2937/sched_probe.c guest:/root/
ssh guest-root 'cc -o /root/sched_probe /root/sched_probe.c'
/root/sched_probe            # as root: 4 bogus policies -> rc=0  (BUG)
                             # as user: everything pid-taking -> EPERM
```

Expected (unpatched, root): the last four lines all print `rc=0 errno=0`.
Expected (patched, root):   the last four lines all print `rc=-1 errno=22`.

## Fix

`default: e = EINVAL;` in the policy switch — `fix.diff` in this directory.
Validated by in-guest kernel rebuild: `run.patched.log`.
