Skip to content

Commit

Permalink
Merge pull request #289 from changweige/pick-meta-validate
Browse files Browse the repository at this point in the history
Rafs: validate metadata stored in bootstrap
  • Loading branch information
bergwolf authored Jan 28, 2022
2 parents 2493eb7 + d62430b commit 3008d7b
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 65 deletions.
38 changes: 14 additions & 24 deletions rafs/src/metadata/direct_v6.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ use std::sync::Arc;

use arc_swap::ArcSwap;

use crate::metadata::layout::v6::RafsV6BlobTable;
// use crate::metadata::layout::MetaRange;
use crate::metadata::layout::v6::{RafsV6BlobTable, EROFS_BLOCK_SIZE};
use crate::metadata::layout::MetaRange;
use crate::metadata::{
Inode, RafsInode, RafsSuperBlobs, RafsSuperBlock, RafsSuperInodes, RafsSuperMeta,
};
Expand Down Expand Up @@ -150,28 +150,18 @@ impl DirectSuperBlockV6 {
let md = file.metadata()?;
let len = md.len();
let size = len as usize;
// if len < RAFSV5_SUPERBLOCK_SIZE as u64
// || len > RAFS_MAX_METADATA_SIZE as u64
// || len & (RAFSV5_ALIGNMENT as u64 - 1) != 0
// {
// return Err(ebadf!("invalid bootstrap file"));
// }
// let md_range = MetaRange::new(
// RAFSV5_SUPERBLOCK_SIZE as u64,
// len - RAFSV5_SUPERBLOCK_SIZE as u64,
// true,
// )?;

// Validate blob table layout
// let blob_table_size = old_state.meta.blob_table_size as u64;

// let blob_table_start = old_state.meta.blob_table_offset;
// let blob_table_range = MetaRange::new(blob_table_start, blob_table_size, false)?;
// if !blob_table_range.is_subrange_of(&md_range)
// || blob_table_range.intersect_with(&inode_table_range)
// {
// return Err(ebadf!("invalid blob table"));
// }

let md_range =
MetaRange::new(EROFS_BLOCK_SIZE as u64, len - EROFS_BLOCK_SIZE as u64, true)?;

// Validate blob table layout as blob_table_start and
// blob_table_offset is read from bootstrap.
let blob_table_size = old_state.meta.blob_table_size as u64;
let blob_table_start = old_state.meta.blob_table_offset;
let blob_table_range = MetaRange::new(blob_table_start, blob_table_size, false)?;
if !blob_table_range.is_subrange_of(&md_range) {
return Err(ebadf!("invalid blob table"));
}

// Prefetch the bootstrap file
readahead(fd, 0, len);
Expand Down
174 changes: 135 additions & 39 deletions rafs/src/metadata/layout/v6.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use std::sync::Arc;

use nydus_utils::{digest, round_up, ByteSize};
use storage::device::{BlobFeatures, BlobInfo};
use storage::meta::BlobMetaHeaderOndisk;
use storage::meta::{BlobMetaHeaderOndisk, BLOB_FEATURE_4K_ALIGNED};
use storage::{compress, RAFS_MAX_CHUNK_SIZE};

use crate::metadata::{layout::RafsXAttrs, RafsStore, RafsSuperFlags};
Expand Down Expand Up @@ -135,35 +135,71 @@ impl RafsV6SuperBlock {
/// Validate the Rafs v6 super block.
pub fn validate(&self, meta_size: u64) -> Result<()> {
if meta_size < EROFS_BLOCK_SIZE {
Err(einval!("invalid Rafs v6 metadata size"))
} else if self.s_blkszbits != EROFS_BLOCK_BITS {
Err(einval!("invalid block size bits in Rafsv6 superblock"))
} else if u64::from_le(self.s_inos) == 0 {
Err(einval!("invalid inode number in Rafsv6 superblock"))
} else if self.s_extslots != 0 {
Err(einval!("invalid extended slots in Rafsv6 superblock"))
} else if self.s_blocks == 0 {
Err(einval!("invalid blocks in Rafsv6 superblock"))
} else if u16::from_le(self.s_u) != 0 {
Err(einval!("invalid union field in Rafsv6 superblock"))
} else if u64::from_le(self.s_build_time) != 0 || u32::from_le(self.s_build_time_nsec) != 0
{
Err(einval!("invalid build time in Rafsv6 superblock"))
} else if u32::from_le(self.s_feature_incompat)
return Err(einval!(format!(
"invalid Rafs v6 metadata size: {}",
meta_size
)));
}

if meta_size & (EROFS_BLOCK_SIZE - 1) != 0 {
return Err(einval!(format!(
"invalid Rafs v6 metadata size: bootstrap size {} is not aligned",
meta_size
)));
}

if u32::from_le(self.s_checksum) != 0 {
return Err(einval!(format!(
"invalid checksum {} in Rafsv6 superblock",
u32::from_le(self.s_checksum)
)));
}

if self.s_blkszbits != EROFS_BLOCK_BITS {
return Err(einval!(format!(
"invalid block size bits {} in Rafsv6 superblock",
self.s_blkszbits
)));
}

if u64::from_le(self.s_inos) == 0 {
return Err(einval!("invalid inode number in Rafsv6 superblock"));
}

if self.s_extslots != 0 {
return Err(einval!("invalid extended slots in Rafsv6 superblock"));
}

if self.s_blocks == 0 {
return Err(einval!("invalid blocks in Rafsv6 superblock"));
}

if u16::from_le(self.s_u) != 0 {
return Err(einval!("invalid union field in Rafsv6 superblock"));
}

// s_build_time may be used as compact_inode's timestamp in the future.
// if u64::from_le(self.s_build_time) != 0 || u32::from_le(self.s_build_time_nsec) != 0 {
// return Err(einval!("invalid build time in Rafsv6 superblock"));
// }

if u32::from_le(self.s_feature_incompat)
!= EROFS_FEATURE_INCOMPAT_CHUNKED_FILE | EROFS_FEATURE_INCOMPAT_DEVICE_TABLE
{
Err(einval!(
return Err(einval!(
"invalid incompatible feature bits in Rafsv6 superblock"
))
} else if u32::from_le(self.s_feature_compat) & EROFS_FEATURE_COMPAT_RAFS_V6
));
}

if u32::from_le(self.s_feature_compat) & EROFS_FEATURE_COMPAT_RAFS_V6
!= EROFS_FEATURE_COMPAT_RAFS_V6
{
Err(einval!(
return Err(einval!(
"invalid compatible feature bits in Rafsv6 superblock"
))
} else {
Ok(())
));
}

Ok(())
}

/// Check whether it's super block for Rafs v6.
Expand Down Expand Up @@ -280,23 +316,25 @@ impl RafsV6SuperBlockExt {
}

/// Validate the Rafs v6 super block.
pub fn validate(&self, _meta_size: u64) -> Result<()> {
pub fn validate(&self) -> Result<()> {
let mut flags = self.flags();
flags &= RafsSuperFlags::COMPRESS_NONE.bits()
| RafsSuperFlags::COMPRESS_LZ4_BLOCK.bits()
| RafsSuperFlags::COMPRESS_GZIP.bits();
if flags.count_ones() != 1 {
return Err(einval!(
"invalid flags related to compression algorithm in Rafs v6 extended superblock"
));
return Err(einval!(format!(
"invalid flags {:#x} related to compression algorithm in Rafs v6 extended superblock",
flags
)));
}

let mut flags = self.flags();
flags &= RafsSuperFlags::DIGESTER_BLAKE3.bits() | RafsSuperFlags::DIGESTER_SHA256.bits();
if flags.count_ones() != 1 {
return Err(einval!(
"invalid flags related to digest algorithm in Rafs v6 extended superblock"
));
return Err(einval!(format!(
"invalid flags {:#x} related to digest algorithm in Rafs v6 extended superblock",
flags
)));
}

let chunk_size = u32::from_le(self.s_chunk_size) as u64;
Expand All @@ -307,6 +345,12 @@ impl RafsV6SuperBlockExt {
return Err(einval!("invalid chunk size in Rafs v6 extended superblock"));
}

if self.blob_table_offset() & (EROFS_BLOCK_SIZE - 1) != 0 {
return Err(einval!(format!(
"invalid blob table offset {} in Rafs v6 extended superblock",
self.blob_table_offset()
)));
}
Ok(())
}

Expand Down Expand Up @@ -819,7 +863,7 @@ impl RafsStore for RafsV6InodeChunkAddr {
#[derive(Clone, Copy, Debug)]
pub struct RafsV6Device {
/// Blob id of sha256.
blob_id: [u8; 64],
blob_id: [u8; BLOB_SHA256_LEN],
/// Number of blocks on the device.
blocks: u32,
/// Mapping start address.
Expand Down Expand Up @@ -875,6 +919,34 @@ impl RafsV6Device {
pub fn load(&mut self, r: &mut RafsIoReader) -> Result<()> {
r.read_exact(self.as_mut())
}

/// Validate the Rafs v6 Device slot.
pub fn validate(&self) -> Result<()> {
match String::from_utf8(self.blob_id.to_vec()) {
Ok(v) => {
if v.len() != BLOB_SHA256_LEN {
return Err(einval!(format!("v.len {} is invalid", v.len())));
}
}

Err(_) => {
return Err(einval!("blob_id from_utf8 is invalid"));
}
}

if self.blocks() == 0 {
return Err(einval!(format!(
"invalid blocks {} in Rafs v6 Device",
self.blocks()
)));
}

if u32::from_le(self.mapped_blkaddr) != 0 {
return Err(einval!("invalid mapped_addr in Rafs v6 Device"));
}

Ok(())
}
}

impl_bootstrap_converter!(RafsV6Device);
Expand Down Expand Up @@ -1026,23 +1098,26 @@ impl RafsV6Blob {
}

fn validate(&self, blob_index: u32, chunk_size: u32, _flags: RafsSuperFlags) -> bool {
/* TODO: check fields: compressed_size, meta_features, ci_offset, ci_compressed_size, ci_uncompressed_size, ci_digest */
/* TODO: check fields: compressed_size, meta_features, ci_offset, ci_compressed_size, ci_uncompressed_size */
match String::from_utf8(self.blob_id.to_vec()) {
Ok(v) => {
if v.len() != BLOB_SHA256_LEN {
error!("v.len {} is invalid", v.len());
error!("RafsV6Blob: v.len {} is invalid", v.len());
return false;
}
}

Err(_) => {
error!("blob_id from_utf8 is invalid");
error!("RafsV6Blob: blob_id from_utf8 is invalid");
return false;
}
}

if self.blob_index != blob_index.to_le() {
error!("blob_index mismatch {} {}", self.blob_index, blob_index);
error!(
"RafsV6Blob: blob_index mismatch {} {}",
self.blob_index, blob_index
);
return false;
}

Expand All @@ -1053,23 +1128,26 @@ impl RafsV6Blob {
|| c_size != chunk_size as u64
{
error!(
"invalid c_size {}, count_ones() {}",
"RafsV6Blob: invalid c_size {}, count_ones() {}",
c_size,
c_size.count_ones()
);
return false;
}

if u32::from_le(self.chunk_count) >= (1u32 << 24) {
error!("chunk_count {}", u32::from_le(self.chunk_count));
error!(
"RafsV6Blob: invalid chunk_count {}",
u32::from_le(self.chunk_count)
);
return false;
}

let uncompressed_size = u64::from_le(self.uncompressed_size);
if uncompressed_size & (EROFS_BLOCK_SIZE as u64 - 1) != 0
|| uncompressed_size >= (1u64 << 44)
{
error!("uncompressd_size {}", uncompressed_size);
error!("RafsV6Blob: invalid uncompressd_size {}", uncompressed_size);
return false;
}

Expand All @@ -1078,12 +1156,30 @@ impl RafsV6Blob {
|| digest::Algorithm::try_from(u32::from_le(self.digest_algo)).is_err()
{
error!(
"invalid compression_algo {} ci_compressor {} digest_algo {}",
"RafsV6Blob: invalid compression_algo {} ci_compressor {} digest_algo {}",
self.compression_algo, self.ci_compressor, self.digest_algo
);
return false;
}

if self.ci_digest != [0u8; 32] {
error!("RafsV6Blob: invalid ci_digest",);
return false;
}

// for now the uncompressed data chunk of v6 image is 4k aligned.
if u32::from_le(self.meta_features) & BLOB_FEATURE_4K_ALIGNED == 0 {
error!("RafsV6Blob: invalid meta_features",);
return false;
}

let ci_compr_size = u64::from_le(self.ci_compressed_size);
let ci_uncompr_size = u64::from_le(self.ci_uncompressed_size);
if ci_compr_size > ci_uncompr_size {
error!("RafsV6Blob: invalid fields, ci_compressed_size {} is greater than ci_uncompressed_size {}", ci_compr_size, ci_uncompr_size);
return false;
}

true
}
}
Expand Down
2 changes: 1 addition & 1 deletion rafs/src/metadata/md_v6.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ impl RafsSuper {

let mut ext_sb = RafsV6SuperBlockExt::new();
ext_sb.load(r)?;
ext_sb.validate(end)?;
ext_sb.validate()?;
self.meta.chunk_size = ext_sb.chunk_size();
self.meta.blob_table_offset = ext_sb.blob_table_offset();
self.meta.blob_table_size = ext_sb.blob_table_size();
Expand Down
2 changes: 1 addition & 1 deletion storage/src/meta/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const BLOB_CHUNK_UNCOMP_OFFSET_MASK: u64 = 0xfff_ffff_f000;
const BLOB_CHUNK_SIZE_MASK: u64 = 0xf_ffff;
const BLOB_CHUNK_SIZE_SHIFT: u64 = 44;
const FILE_SUFFIX: &str = "blob.meta";
const BLOB_FEATURE_4K_ALIGNED: u32 = 0x1;
pub const BLOB_FEATURE_4K_ALIGNED: u32 = 0x1;

/// Blob metadata on disk format.
#[repr(C)]
Expand Down

0 comments on commit 3008d7b

Please sign in to comment.