⬢ DragonFlyBSD Kernel Audit
DF-4883 / fix.diff
← back to finding ↓ download raw
Fix DF-4883: implement the domain guard dc_fixpt_log's own TODO promises.

sys/dev/drm/amd/display/dc/basics/fixpt31_32.c: dc_fixpt_log validates its
argument only with ASSERT (a no-op in production builds, os_types.h:76).
For arg <= 0 the Newton iteration diverges: for arg == 0 each step
decrements res by exactly 1.0 fixpt (2^32 raw units), which can never
satisfy the raw-unit exit test (abs(error) > 100), so res descends until
dc_fixpt_exp(res) underflows to 0 and dc_fixpt_div(arg, 0) divides by
zero (#DE panic via complete_integer_division_u64 - the DF-1949 sink);
for arg < 0 divergence is immediate. Proof-of-behavior harness in this
evidence pack (run.log: SIGFPE after ~21 iterations for log(0), ~2 for
log(-1), while log(2) converges in 9).

--- a/sys/dev/drm/amd/display/dc/basics/fixpt31_32.c
+++ b/sys/dev/drm/amd/display/dc/basics/fixpt31_32.c
@@ -382,9 +382,17 @@ struct fixed31_32 dc_fixpt_log(struct fixed31_32 arg)
 	struct fixed31_32 error;
 
 	ASSERT(arg.value > 0);
-	/* TODO if arg is negative, return NaN */
-	/* TODO if arg is zero, return -INF */
+	/*
+	 * ASSERTs are compiled out in production.  For arg <= 0 the
+	 * Newton iteration below diverges (for arg == 0 every step moves
+	 * res by 1.0 fixpt, never satisfying the raw-unit exit test) until
+	 * exp(res) underflows to 0 and the loop divides by zero (#DE).
+	 * Implement the TODOs: saturate instead of hanging/panicking.
+	 */
+	if (arg.value <= 0)
+		/* TODO if arg is negative, return NaN */
+		return (struct fixed31_32){ .value = LLONG_MIN }; /* -INF */
 
 	do {
 		struct fixed31_32 res1 = dc_fixpt_add(