Ensure that cluster allocations never allocate clusters outside the

volume limits.  In particular:
- Assert that usemap_alloc() and usemap_free() cluster number argument
  is valid.
- In chainlength(), return 0 if cluster start is after the max cluster.
- In chainlength(), cut the calculated cluster chain length at the max
  cluster.
- For true paranoia, after the pm_inusemap is calculated in
  fillinusemap(), reset all bits in the array for clusters after the
  max cluster, as in-use.

Reported and tested by:	pho
Sponsored by:	The FreeBSD Foundation
MFC after:	2 weeks
This commit is contained in:
Konstantin Belousov 2016-10-28 11:34:32 +00:00
parent 03b8a419e4
commit b05088aeeb

View File

@ -382,6 +382,8 @@ usemap_alloc(struct msdosfsmount *pmp, u_long cn)
MSDOSFS_ASSERT_MP_LOCKED(pmp); MSDOSFS_ASSERT_MP_LOCKED(pmp);
KASSERT(cn <= pmp->pm_maxcluster, ("cn too large %lu %lu", cn,
pmp->pm_maxcluster));
KASSERT((pmp->pm_flags & MSDOSFSMNT_RONLY) == 0, KASSERT((pmp->pm_flags & MSDOSFSMNT_RONLY) == 0,
("usemap_alloc on ro msdosfs mount")); ("usemap_alloc on ro msdosfs mount"));
KASSERT((pmp->pm_inusemap[cn / N_INUSEBITS] & (1 << (cn % N_INUSEBITS))) KASSERT((pmp->pm_inusemap[cn / N_INUSEBITS] & (1 << (cn % N_INUSEBITS)))
@ -398,6 +400,9 @@ usemap_free(struct msdosfsmount *pmp, u_long cn)
{ {
MSDOSFS_ASSERT_MP_LOCKED(pmp); MSDOSFS_ASSERT_MP_LOCKED(pmp);
KASSERT(cn <= pmp->pm_maxcluster, ("cn too large %lu %lu", cn,
pmp->pm_maxcluster));
KASSERT((pmp->pm_flags & MSDOSFSMNT_RONLY) == 0, KASSERT((pmp->pm_flags & MSDOSFSMNT_RONLY) == 0,
("usemap_free on ro msdosfs mount")); ("usemap_free on ro msdosfs mount"));
pmp->pm_freeclustercount++; pmp->pm_freeclustercount++;
@ -637,6 +642,8 @@ chainlength(struct msdosfsmount *pmp, u_long start, u_long count)
MSDOSFS_ASSERT_MP_LOCKED(pmp); MSDOSFS_ASSERT_MP_LOCKED(pmp);
if (start > pmp->pm_maxcluster)
return (0);
max_idx = pmp->pm_maxcluster / N_INUSEBITS; max_idx = pmp->pm_maxcluster / N_INUSEBITS;
idx = start / N_INUSEBITS; idx = start / N_INUSEBITS;
start %= N_INUSEBITS; start %= N_INUSEBITS;
@ -644,11 +651,18 @@ chainlength(struct msdosfsmount *pmp, u_long start, u_long count)
map &= ~((1 << start) - 1); map &= ~((1 << start) - 1);
if (map) { if (map) {
len = ffs(map) - 1 - start; len = ffs(map) - 1 - start;
return (len > count ? count : len); len = MIN(len, count);
if (start + len > pmp->pm_maxcluster)
len = pmp->pm_maxcluster - start + 1;
return (len);
} }
len = N_INUSEBITS - start; len = N_INUSEBITS - start;
if (len >= count) if (len >= count) {
return (count); len = count;
if (start + len > pmp->pm_maxcluster)
len = pmp->pm_maxcluster - start + 1;
return (len);
}
while (++idx <= max_idx) { while (++idx <= max_idx) {
if (len >= count) if (len >= count)
break; break;
@ -659,7 +673,10 @@ chainlength(struct msdosfsmount *pmp, u_long start, u_long count)
} }
len += N_INUSEBITS; len += N_INUSEBITS;
} }
return (len > count ? count : len); len = MIN(len, count);
if (start + len > pmp->pm_maxcluster)
len = pmp->pm_maxcluster - start + 1;
return (len);
} }
/* /*
@ -918,6 +935,11 @@ fillinusemap(struct msdosfsmount *pmp)
} }
if (bp != NULL) if (bp != NULL)
brelse(bp); brelse(bp);
for (cn = pmp->pm_maxcluster + 1; cn < (pmp->pm_maxcluster +
N_INUSEBITS) / N_INUSEBITS; cn++)
pmp->pm_inusemap[cn / N_INUSEBITS] |= 1 << (cn % N_INUSEBITS);
return (0); return (0);
} }