Root-writable bioq_reorder_minor_interval used as modulus divisor without validation -> divide-by-zero panic
| Field | Value |
|---|---|
| ID | DF-0026 |
| Status | new |
| Severity | Low |
| CVSS 3.1 | CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:N/I:N/A:H |
| CWE | CWE-369 Divide By Zero; CWE-20 Improper Input Validation |
| File | sys/kern/subr_disk.c |
| Lines | 1325-1327, 1376 |
| Area | kern |
| Confidence | certain |
| Discovered | 2026-06-29 |
| Reported | pending |
Summary
kern.bioq_reorder_minor_interval is exported as a CTLFLAG_RW SYSCTL_INT
with no bounds validation and is used directly as the right-hand operand of a
modulus (bioq->reorder % bioq_reorder_minor_interval) in the disk bio-sort
hot path bioqdisksort(). Setting it to 0 (root) causes an immediate integer
divide-by-zero (FPE_INTDIV) and kernel panic on the next disk read dispatched
to a bioq that already has pending writes (bioq->transition != NULL).
Root cause
sys/kern/subr_disk.c:1325-1327:
int bioq_reorder_minor_interval = 5;
SYSCTL_INT(_kern, OID_AUTO, bioq_reorder_minor_interval,
CTLFLAG_RW, &bioq_reorder_minor_interval, 0, "");
if (bioq->reorder % bioq_reorder_minor_interval == 0) { /* no divisor guard */
There is no lower-bound check on the divisor. The other three bioq_reorder_*
knobs are not divisors (burst_interval is only compared with >=; the
*_bytes knobs are subtracted under a left < n guard), so this is the only
fatal one.
Threat model & preconditions
- Attacker position: privileged โ writing
kern.*sysctls requiresSYSCAP_NOSYSCTL_WR(root,cr_uid==0). So this is a privileged-user- triggered kernel panic (self-DoS / hardening defect), not an escalation. - Privileges gained or impact: kernel panic (full-system DoS). No integrity/confidentiality impact.
- Required config or capabilities: root; a disk with mixed read/write I/O
so a read reaches the
bioq->transition != NULLbranch. - Reachability:
sysctl kern.bioq_reorder_minor_interval=0, then any disk read on a bioq with pending writes.
Proof of concept
PoC source: findings/poc/DF-0026/bioq_div0.sh
Run (root, disposable VM)
sh findings/poc/DF-0026/bioq_div0.sh
Expected output
panic: integer divide fault (FPE_INTDIV in bioqdisksort)
Impact
Root-only self-DoS. The kernel should not panic from a sysctl write of an in-range integer โ an unintentional panic knob reachable through normal disk I/O. Rated Low (privileged trigger). Same class as DF-0019.
Recommended fix
Guard the divisor in the consumer (and/or validate in a SYSCTL_PROC handler):
--- a/sys/kern/subr_disk.c
+++ b/sys/kern/subr_disk.c
@@ -1373,8 +1373,14 @@
* Insert before the first write. Bleedover writes
* based on reorder intervals to prevent starvation.
*/
+ int minor_interval = bioq_reorder_minor_interval;
+ int burst_interval = bioq_reorder_burst_interval;
+ if (minor_interval < 1)
+ minor_interval = 1;
+ if (burst_interval < minor_interval)
+ burst_interval = minor_interval;
TAILQ_INSERT_BEFORE(bioq->transition, bio, bio_act);
++bioq->reorder;
- if (bioq->reorder % bioq_reorder_minor_interval == 0) {
+ if (bioq->reorder % minor_interval == 0) {
bioqwritereorder(bioq);
- if (bioq->reorder >= bioq_reorder_burst_interval) {
+ if (bioq->reorder >= burst_interval) {
bioq->reorder = 0;
Defense-in-depth: convert the knob to a SYSCTL_PROC handler that rejects
values < 1 (and enforces the existing "burst must be a multiple of minor"
invariant noted in the comment at :1322).
References
sys/kern/subr_disk.c:1325-1327โbioq_reorder_minor_intervalSYSCTL_INT(no bound).sys/kern/subr_disk.c:1376โ unguarded% bioq_reorder_minor_interval.- CWE-369 Divide By Zero; CWE-20 Improper Input Validation.
Timeline
- 2026-06-29 Discovered during automated file-by-file audit of
sys/kern/subr_disk.c. - pending Reported to DragonFlyBSD security contact.