it_generic_stemp missing (int8_t) sign conversion for IT87xx temperature registers β sub-zero temps published as +128..+255 C under hw.sensors
Summary
it_readreg returns u_int8_t but the registers are two's-complement degrees - every negative temp reports as +128..255C (up to ~528K); comment also mislabels the framework unit as Fahrenheit (it is micro-Kelvin). OpenBSD 2007 ancestor bug inherited unchanged. Data-correctness only; module absent from GENERIC and guest (verified). One-line (int8_t) cast fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5031 Β· 8 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.7 KB | β raw | |
| VERDICT.md | β | 4.0 KB | β raw | |
| reachability.sh | β | 688 B | view raw | |
| reachability.log | β | 391 B | view raw | |
| fix.diff | β | 529 B | view raw | |
| manifest.json | β | 866 B | view raw | |
| verdict.json | β | 2.8 KB | view raw | |
| env.txt | β | 150 B | view raw |
DF-5031 β it.c: missing signed conversion of IT87xx temperature registers
File: sys/dev/powermng/it/it.c
Severity: Info (sensor-data correctness; no memory-safety impact)
Confidence: certain (code inspection; chip semantics from IT87xx datasheet family)
What the code does
void
it_generic_stemp(struct it_softc *sc, struct ksensor *sensors)
{
int i, sdata;
for (i = 0; i < 3; i++) {
sdata = it_readreg(sc, ITD_SENSORTEMPBASE + i); /* u_int8_t */
/* Convert temperature to Fahrenheit degres */
sensors[i].value = sdata * 1000000 + 273150000; /* it.c:261 */
}
}
it_readreg() returns u_int8_t (it.c:192-197), so sdata is 0..255.
IT87xx temperature data registers (0x29-0x2B here) are signed two's
complement degrees Celsius. The formula degC * 1e6 + 273150000 is the
correct Β΅K conversion used across the DragonFly hw.sensors framework, but
without an (int8_t) cast every sub-zero reading (0x80..0xFF = -128..-1 Β°C)
is reported as +128 .. +255 Β°C (up to ~528 K) under
hw.sensors.it0.tempN. The stale comment also says "Fahrenheit" β the
framework unit is micro-Kelvin.
Impact
Wrong monitoring data only. An overheating-vs-frozen misread is an
operational hazard (a chilled sensor reads as scorching), but there is no
memory-safety, privilege, or DoS consequence: sensors[i].value is
int64_t (sys/sys/sensors.h:146) and the arithmetic stays far below
overflow even with the sign bug (255e6 + 273.15e6 β 5.3e8 < 2^63).
Why it is only Info
The driver requires two admin gates before it exists at runtime:
sys/conf/files:2601 gates it behind optional it isa (not in
X86_64_GENERIC β guest-verified 0 matches; no it.ko shipped or loaded),
and the ISA device needs an explicit port hint before probe/attach run. Once
attached, the parsed data comes from the monitored chip's own registers, not
from any unprivileged user input; unprivileged users can only read the
resulting sysctl values.
Reproduce (reachability, read-only)
reachability.sh / reachability.log β on the QEMU guest: device it
absent from the config, no it.ko, not loaded. No runtime PoC possible
(no IT87xx hardware behind QEMU).
Recommended fix
--- a/sys/dev/powermng/it/it.c
+++ b/sys/dev/powermng/it/it.c
@@ -258,7 +258,8 @@ it_generic_stemp(struct it_softc *sc, struct ksensor *sensors)
int i, sdata;
for (i = 0; i < 3; i++) {
- sdata = it_readreg(sc, ITD_SENSORTEMPBASE + i);
- /* Convert temperature to Fahrenheit degres */
+ /* temp registers are signed two's complement, degC */
+ sdata = (int8_t)it_readreg(sc, ITD_SENSORTEMPBASE + i);
+ /* Convert temperature to micro-Kelvin */
sensors[i].value = sdata * 1000000 + 273150000;
}
}
(Full context in fix.diff; never applied to sys/.)
DF-5031 VERDICT β untested (Phase V skipped: Info-severity; no IT87xx hardware on guest)
Bottom line
it_generic_stemp() (sys/dev/powermng/it/it.c:253-263) reads the IT87xx
temperature registers through it_readreg() which returns u_int8_t
(it.c:192-197) and never applies the signed conversion the registers
require. Sub-zero temperatures (0x80..0xFF, i.e. -128..-1 Β°C) are published
to the hw.sensors framework as +128..+255 Β°C. Pure sensor-data correctness
defect inherited from the 2007 OpenBSD port ($OpenBSD: it.c,v 1.22): the
framework unit is Β΅K (degC*1e6 + 273150000), the in-tree comment
mislabels it "Fahrenheit". No memory-safety impact: the destination is
int64_t and the math cannot overflow.
Why no runtime PoC (status untested)
The it driver is not present on the audit guest at all (guest-verified:
no device it in X86_64_GENERIC, no it.ko in /boot/kernel, nothing loaded β
reachability.log), and even with the module hand-built it needs an ISA port
hint plus real IT8705-class Super-I/O silicon to produce a reading; QEMU
exposes no such device. There is no unprivileged input path into this code
in any case β the only user-facing surface is reading the (wrong) values
back via sysctl.
Evidence
reachability.logβ guest output:device-it-in-config: 0,it.ko β¦ NONE,it-loaded: 0; onlycoretempfrom the powermng family is in the default config.- Source: it.c:259-261 (the unsigned read + Β΅K conversion),
it.c:192-197 (
it_readregreturns u_int8_t), itvar.h:62 (temp base 0x29), sys/sys/sensors.h:146 (value int64_t), sys/conf/files:2601 (optional it isa).
Other classes checked and killed in this file (pass 2)
- Fan divisor div-by-zero (it.c:288-313): impossible β
sdata == 0and0xffare handled before1350000 / (sdata << (divisor & 7))at it.c:308; the divisor argument is β₯ 1 for every surviving path. - Volt scaling overflow (it.c:266-285): max intermediate
4080 Γ 51500 = 2.10e8 < INT32_MAX, stored in int64_t;
(sdata<<4)-4096can be negative by design (negative rails) β values stay sane. - Sensor array bounds (it.c:154-156, 323-325 vs itvar.h:32,86): fan 0-2, volt 3-11, temp 12-14 in sensors[15]; refresh passes matching bases.
- Chip-ID bypass (it.c:125-130): exact equality on 0x90; no mask, no bypass; single-register probe is the standard IT87xx EC ID check.
- Detach UAF vs sensor task (it.c:175-190): killed by kern_sensors.c β
st->func(st->arg)executes underthr->lock(kern_sensors.c:334) andsensor_task_unregistertakes the same lock before clearingrunning(kern_sensors.c:243-248); the task entry is framework-owned and freed by the sensors thread (kern_sensors.c:326-331) without touchingarg. - Uninitialized sensor values via sysctl: newbus softc is allocated with
M_INTWAIT | M_ZERO(kern/subr_bus.c:1952-1953), andsysctl_handle_sensorcopies explicit fields only (kern_sensors.c:484-493). - Port-range OOB: only offsets 5/6 (ITC_ADDR/ITC_DATA, itvar.h:39-40) of the 8 allocated ports are ever touched (it.c:115-116, 143-144, 195-203); register indices like ITD_CHIPID 0x58 are address-port values, not port offsets.
- Probe/attach interleaving:
sensor_task_registerat it.c:158 may fire before monitoring enable at 161-163, butsc_iot/sc_iohare set at 149-150 before registration; a garbage first sample is harmless. - Probe returns 1 instead of ENXIO (it.c:119,130) β style only; any non-zero probe return means no match in newbus.
Fix validation
Not performed β no runtime baseline exists on the guest (driver absent,
hardware absent). fix.diff is the reviewer-authored one-line sign fix,
untested at runtime by construction.
Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
Fix verification
not_testableFix validation not applicable: Info-severity correctness finding, no runtime baseline exists on the guest (driver not compiled, hardware absent), Phase V skipped per contract.
n/a (no fix build performed)
Confirmed kernel references
Detail
Evidence (decisive lines)
reachability.log (guest: device-it-in-config 0, it.ko NONE, it-loaded 0); it.c:259-261, it.c:192-197, itvar.h:62, sys/sys/sensors.h:146, sys/conf/files:2601; VERDICT.md (full negative-result list)
PoC changes
No PoC built: driver not compiled into the default kernel (optional it isa) and no IT87xx hardware exists behind QEMU. reachability.sh replaces the trigger.
Verified recommended fix
Cast the temperature register read to signed in it_generic_stemp: sdata = (int8_t)it_readreg(sc, ITD_SENSORTEMPBASE + i); and fix the unit comment (fix.diff).
Verdict
it_generic_stemp (sys/dev/powermng/it/it.c:259-261) reads IT87xx temperature registers via it_readreg() which returns u_int8_t (it.c:192-197) and never applies the required (int8_t) signed conversion, so sub-zero temperatures (0x80..0xFF = -128..-1 degC) are published under hw.sensors as +128..+255 degC; the in-tree comment also mislabels the framework unit (Β΅K) as Fahrenheit. Pure sensor-data correctness defect inherited from the OpenBSD ancestor; no memory-safety impact (value is int64_t, arithmetic bounded at ~5.3e8). Driver absent on the audit guest (no 'device it' in X86_64_GENERIC, no it.ko shipped/loaded, no IT87xx silicon in QEMU) and gated behind admin-only config, so Phase V runtime PoC skipped per contract.
No comments yet.