Make `from_bits` in `bitflags!` safe; add `from_bits_truncate`

Previously, the `from_bits` function in the `std::bitflags::bitflags`
macro was marked as unsafe, as it did not check that the bits being
converted actually corresponded to flags.

This patch changes the function to check against the full set of
possible flags and return an `Option` which is `None` if a non-flag bit
is set. It also adds a `from_bits_truncate` function which simply zeros
any bits not corresponding to a flag.

This addresses the concern raised in https://github.com/mozilla/rust/pull/13897
This commit is contained in:
Aaron Turon 2014-05-14 14:39:16 -07:00 committed by Alex Crichton
parent d547de998d
commit 912a9675c0
4 changed files with 33 additions and 16 deletions

View File

@ -493,9 +493,7 @@ fn mkstat(stat: &libc::stat) -> io::FileStat {
io::FileStat {
size: stat.st_size as u64,
kind: kind,
perm: unsafe {
io::FilePermission::from_bits(stat.st_mode as u32) & io::AllPermissions
},
perm: io::FilePermission::from_bits_truncate(stat.st_mode as u32),
created: mktime(stat.st_ctime as u64, stat.st_ctime_nsec as u64),
modified: mktime(stat.st_mtime as u64, stat.st_mtime_nsec as u64),
accessed: mktime(stat.st_atime as u64, stat.st_atime_nsec as u64),

View File

@ -492,9 +492,7 @@ fn mkstat(stat: &libc::stat) -> io::FileStat {
io::FileStat {
size: stat.st_size as u64,
kind: kind,
perm: unsafe {
io::FilePermission::from_bits(stat.st_mode as u32) & io::AllPermissions
},
perm: io::FilePermission::from_bits_truncate(stat.st_mode as u32),
created: stat.st_ctime as u64,
modified: stat.st_mtime as u64,
accessed: stat.st_atime as u64,

View File

@ -285,9 +285,7 @@ impl FsRequest {
FileStat {
size: stat.st_size as u64,
kind: kind,
perm: unsafe {
io::FilePermission::from_bits(stat.st_mode as u32) & io::AllPermissions
},
perm: io::FilePermission::from_bits_truncate(stat.st_mode as u32),
created: to_msec(stat.st_birthtim),
modified: to_msec(stat.st_mtim),
accessed: to_msec(stat.st_atim),

View File

@ -136,10 +136,20 @@ macro_rules! bitflags(
self.bits
}
/// Convert from underlying bit representation. Unsafe because the
/// bits are not guaranteed to represent valid flags.
pub unsafe fn from_bits(bits: $T) -> $BitFlags {
$BitFlags { bits: bits }
/// Convert from underlying bit representation, unless that
/// representation contains bits that do not correspond to a flag.
pub fn from_bits(bits: $T) -> ::std::option::Option<$BitFlags> {
if (bits & !$BitFlags::all().bits()) != 0 {
::std::option::None
} else {
::std::option::Some($BitFlags { bits: bits })
}
}
/// Convert from underlying bit representation, dropping any bits
/// that do not correspond to flags.
pub fn from_bits_truncate(bits: $T) -> $BitFlags {
$BitFlags { bits: bits } & $BitFlags::all()
}
/// Returns `true` if no flags are currently stored.
@ -209,6 +219,7 @@ macro_rules! bitflags(
#[cfg(test)]
mod tests {
use option::{Some, None};
use ops::{BitOr, BitAnd, Sub, Not};
bitflags!(
@ -231,9 +242,21 @@ mod tests {
#[test]
fn test_from_bits() {
assert!(unsafe { Flags::from_bits(0x00000000) } == Flags::empty());
assert!(unsafe { Flags::from_bits(0x00000001) } == FlagA);
assert!(unsafe { Flags::from_bits(0x00000111) } == FlagABC);
assert!(Flags::from_bits(0) == Some(Flags::empty()));
assert!(Flags::from_bits(0x1) == Some(FlagA));
assert!(Flags::from_bits(0x10) == Some(FlagB));
assert!(Flags::from_bits(0x11) == Some(FlagA | FlagB));
assert!(Flags::from_bits(0x1000) == None);
}
#[test]
fn test_from_bits_truncate() {
assert!(Flags::from_bits_truncate(0) == Flags::empty());
assert!(Flags::from_bits_truncate(0x1) == FlagA);
assert!(Flags::from_bits_truncate(0x10) == FlagB);
assert!(Flags::from_bits_truncate(0x11) == (FlagA | FlagB));
assert!(Flags::from_bits_truncate(0x1000) == Flags::empty());
assert!(Flags::from_bits_truncate(0x1001) == FlagA);
}
#[test]