All of lore.kernel.org
 help / color / mirror / Atom feed
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 08/12] famfs: iomap_begin and file-to-dax offset resolution
Date: Mon, 03 Aug 2026 02:44:45 +0000	[thread overview]
Message-ID: <20260803024445.7DC561F000E9@smtp.kernel.org> (raw)
In-Reply-To: <0100019fc5748c1e-45cb66a3-f847-4c3e-8a89-a04c89d24b32-000000@email.amazonses.com>

Thank you for your contribution! Sashiko AI review found 8 potential issue(s) to consider:

New issues:
- [Critical] Missing read memory barrier (`smp_rmb()`) in `famfs_daxdev_from_index` allows returning an uninitialized pointer, causing the DAX core to use an uninitialized iomap structure.
- [Critical] The iomap_begin handlers mistakenly assign the input operation `flags` to the output `iomap->flags`, unintentionally setting `IOMAP_F_NEW` during writes and causing data corruption.
- [High] The `len` parameter in `famfs_meta_to_dax_offset` and `famfs_meta_to_dax_offset_interleaved` is incorrectly typed as `off_t` instead of `loff_t`, causing integer truncation on 32-bit architectures.
- [High] In `famfs_meta_to_dax_offset_interleaved`, `iomap->length` is not bounded by the end of the interleaved extent, causing out-of-bounds physical mappings.
- [High] Time-Of-Check to Time-Of-Use (TOCTOU) race condition in `famfs_meta_to_dax_offset` allows a NULL pointer dereference when evaluating `inode->i_private`.
- [Low] 64-bit integer division and modulo operations in `famfs_meta_to_dax_offset_interleaved` cause unresolved symbol errors during linking on 32-bit architectures.

Pre-existing issues:
- [Critical] The `FAMFSIOC_MAP_CREATE` ioctl lacks privilege checks, allowing any user with write access to a file to map and access arbitrary physical memory on any registered DAX device.
- [High] `famfs_dax_write_iter` bypasses standard VFS write checks, failing to clear setuid/setgid bits and update file times upon modification.
--

--- Patch [8]: [PATCH V12 08/12] famfs: iomap_begin and file-to-dax offset resolution ---
commit b4e4f3f2df1ac1714b2f127455834eca42345a35
Author: John Groves <John@Groves.net>
Subject: famfs: iomap_begin and file-to-dax offset resolution

Add the iomap resolver that maps a file offset to a (daxdev, offset) pair:
famfs_meta_to_dax_offset() for simple extent lists and
famfs_meta_to_dax_offset_interleaved() for striped files, backed by the
per-daxdev health check (famfs_dax_err) and table lookup
(famfs_daxdev_for_index), plus famfs_iomap_begin() and famfs_iomap_ops.

> diff --git a/fs/famfs/famfs_file.c b/fs/famfs/famfs_file.c
> index d710c8a0c923c..e7f271ce6d03d 100644
> --- a/fs/famfs/famfs_file.c
> +++ b/fs/famfs/famfs_file.c
> @@ -320,6 +320,298 @@ famfs_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
[ ... ]
> +static struct dax_device *
> +famfs_daxdev_from_index(struct famfs_fs_info *fsi, u64 dev_index, int *errp)
> +{
> +	struct famfs_dax_devlist *devlist = fsi->dax_devlist;
> +	struct famfs_daxdev *dd;
> +	int rc;
> +
> +	if (!devlist || dev_index >= devlist->nslots) {
> +		pr_debug("%s: dev_index %llu out of range\n",
> +			__func__, dev_index);
> +		*errp = -EIO;
> +		return NULL;
> +	}
> +	dd = &devlist->devlist[dev_index];
> +	rc = famfs_dax_err(dd);
> +	if (rc) {
> +		*errp = rc;
> +		return NULL;
> +	}
> +	*errp = 0;
> +	return dd->devp;
> +}

[Severity: Critical]
Does this code need a read memory barrier?

In famfs_daxdev_from_index(), dd->valid is read via famfs_dax_err(), and
then dd->devp is returned without an smp_rmb(). If a concurrent page fault
occurs while a DAX device is being installed via famfs_install_daxdev(),
CPU reordering could cause this to see valid == 1 but return a NULL device
pointer.

[ ... ]
> +static int
> +famfs_meta_to_dax_offset_interleaved(struct inode *inode, struct iomap *iomap,
> +			 loff_t file_offset, off_t len, unsigned int flags)

[Severity: High]
Can this cause integer truncation on 32-bit architectures?

The len parameter is typed as off_t (which is 32-bit signed on 32-bit
architectures) instead of loff_t. A large I/O request could result in a
negative value, which is then sign-extended back to a 64-bit value in
min_t(loff_t, len, ...), resulting in a massive iomap->length and causing
out-of-bounds mappings. Should this match the iomap_begin signature with
loff_t?

[ ... ]
> +		/* Is the data is in this striped extent? */
> +		if (local_offset < ext_size) {
> +			u64 chunk_num       = local_offset / chunk_size;
> +			u64 chunk_offset    = local_offset % chunk_size;
> +			u64 stripe_num      = chunk_num / nstrips;
> +			u64 strip_num       = chunk_num % nstrips;

[Severity: Low]
Could these 64-bit integer division and modulo operations cause unresolved
symbol errors during linking on 32-bit architectures?

The code uses standard / and % operators on 64-bit integers (local_offset,
chunk_size, chunk_num), which emits calls to __divdi3 and __umoddi3. Should
this use the kernel division helpers like div64_u64() or do_div()?

[ ... ]
> +			iomap->addr    = strip->ext_offset + strip_offset;
> +			iomap->offset  = file_offset;
> +			iomap->length  = min_t(loff_t, len, chunk_remainder);
> +			iomap->length  = min_t(loff_t, iomap->length,
> +					       strip->ext_len - strip_offset);

[Severity: High]
Does this properly bound the physical mapping length?

The calculation restricts iomap->length to chunk_remainder and the strip's
length, but fails to restrict it to ext_size - local_offset. If a read/write
operation happens near the end of an interleaved extent where ext_size is not
a perfect multiple of the chunk size, the returned length could physically
exceed the logical boundaries of the extent.

> +			iomap->dax_dev = daxdev;
> +			iomap->type    = IOMAP_MAPPED;
> +			iomap->flags   = flags;

[Severity: Critical]
Does assigning the input operational flags directly to the output mapping
flags overwrite valid data?

The flags argument to iomap_begin contains input flags (e.g., IOMAP_WRITE
which is 1 << 0). In the iomap output namespace, 1 << 0 is IOMAP_F_NEW.
Setting IOMAP_F_NEW tells the DAX core that the memory was newly allocated
and requires zeroing on partial writes, overwriting valid existing data with
zeroes.

[ ... ]
> +static int
> +famfs_meta_to_dax_offset(struct inode *inode, struct iomap *iomap,
> +			 loff_t file_offset, off_t len, unsigned int flags)

[Severity: High]
Can this cause integer truncation on 32-bit architectures?

The len parameter is typed as off_t (which is 32-bit signed on 32-bit
architectures) instead of loff_t. A large I/O request could result in a
negative value, which is then sign-extended back to a 64-bit value in
min_t(loff_t, len, ...), resulting in a massive iomap->length and causing
out-of-bounds mappings. Should this match the iomap_begin signature with
loff_t?

> +{
> +	struct famfs_fs_info  *fsi = inode->i_sb->s_fs_info;
> +	struct famfs_file_meta *meta = inode->i_private;
> +	loff_t local_offset = file_offset;
> +	int rc;
> +	int i;
> +
> +	if (fsi->deverror || famfs_file_invalid(inode))
> +		goto err_out;
> +
> +	if (meta->fm_extent_type == FAMFS_IOC_EXT_INTERLEAVE)

[Severity: High]
Is there a Time-Of-Check to Time-Of-Use (TOCTOU) race condition here?

The local meta pointer is fetched from inode->i_private locklessly. It then
calls famfs_file_invalid(inode), which re-fetches inode->i_private. If
another thread initialized the file in between via FAMFSIOC_MAP_CREATE,
famfs_file_invalid() sees the new metadata and returns 0 (success). The first
thread then proceeds to dereference its locally cached meta pointer (which is
still NULL), crashing the kernel.

[ ... ]
> +		if (local_offset < dax_ext_len) {
> +			loff_t ext_len_remainder = dax_ext_len - local_offset;
> +			struct dax_device *daxdev;
> +
> +			daxdev = famfs_daxdev_from_index(fsi,
> +						meta->se[i].dev_index, &rc);
> +			if (!daxdev) {
> +				meta->error = true;
> +				return rc;
> +			}

[Severity: Critical]
Are we returning an uninitialized iomap structure here?

Because famfs_daxdev_from_index() returns a 0 error code when valid == 1,
famfs_meta_to_dax_offset() treats a NULL daxdev as a success (rc = 0) but
bails out early, returning 0 without initializing iomap->addr or
iomap->length. The DAX core would then operate on garbage physical addresses.

[ ... ]
> +			iomap->addr    = dax_ext_offset + local_offset;
> +			iomap->offset  = file_offset;
> +			iomap->length  = min_t(loff_t, len, ext_len_remainder);
> +			iomap->dax_dev = daxdev;
> +			iomap->type    = IOMAP_MAPPED;
> +			iomap->flags   = flags;

[Severity: Critical]
Does assigning the input operational flags directly to the output mapping
flags overwrite valid data?

The flags argument to iomap_begin contains input flags (e.g., IOMAP_WRITE
which is 1 << 0). In the iomap output namespace, 1 << 0 is IOMAP_F_NEW.
Setting IOMAP_F_NEW tells the DAX core that the memory was newly allocated
and requires zeroing on partial writes, overwriting valid existing data with
zeroes.

[ ... ]
> @@ -501,7 +793,7 @@ famfs_dax_write_iter(struct kiocb *iocb, struct iov_iter *from)
>  		return rc;
>  	}
>  
> -	rc = dax_iomap_rw(iocb, from, NULL /*&famfs_iomap_ops*/);
> +	rc = dax_iomap_rw(iocb, from, &famfs_iomap_ops);
>  	inode_unlock(inode);
>  	return rc;
>  }

[Severity: High]
This is a pre-existing issue, but does famfs_dax_write_iter() bypass standard
VFS write checks?

It calls dax_iomap_rw() directly without invoking generic_write_checks() or
file_update_time(). This allows users to modify the contents of a setuid
binary without stripping the setuid bit, which is a classic privilege
escalation vector.


[Severity: Critical]
This is a pre-existing issue, but does FAMFSIOC_MAP_CREATE lack a privilege
check?

In famfs_file_init_dax() in fs/famfs/famfs_file.c:

	if (!famfs_opt_enabled(fsi, FAMFS_OPT_MAP_CREATE))
		return -EPERM;

Any unprivileged user with write access to a file could map and access
arbitrary physical memory on any registered DAX device. Should there be a
capable(CAP_SYS_ADMIN) or CAP_SYS_RAWIO check before parsing arbitrary physical
memory offsets? With the introduction of the iomap operations in this patch,
this allows the DAX core to perform I/O against unvalidated offsets.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/0100019fc572ca94-ec363dd7-3a77-484b-b4b7-f2503a0931a6-000000@email.amazonses.com?part=8

  reply	other threads:[~2026-08-03  2:44 UTC|newest]

Thread overview: 52+ 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 19:13     ` Alison Schofield
2026-08-05 20:21       ` John Groves
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-06  4:37     ` Darrick J. Wong
2026-08-06 13:22       ` John Groves
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-06  5:05     ` Darrick J. Wong
2026-08-06 13:36       ` John Groves
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-06  5:12     ` Darrick J. Wong
2026-08-06 16:31       ` John Groves
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-06  5:14     ` Darrick J. Wong
2026-08-06 20:03       ` John Groves
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-06  5:16     ` Darrick J. Wong
2026-08-06 20:40       ` John Groves
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
2026-08-06  5:24     ` Darrick J. Wong
2026-08-06 20:53       ` John Groves
2026-08-07 22:17         ` John Groves
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 [this message]
2026-08-06  5:28     ` Darrick J. Wong
2026-08-06 22:14       ` John Groves
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-06  5:29     ` Darrick J. Wong
2026-08-06 22:22       ` John Groves
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-06  5:31     ` Darrick J. Wong
2026-08-06 22:30       ` John Groves
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-06  5:33     ` Darrick J. Wong
2026-08-07 13:47       ` John Groves
2026-08-03  2:30   ` [PATCH V12 12/12] famfs: Add documentation John Groves
2026-08-06  5:38     ` Darrick J. Wong
2026-08-07 15:05       ` John Groves
2026-08-03  8:52   ` [PATCH V12 00/12] famfs: the Fabric-Attached Memory File System (standalone) Amir Goldstein
2026-08-06  5:19     ` Matthew Wilcox
2026-08-06  5:34       ` Darrick J. Wong

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=20260803024445.7DC561F000E9@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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.