From: sashiko-bot@kernel.org
To: "John Groves" <john@jagalactic.com>
Cc: linux-cxl@vger.kernel.org, nvdimm@lists.linux.dev
Subject: Re: [PATCH V12 07/12] famfs: MAP_CREATE ioctl and fmap ingest (ABI 44)
Date: Mon, 03 Aug 2026 02:42:26 +0000 [thread overview]
Message-ID: <20260803024226.6339E1F000E9@smtp.kernel.org> (raw)
In-Reply-To: <0100019fc5746312-6cfe9a07-a658-4aba-b1e9-5233b0ddfcf4-000000@email.amazonses.com>
Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider:
New issues:
- [Critical] The FAMFSIOC_MAP_CREATE ioctl lacks a privilege check, allowing unprivileged users to map arbitrary physical DAX memory.
- [High] 64-bit file size and extent lengths are silently truncated to size_t on 32-bit architectures.
- [High] Missing overflow checks on extent lengths and physical offsets.
- [High] Race condition in state publication leads to permanent file corruption (Denial of Service).
- [High] Persistent memory allocations for file metadata lack __GFP_ACCOUNT, bypassing memcg limits.
- [Low] Format string type mismatch for size_t arguments causes compiler warnings.
Pre-existing issues:
- [High] NULL pointer dereference in famfs_meta_to_dax_offset_interleaved.
--
--- Patch [7]: [PATCH V12 07/12] famfs: MAP_CREATE ioctl and fmap ingest (ABI 44) ---
commit 7c28fdf850baa1b17b165378da6b0515248fad98
Author: John Groves <John@Groves.net>
famfs: MAP_CREATE ioctl and fmap ingest (ABI 44)
This commit adds the famfs file ioctl handler, including the
FAMFSIOC_MAP_CREATE ioctl to ingest user-provided mapping extent messages.
It parses both simple and interleaved wire formats into a famfs_file_meta
structure that is published on the inode.
> diff --git a/fs/famfs/famfs_internal.h b/fs/famfs/famfs_internal.h
> --- a/fs/famfs/famfs_internal.h
> +++ b/fs/famfs/famfs_internal.h
> @@ -15,8 +15,52 @@
> #include <linux/bits.h>
> #include <linux/build_bug.h>
>
> +#include <linux/famfs_ioctl.h>
> +
> extern const struct file_operations famfs_file_operations;
[ ... ]
> +struct famfs_file_meta {
> + bool error;
> + enum famfs_file_type file_type;
> + size_t file_size;
[Severity: High]
Will this type choice truncate the 64-bit file size to 32 bits on 32-bit
architectures? If fmh.file_size exceeds 4GB, the upper 32 bits will be
lost, which could silently corrupt the file size and allow validation
checks to be bypassed.
> + enum famfs_ioc_ext_type fm_extent_type;
> + u64 dev_bitmap; /* referenced daxdev indices */
> + union { /* This will make code a bit more readable */
> + struct {
> + size_t fm_nextents;
> + struct famfs_meta_simple_ext *se;
> + };
> + struct {
> + size_t fm_niext;
> + struct famfs_meta_interleaved_ext *ie;
> + };
> + };
> +};
> diff --git a/fs/famfs/famfs_file.c b/fs/famfs/famfs_file.c
> --- a/fs/famfs/famfs_file.c
> +++ b/fs/famfs/famfs_file.c
[ ... ]
> @@ -60,6 +81,250 @@
> +static int
> +famfs_file_init_dax(struct file *file, void __user *arg)
> +{
[ ... ]
> + fmap_buf = kvmalloc(fmh.fmap_size, GFP_KERNEL);
[Severity: High]
Should these metadata allocations use the __GFP_ACCOUNT flag?
This allocation scales with user input. Without __GFP_ACCOUNT, an
unprivileged user mapping up to 4MB of metadata per file could bypass
memory cgroup limits and potentially trigger a host-wide out-of-memory
condition.
> + if (!fmap_buf)
> + return -ENOMEM;
> +
> + if (copy_from_user(fmap_buf, arg, fmh.fmap_size)) {
> + rc = -EFAULT;
> + goto out;
> + }
> + next_offset = sizeof(fmh); /* start of the extent list */
> +
> + meta = kzalloc_obj(*meta, GFP_KERNEL);
> + if (!meta) {
> + rc = -ENOMEM;
> + goto out;
> + }
> +
> + meta->error = false;
> + meta->file_type = fmh.file_type;
> + meta->file_size = fmh.file_size;
> + meta->fm_extent_type = fmh.ext_type;
> +
> + switch (fmh.ext_type) {
> + case FAMFS_IOC_EXT_SIMPLE: {
> + struct famfs_ioc_simple_ext *se_in = fmap_buf + next_offset;
[ ... ]
> + for (i = 0; i < fmh.nextents; i++) {
> + meta->se[i].dev_index = se_in[i].se_devindex;
> + meta->se[i].ext_offset = se_in[i].se_offset;
> + meta->se[i].ext_len = se_in[i].se_len;
> +
> + if (meta->se[i].dev_index >= FAMFS_MAX_DAXDEVS) {
> + rc = -EINVAL;
> + goto out;
> + }
> + meta->dev_bitmap |= BIT_ULL(meta->se[i].dev_index);
> + errs += famfs_check_ext_alignment(&meta->se[i]);
> + extent_total += meta->se[i].ext_len;
[Severity: High]
Is there a potential integer overflow when accumulating extent_total here?
If the sum of ext_len wraps around, it might pass the extent_total check below
even if the extents do not actually cover the file size. Could ext_offset +
ext_len also overflow U64_MAX and cause out-of-bounds physical memory
accesses when constructing the iomap?
> + }
> + break;
> + }
[ ... ]
> + /* Publish the famfs metadata on inode->i_private */
> + inode_lock(inode);
> + if (inode->i_private) {
> + rc = -EEXIST; /* file already has famfs metadata */
> + } else {
> + inode->i_private = meta;
> + i_size_write(inode, meta->file_size);
[Severity: High]
Could a concurrent reader observe the published metadata but a stale i_size?
Because inode->i_private is assigned before i_size_write(), a lockless
reader could call famfs_file_invalid() in the middle of this window. It
would see a size mismatch and incorrectly set meta->error = true, which
permanently marks the file as corrupted.
> + inode->i_flags |= S_DAX;
> + meta = NULL; /* owned by the inode now */
> + rc = 0;
> + }
> + inode_unlock(inode);
> +
> +out:
> + kvfree(fmap_buf);
> + if (meta)
> + famfs_meta_free(meta);
> + return rc;
> +}
> +
> +/**
> + * famfs_file_ioctl() - Top-level famfs file ioctl handler
> + * @file: the file
> + * @cmd: ioctl opcode
> + * @arg: ioctl opcode argument (if any)
> + */
> +static long
> +famfs_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
> +{
> + struct inode *inode = file_inode(file);
> + struct famfs_fs_info *fsi = inode->i_sb->s_fs_info;
> + long rc;
> +
> + if (fsi->deverror && (cmd != FAMFSIOC_NOP))
> + return -ENODEV;
> +
> + switch (cmd) {
> + case FAMFSIOC_NOP:
> + rc = 0;
> + break;
> +
> + case FAMFSIOC_MAP_CREATE:
> + rc = famfs_file_init_dax(file, (void __user *)arg);
[Severity: Critical]
Does this ioctl command require a privilege check like capable(CAP_SYS_ADMIN)?
Without it, it appears any unprivileged user who can open the file could map
arbitrary physical DAX memory directly into their processes.
> + break;
> +
> + default:
> + rc = -ENOTTY;
> + break;
> + }
> +
> + return rc;
> +}
[ ... ]
> @@ -93,9 +397,25 @@ const struct vm_operations_struct famfs_file_vm_ops = {
> static ssize_t
> famfs_file_invalid(struct inode *inode)
> {
> + struct famfs_file_meta *meta = inode->i_private;
> + size_t i_size = i_size_read(inode);
> +
> + if (!meta) {
> + pr_debug("%s: un-initialized famfs file\n", __func__);
> + return -EIO;
> + }
> + if (meta->error) {
> + pr_debug("%s: previously detected metadata errors\n", __func__);
> + return -EIO;
> + }
> + if (i_size != meta->file_size) {
> + pr_warn("%s: i_size overwritten from %ld to %ld\n",
> + __func__, meta->file_size, i_size);
[Severity: Low]
Should this format string use %zu instead of %ld for size_t variables?
This can cause compiler warnings and misformatted logs on 32-bit platforms.
> + meta->error = true;
> + return -ENXIO;
> + }
[ ... ]
[Severity: High]
This is a pre-existing issue, but can famfs_meta_to_dax_offset_interleaved()
dereference a NULL pointer if called on an uninitialized file?
fs/famfs/famfs_file.c:famfs_meta_to_dax_offset_interleaved() {
...
struct famfs_file_meta *meta = inode->i_private;
...
if (meta->fm_extent_type != FAMFS_IOC_EXT_INTERLEAVE) {
pr_debug("%s: bad extent type\n", __func__);
goto err_out;
}
if (fsi->deverror || famfs_file_invalid(inode))
goto err_out;
...
}
The meta pointer is dereferenced to check fm_extent_type before the call to
famfs_file_invalid(), which is where the check for a NULL meta pointer
actually happens. If the file is uninitialized (inode->i_private is NULL),
this will cause a panic.
--
Sashiko AI review · https://sashiko.dev/#/patchset/0100019fc572ca94-ec363dd7-3a77-484b-b4b7-f2503a0931a6-000000@email.amazonses.com?part=7
next prev parent reply other threads:[~2026-08-03 2:42 UTC|newest]
Thread overview: 25+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <20260803022730.75731-1-john@jagalactic.com>
2026-08-03 2:27 ` [PATCH V12 00/12] famfs: the Fabric-Attached Memory File System (standalone) John Groves
2026-08-03 2:28 ` [PATCH V12 01/12] dax: replace exported dax_dev_get() with non-allocating dax_dev_find() John Groves
2026-08-03 2:43 ` sashiko-bot
2026-08-03 2:28 ` [PATCH V12 02/12] famfs: Module operations, fs_context, and mount John Groves
2026-08-03 2:49 ` sashiko-bot
2026-08-03 2:28 ` [PATCH V12 03/12] famfs: Add daxdev table and dax notify_failure support John Groves
2026-08-03 2:45 ` sashiko-bot
2026-08-03 2:28 ` [PATCH V12 04/12] famfs: Introduce inode_operations and super_operations John Groves
2026-08-03 2:42 ` sashiko-bot
2026-08-03 2:29 ` [PATCH V12 05/12] famfs: Introduce file_operations read/write John Groves
2026-08-03 2:42 ` sashiko-bot
2026-08-03 2:29 ` [PATCH V12 06/12] famfs: Introduce mmap and VM fault handling John Groves
2026-08-03 2:46 ` sashiko-bot
2026-08-03 2:29 ` [PATCH V12 07/12] famfs: MAP_CREATE ioctl and fmap ingest (ABI 44) John Groves
2026-08-03 2:42 ` sashiko-bot [this message]
2026-08-03 2:29 ` [PATCH V12 08/12] famfs: iomap_begin and file-to-dax offset resolution John Groves
2026-08-03 2:44 ` sashiko-bot
2026-08-03 2:29 ` [PATCH V12 09/12] famfs: Register secondary daxdevs by path (FAMFSIOC_DAXDEV_OPEN) John Groves
2026-08-03 2:42 ` sashiko-bot
2026-08-03 2:29 ` [PATCH V12 10/12] famfs: Add runtime operation-permission (opts) framework John Groves
2026-08-03 2:42 ` sashiko-bot
2026-08-03 2:30 ` [PATCH V12 11/12] famfs: Report device capacity via statfs so df works John Groves
2026-08-03 2:58 ` sashiko-bot
2026-08-03 2:30 ` [PATCH V12 12/12] famfs: Add documentation John Groves
2026-08-03 8:52 ` [PATCH V12 00/12] famfs: the Fabric-Attached Memory File System (standalone) Amir Goldstein
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260803024226.6339E1F000E9@smtp.kernel.org \
--to=sashiko-bot@kernel.org \
--cc=john@jagalactic.com \
--cc=linux-cxl@vger.kernel.org \
--cc=nvdimm@lists.linux.dev \
--cc=sashiko-reviews@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox