Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 62 additions & 11 deletions daxfs/overlay.c
Original file line number Diff line number Diff line change
Expand Up @@ -404,37 +404,88 @@ int daxfs_overlay_init(struct daxfs_info *info)
{
struct daxfs_overlay *ovl;
u64 ovl_offset = le64_to_cpu(info->super->overlay_offset);
u64 ovl_size = le64_to_cpu(info->super->overlay_size);
struct daxfs_overlay_header *hdr;
u64 bucket_offset, bucket_bytes, pool_offset, pool_size;
u32 bucket_count;

if (!ovl_offset)
return 0;

ovl = kzalloc(sizeof(*ovl), GFP_KERNEL);
if (!ovl)
return -ENOMEM;
/* The header is dereferenced immediately below */
if (!daxfs_valid_offset(info, ovl_offset, sizeof(*hdr))) {
pr_err("daxfs: overlay header outside image bounds\n");
return -EINVAL;
}

hdr = daxfs_mem_ptr(info, ovl_offset);
if (le32_to_cpu(hdr->magic) != DAXFS_OVERLAY_MAGIC) {
pr_err("daxfs: invalid overlay magic 0x%x\n",
le32_to_cpu(hdr->magic));
kfree(ovl);
return -EINVAL;
}
if (le32_to_cpu(hdr->version) != DAXFS_OVERLAY_VERSION) {
pr_err("daxfs: unsupported overlay version %u (expected %u)\n",
le32_to_cpu(hdr->version), DAXFS_OVERLAY_VERSION);
kfree(ovl);
return -EINVAL;
}

bucket_count = le32_to_cpu(info->super->overlay_bucket_count);
bucket_offset = le64_to_cpu(hdr->bucket_offset);
pool_offset = le64_to_cpu(hdr->pool_offset);
pool_size = le64_to_cpu(hdr->pool_size);

/*
* bucket_mask is bucket_count - 1 and is applied to every probe, so a
* non-power-of-two count silently stops covering the array.
*/
if (!bucket_count || !is_power_of_2(bucket_count)) {
pr_err("daxfs: overlay bucket_count %u is not a power of 2\n",
bucket_count);
return -EINVAL;
}

bucket_bytes = (u64)bucket_count *
sizeof(struct daxfs_overlay_bucket);

/*
* These three come from the image and are never re-checked: every
* index in [0, bucket_count) is dereferenced by the probe loops, and
* overlay_pool_ptr() bounds pool offsets against pool_size alone. Both
* spans must therefore sit inside the overlay region and the mapping,
* or a malformed image reads and CAS-writes outside it.
*/
if (bucket_offset > ovl_size ||
bucket_bytes > ovl_size - bucket_offset ||
pool_offset > ovl_size ||
pool_size > ovl_size - pool_offset) {
pr_err("daxfs: overlay bucket/pool exceed the overlay region\n");
return -EINVAL;
}

if (!daxfs_valid_offset(info, ovl_offset + bucket_offset,
bucket_bytes) ||
!daxfs_valid_offset(info, ovl_offset + pool_offset, pool_size)) {
pr_err("daxfs: overlay bucket/pool exceed image bounds\n");
return -EINVAL;
}

if (bucket_offset < pool_offset + pool_size &&
pool_offset < bucket_offset + bucket_bytes) {
pr_err("daxfs: overlay bucket array overlaps the pool\n");
return -EINVAL;
}

ovl = kzalloc(sizeof(*ovl), GFP_KERNEL);
if (!ovl)
return -ENOMEM;

ovl->header = hdr;
ovl->mem_model = info->mem_model;
ovl->bucket_count = le32_to_cpu(info->super->overlay_bucket_count);
ovl->bucket_mask = ovl->bucket_count - 1;
ovl->buckets = daxfs_mem_ptr(info,
ovl_offset + le64_to_cpu(hdr->bucket_offset));
ovl->pool = daxfs_mem_ptr(info,
ovl_offset + le64_to_cpu(hdr->pool_offset));
ovl->bucket_count = bucket_count;
ovl->bucket_mask = bucket_count - 1;
ovl->buckets = daxfs_mem_ptr(info, ovl_offset + bucket_offset);
ovl->pool = daxfs_mem_ptr(info, ovl_offset + pool_offset);

info->overlay = ovl;

Expand Down
41 changes: 37 additions & 4 deletions daxfs/pcache.c
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,7 @@ int daxfs_pcache_init(struct daxfs_info *info, const char *backing_path)
{
struct daxfs_pcache *pc;
u64 pcache_offset = le64_to_cpu(info->super->pcache_offset);
u64 pcache_size = le64_to_cpu(info->super->pcache_size);
struct daxfs_pcache_header *hdr;

if (!pcache_offset)
Expand All @@ -710,6 +711,12 @@ int daxfs_pcache_init(struct daxfs_info *info, const char *backing_path)
pc->block_size = info->block_size;
pc->block_shift = ilog2(info->block_size);

if (!daxfs_valid_offset(info, pcache_offset, sizeof(*hdr))) {
pr_err("daxfs: pcache header outside image bounds\n");
kfree(pc);
return -EINVAL;
}

hdr = daxfs_mem_ptr(info, pcache_offset);
if (le32_to_cpu(hdr->magic) != DAXFS_PCACHE_MAGIC) {
pr_err("daxfs: invalid pcache magic 0x%x\n",
Expand All @@ -722,10 +729,36 @@ int daxfs_pcache_init(struct daxfs_info *info, const char *backing_path)
/* Read layout from main superblock */
pc->slot_count = le32_to_cpu(info->super->pcache_slot_count);
pc->hash_mask = pc->slot_count - 1;
pc->slots = daxfs_mem_ptr(info,
pcache_offset + le64_to_cpu(hdr->slot_meta_offset));
pc->data = daxfs_mem_ptr(info,
pcache_offset + le64_to_cpu(hdr->slot_data_offset));

{
u64 meta_offset = le64_to_cpu(hdr->slot_meta_offset);
u64 data_offset = le64_to_cpu(hdr->slot_data_offset);
u64 meta_bytes = (u64)pc->slot_count *
sizeof(struct daxfs_pcache_slot);
u64 data_bytes = (u64)pc->slot_count * pc->block_size;

/*
* Same exposure as the overlay: every slot index in
* [0, slot_count) is dereferenced without further checking,
* so both arrays must be inside the pcache region and the
* mapping.
*/
if (meta_offset > pcache_size ||
meta_bytes > pcache_size - meta_offset ||
data_offset > pcache_size ||
data_bytes > pcache_size - data_offset ||
!daxfs_valid_offset(info, pcache_offset + meta_offset,
meta_bytes) ||
!daxfs_valid_offset(info, pcache_offset + data_offset,
data_bytes)) {
pr_err("daxfs: pcache slot arrays exceed region bounds\n");
kfree(pc);
return -EINVAL;
}

pc->slots = daxfs_mem_ptr(info, pcache_offset + meta_offset);
pc->data = daxfs_mem_ptr(info, pcache_offset + data_offset);
}

info->pcache = pc;

Expand Down
19 changes: 16 additions & 3 deletions daxfs/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -222,12 +222,25 @@ static int daxfs_fill_super(struct super_block *sb, struct fs_context *fc)
if (le64_to_cpu(info->super->base_offset)) {
u64 base_off = le64_to_cpu(info->super->base_offset);
struct daxfs_super *s = info->super;
u64 inode_off = base_off + le64_to_cpu(s->inode_offset);
u32 inode_count = le32_to_cpu(s->inode_count);

info->base_inodes = daxfs_mem_ptr(info,
base_off + le64_to_cpu(s->inode_offset));
/*
* inode_count bounds every &base_inodes[ino - 1] lookup and
* nothing downstream re-checks it, so the table has to be
* mapped even when the caller did not ask for validation.
*/
if (!daxfs_valid_offset(info, inode_off, (u64)inode_count *
sizeof(struct daxfs_base_inode))) {
pr_err("daxfs: base inode table exceeds image bounds\n");
ret = -EINVAL;
goto err_unmap;
}

info->base_inodes = daxfs_mem_ptr(info, inode_off);
info->base_data_offset = base_off +
le64_to_cpu(s->data_offset);
info->base_inode_count = le32_to_cpu(s->inode_count);
info->base_inode_count = inode_count;

/* Validate base image structure (if requested) */
if (ctx->validate) {
Expand Down