From 20cbf39c0deeff75922b2fe6c3f7d3e0bbd8aaba Mon Sep 17 00:00:00 2001 From: Colin Finck Date: Sun, 13 Mar 2022 17:48:03 +0100 Subject: [PATCH] Tighten the cluster size and record size limits. We may loosen these limits later again if we find valid NTFS filesystems outside these ranges. For now, they guard against malicious filesystem images that previously led to huge buffer allocations. Fixes #2 --- src/boot_sector.rs | 29 ++++++++++++++++++----------- src/error.rs | 6 +++--- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/boot_sector.rs b/src/boot_sector.rs index 7cbd94b..c8dcb41 100644 --- a/src/boot_sector.rs +++ b/src/boot_sector.rs @@ -1,4 +1,4 @@ -// Copyright 2021 Colin Finck +// Copyright 2021-2022 Colin Finck // SPDX-License-Identifier: MIT OR Apache-2.0 use crate::error::{NtfsError, Result}; @@ -42,14 +42,20 @@ pub(crate) struct BiosParameterBlock { impl BiosParameterBlock { /// Returns the size of a single cluster, in bytes. pub(crate) fn cluster_size(&self) -> Result { + /// The cluster size cannot go lower than a single sector. + const MIN_CLUSTER_SIZE: u32 = 512; + /// The maximum cluster size supported by Windows is 2 MiB. /// Source: https://en.wikipedia.org/wiki/NTFS - const MAXIMUM_CLUSTER_SIZE: u32 = 2097152; + const MAX_CLUSTER_SIZE: u32 = 2097152; + + const CLUSTER_SIZE_RANGE: RangeInclusive = MIN_CLUSTER_SIZE..=MAX_CLUSTER_SIZE; let cluster_size = self.sectors_per_cluster as u32 * self.sector_size as u32; - if cluster_size > MAXIMUM_CLUSTER_SIZE || !cluster_size.is_power_of_two() { + if !CLUSTER_SIZE_RANGE.contains(&cluster_size) || !cluster_size.is_power_of_two() { return Err(NtfsError::UnsupportedClusterSize { - expected: MAXIMUM_CLUSTER_SIZE, + min: MIN_CLUSTER_SIZE, + max: MAX_CLUSTER_SIZE, actual: cluster_size, }); } @@ -68,16 +74,17 @@ impl BiosParameterBlock { /// Source: https://en.wikipedia.org/wiki/NTFS#Partition_Boot_Sector_(VBR) fn record_size(&self, size_info: i8) -> Result { - /// The usual exponent of `BiosParameterBlock::file_record_size_info` is 10 (2^10 = 1024 bytes). + // The usual exponent of `BiosParameterBlock::file_record_size_info` is 10 (2^10 = 1024 bytes). + // For index records, it's usually 12 (2^12 = 4096 bytes). + /// Exponents < 10 have never been seen and are denied to guarantee that every record header /// fits into a record. - const MINIMUM_SIZE_INFO_EXPONENT: u32 = 10; + const MIN_EXPONENT: u32 = 10; - /// Exponents > 10 would come as a surprise, but our code should still be able to handle those. - /// Exponents > 31 (2^31 = 2 GiB) would make no sense, exceed a u32, and must be outright denied. - const MAXIMUM_SIZE_INFO_EXPONENT: u32 = 31; + /// Exponents > 12 have neither been seen and are denied to prevent allocating too large buffers. + const MAX_EXPONENT: u32 = 12; - const SIZE_INFO_RANGE: Range = MINIMUM_SIZE_INFO_EXPONENT..MAXIMUM_SIZE_INFO_EXPONENT; + const EXPONENT_RANGE: RangeInclusive = MIN_EXPONENT..=MAX_EXPONENT; let cluster_size = self.cluster_size()?; @@ -93,7 +100,7 @@ impl BiosParameterBlock { // The size field denotes a binary exponent after negation. let exponent = (-size_info) as u32; - if !SIZE_INFO_RANGE.contains(&exponent) { + if !EXPONENT_RANGE.contains(&exponent) { return Err(NtfsError::InvalidRecordSizeInfo { size_info, cluster_size, diff --git a/src/error.rs b/src/error.rs index eb09ff7..77cf996 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,4 +1,4 @@ -// Copyright 2021 Colin Finck +// Copyright 2021-2022 Colin Finck // SPDX-License-Identifier: MIT OR Apache-2.0 use crate::attribute::NtfsAttributeType; @@ -163,8 +163,8 @@ pub enum NtfsError { UnexpectedResidentAttribute { position: u64 }, /// The type of the NTFS Attribute at byte position {position:#010x} is {actual:#010x}, which is not supported UnsupportedAttributeType { position: u64, actual: u32 }, - /// The cluster size is {actual} bytes, but the maximum supported one is {expected} - UnsupportedClusterSize { expected: u32, actual: u32 }, + /// The cluster size is {actual} bytes, but it needs to be between {min} and {max} + UnsupportedClusterSize { min: u32, max: u32, actual: u32 }, /// The namespace of the NTFS file name starting at byte position {position:#010x} is {actual}, which is not supported UnsupportedFileNamespace { position: u64, actual: u8 }, /// The sector size is {actual} bytes, but the only supported one is {expected}