# DF-4883 — fixpt31_32.c ASSERT-only domain validation: dc_fixpt_log
# diverges into 0/0 (#DE) for nonpositive arguments; related UB/overflow
# family members

## What
`sys/dev/drm/amd/display/dc/basics/fixpt31_32.c` validates its inputs only
with `ASSERT()`, which is `WARN_ON` (non-fatal) in production builds
(`sys/dev/drm/amd/display/dc/os_types.h:76` — the same fact that underlies
DF-1949, the known div0 finding at :43-67, which is NOT re-reported here).

The new, halt-class instance is **dc_fixpt_log (:378-407)**:

```c
ASSERT(arg.value > 0);          /* no-op in production */
/* TODO if arg is negative, return NaN */
/* TODO if arg is zero, return -INF */
do {
        res1 = (res - 1) + arg/exp(res);
        error = res - res1;
        res = res1;
} while (abs_i64(error.value) > 100ULL);   /* raw units: 100 ≈ 2.3e-8 */
```

For **arg == 0**: every step decrements res by exactly 1.0 fixpt
(4294967296 raw units) — the exit test compares RAW units, so
`|error| = 2^32 > 100` forever; res descends −1, −2, −3 … until
`dc_fixpt_exp(res)` underflows to exactly 0, after which
`dc_fixpt_div(arg, 0)` → `complete_integer_division_u64(·, 0)` →
`div64_u64_rem(·, 0)` → **#DE → kernel panic** (or, if the divisor never
reaches exactly 0 on a given arch/path, an unbounded kernel loop).

For **arg < 0**: divergence is immediate and violent (res plummets), same
terminal 0/0.

The file's own TODO comments (:386-387) acknowledge the missing domain
handling.

## Proof (behavioral, on the QEMU guest)
`fixpt_harness.c` copies the library functions VERBATIM (from_fraction,
mul, exp + Taylor series, log, and the header inlines) with production
ASSERT semantics (`ASSERT(x)` → no-op) and plain 64-bit `/`/`%` for
`div64_u64_rem`. Run on the DragonFly guest (build.sh/run.sh):

```
[sanity] dc_fixpt_log(2.0) = 0.693147180 (expect ~0.693147181) after 9 iterations
[child] SIGFPE (integer 0/0) trapped after ~21 log iterations
[DF-4883] log(0.0): child exited status=136
[child] SIGFPE (integer 0/0) trapped after ~2 log iterations
[DF-4883] log(-1.0): child exited status=136
```

`log(2)` converging correctly validates the harness; `log(0)` and
`log(-1)` trap integer-divide-by-zero (SIGFPE = the userspace mirror of
the kernel #DE). This proves the *algorithm* defect with the file's exact
code — see VERDICT.md for why the kernel-level manifestation is gated.

## Kernel reachability in this vintage (honest assessment)
- `dc_fixpt_log` has exactly one in-tree consumer family:
  `dc_fixpt_pow` (fixed31_32.h:431-438 = `exp(log(arg1)·arg2)`) and the
  direct call in `modules/color/color_gamma.c:159`
  (`compute_hlg_oetf`, guarded: threshold > b ⇒ positive argument).
- `compute_hlg_eotf` (color_gamma.c:184-187) evaluates
  `dc_fixpt_pow(in_x, 2)` for in_x ≤ ½ — but its x grid
  (`coordinates_x`, color_gamma.c:52-66) starts at a positive power of
  two and never contains 0, so log never receives 0 there in practice.
- The user-gamma paths (`translate_{from,to}_linear_space`) feed strictly
  positive bases (branches at color_gamma.c:270-334).
- Everything is behind AMD DC hardware (absent on the audit guest).

⇒ behavior: certain; in-vintage kernel reachability: latent/speculative.
Low severity.

## Related family members folded into this finding (same root cause:
ASSERT-only validation, non-div0 instances)
- `dc_fixpt_exp` (:347-371): `m` from `dc_fixpt_round(div(arg, ln2))`;
  `1LL << -m` for m < -63 is UB (x86 masks the count); positive m is
  truncated to `unsigned char` for `dc_fixpt_shl` (:365-367, up to 255 →
  shift >= 64 UB) — garbage exponents, no trap.
- `dc_fixpt_mul` (:137-165) / `dc_fixpt_sqr` (:182-205) /
  `from_fraction` (:81-108): all overflow checks are ASSERTs; signed
  `res.value <<= 32` at :141 is UB when the integer part exceeds 2^31;
  silent garbage for out-of-31.32-range operands.
- `-numerator`/`-arg.value`/`abs_i64` negate LLONG_MIN unchecked
  (:35, :74-75, :126-127) — UB, wraps in practice.

## Fix
Guard the log domain (implement the file's own TODO) — see fix.diff.

## Build / run
```
cc -O2 -o fixpt_harness fixpt_harness.c   # on the DFly guest
./fixpt_harness
```
Success criterion: sanity line prints ≈0.693147; the log(0)/log(-1)
children die SIGFPE (status 136) instead of returning.
