Illegal fragmentation block sizes can trigger division by zero in the disklabel and fsck_ffs tools.
See this sequence of commands to reproduce: # dd if=/dev/zero of=nofrag.iso bs=1M count=1 # vnconfig vnd0 nofrag.iso # disklabel -e vnd0 # create 'a' and set fsize = bsize = 1 # fsck_ffs vnd0a ** /dev/vnd0a (vnd0a) BAD SUPER BLOCK: MAGIC NUMBER WRONG Floating point exception (core dumped) # disklabel -E vnd0 Label editor (enter '?' for help at any prompt) > m a offset: [0] size: [2048] FS type: [4.2BSD] Floating point exception (core dumped) # vnconfig -u vnd0 A fragmentation (block) size smaller than a sector size is not valid while using "disklabel -E", and really doesn't make sense. While using "disklabel -e", not all validation checks are performed, which makes it possible to enter invalid values. If "disklabel -E" is used without the expert mode, fragmentation sizes cannot be changed and will be just accepted from the parsed disklabel, resulting in a division by zero if they are too small. And the same happens in fsck_ffs. Instead of coming up with a guessed value in fsck_ffs, I think it's better to simply fail and let the user fix the disklabel. After all, it shouldn't be fsck_ffs's duty to fix faulty values outside the filesystem. Index: sbin/disklabel/disklabel.c =================================================================== RCS file: /cvs/src/sbin/disklabel/disklabel.c,v retrieving revision 1.222 diff -u -p -u -p -r1.222 disklabel.c --- sbin/disklabel/disklabel.c 19 Jun 2016 13:42:56 -0000 1.222 +++ sbin/disklabel/disklabel.c 27 Aug 2016 13:30:38 -0000 @@ -1096,9 +1096,24 @@ getasciilabel(FILE *f, struct disklabel case FS_BSDFFS: NXTNUM(fsize, fsize, &errstr); - if (fsize == 0) + if (fsize < lp->d_secsize || + (fsize % lp->d_secsize) != 0) { + warnx("line %d: " + "bad fragmentation size: %s", + lineno, cp); + errors++; break; + } NXTNUM(v, v, &errstr); + if (v < fsize || (fsize != v && + fsize * 2 != v && fsize * 4 != v && + fsize * 8 != v)) { + warnx("line %d: " + "bad block size: %s", + lineno, cp); + errors++; + break; + } pp->p_fragblock = DISKLABELV1_FFS_FRAGBLOCK(fsize, v / fsize); NXTNUM(pp->p_cpg, pp->p_cpg, &errstr); Index: sbin/disklabel/editor.c =================================================================== RCS file: /cvs/src/sbin/disklabel/editor.c,v retrieving revision 1.301 diff -u -p -u -p -r1.301 editor.c --- sbin/disklabel/editor.c 19 Aug 2016 08:06:25 -0000 1.301 +++ sbin/disklabel/editor.c 27 Aug 2016 13:30:39 -0000 @@ -2024,16 +2024,16 @@ get_bsize(struct disklabel *lp, int part if (pp->p_fstype != FS_BSDFFS) return (0); + frag = DISKLABELV1_FFS_FRAG(pp->p_fragblock); + fsize = DISKLABELV1_FFS_FSIZE(pp->p_fragblock); + /* Avoid dividing by zero... */ - if (pp->p_fragblock == 0) - return(1); + if (frag * fsize < lp->d_secsize) + return (1); if (!expert) goto align; - fsize = DISKLABELV1_FFS_FSIZE(pp->p_fragblock); - frag = DISKLABELV1_FFS_FRAG(pp->p_fragblock); - for (;;) { ui = getuint64(lp, "block size", "Size of ffs blocks. 1, 2, 4 or 8 times ffs fragment size.", @@ -2074,8 +2074,7 @@ align: orig_size = DL_GETPSIZE(pp); orig_offset = DL_GETPOFFSET(pp); - bsize = (DISKLABELV1_FFS_FRAG(pp->p_fragblock) * - DISKLABELV1_FFS_FSIZE(pp->p_fragblock)) / lp->d_secsize; + bsize = (frag * fsize) / lp->d_secsize; if (DL_GETPOFFSET(pp) != starting_sector) { /* Can't change offset of first partition. */ adj = bsize - (DL_GETPOFFSET(pp) % bsize); Index: sbin/fsck_ffs/setup.c =================================================================== RCS file: /cvs/src/sbin/fsck_ffs/setup.c,v retrieving revision 1.61 diff -u -p -u -p -r1.61 setup.c --- sbin/fsck_ffs/setup.c 20 Aug 2016 15:04:21 -0000 1.61 +++ sbin/fsck_ffs/setup.c 27 Aug 2016 13:30:39 -0000 @@ -625,6 +625,10 @@ calcsb(char *dev, int devfd, struct fs * fs->fs_fsize = DISKLABELV1_FFS_FSIZE(pp->p_fragblock); fs->fs_frag = DISKLABELV1_FFS_FRAG(pp->p_fragblock); fs->fs_bsize = fs->fs_fsize * fs->fs_frag; + if (fs->bsize < lp->d_secsize) { + pfatal("%s: INVALID BLOCK FRAGMENTATION SIZE\n", dev); + return (0); + } fs->fs_cpg = pp->p_cpg; fs->fs_nspf = DL_SECTOBLK(lp, fs->fs_fsize / lp->d_secsize); /*