diff --git a/sys/dev/disk/fd/fd.c b/sys/dev/disk/fd/fd.c --- a/sys/dev/disk/fd/fd.c +++ b/sys/dev/disk/fd/fd.c @@ -2326,7 +2326,25 @@ /* this is considered harmful; only allow for superuser */ if (caps_priv_check(ap->a_cred, SYSCAP_RESTRICTEDROOT)) return EPERM; - fd->ft = *(struct fd_type *)ap->a_data; + /* + * Validate the caller-supplied type before installing it. + * sectrac and heads are used as divisors (d_secpercyl = + * sectrac*heads; d_ncylinders = size / d_secpercyl; and + * blknum/(sectrac*heads), sec/sectrac in fdstate), and + * secsize is a left-shift count (128 << secsize). Without + * these checks a planted type with sectrac==0 or heads==0 + * arms a kernel divide-by-zero trap on the next open/I/O, + * and secsize>=25 (signed overflow) / >=32 (shift >= width) + * is undefined behaviour. + */ + { + struct fd_type *nft = (struct fd_type *)ap->a_data; + if (nft->sectrac <= 0 || nft->heads <= 0 || + nft->secsize < 0 || nft->secsize > 7) { + return EINVAL; + } + fd->ft = *nft; + } break; case FD_GOPTS: /* get drive options */