linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] vfs: sanity check the length passed to inode_set_cached_link()
@ 2025-02-04 21:32 Mateusz Guzik
  2025-02-04 21:46 ` Mateusz Guzik
                   ` (3 more replies)
  0 siblings, 4 replies; 6+ messages in thread
From: Mateusz Guzik @ 2025-02-04 21:32 UTC (permalink / raw)
  To: brauner; +Cc: viro, jack, linux-kernel, linux-fsdevel, Mateusz Guzik

This costs a strlen() call when instatianating a symlink.

Preferably it would be hidden behind VFS_WARN_ON (or compatible), but
there is no such facility at the moment. With the facility in place the
call can be patched out in production kernels.

In the meantime, since the cost is being paid unconditionally, use the
result to a fixup the bad caller.

This is not expected to persist in the long run (tm).

Sample splat:
bad length passed for symlink [/tmp/syz-imagegen43743633/file0/file0] (got 131109, expected 37)
[rest of WARN blurp goes here]

Signed-off-by: Mateusz Guzik <mjguzik@gmail.com>
---

This has a side effect of working around the panic reported in:
https://lore.kernel.org/all/67a1e1f4.050a0220.163cdc.0063.GAE@google.com/

I'm confident this merely exposed a bug in ext4, see:
https://lore.kernel.org/all/CAGudoHEv+Diti3r0x9VmF5ixgRVKk4trYnX_skVJNkQoTMaDHg@mail.gmail.com/#t

Nonethelss, should help catch future problems.

 include/linux/fs.h | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/include/linux/fs.h b/include/linux/fs.h
index be3ad155ec9f..1437a3323731 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -791,6 +791,19 @@ struct inode {
 
 static inline void inode_set_cached_link(struct inode *inode, char *link, int linklen)
 {
+	int testlen;
+
+	/*
+	 * TODO: patch it into a debug-only check if relevant macros show up.
+	 * In the meantime, since we are suffering strlen even on production kernels
+	 * to find the right length, do a fixup if the wrong value got passed.
+	 */
+	testlen = strlen(link);
+	if (testlen != linklen) {
+		WARN_ONCE(1, "bad length passed for symlink [%s] (got %d, expected %d)",
+			  link, linklen, testlen);
+		linklen = testlen;
+	}
 	inode->i_link = link;
 	inode->i_linklen = linklen;
 	inode->i_opflags |= IOP_CACHED_LINK;
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH] vfs: sanity check the length passed to inode_set_cached_link()
  2025-02-04 21:32 [PATCH] vfs: sanity check the length passed to inode_set_cached_link() Mateusz Guzik
@ 2025-02-04 21:46 ` Mateusz Guzik
  2025-02-04 22:36 ` Darrick J. Wong
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 6+ messages in thread
From: Mateusz Guzik @ 2025-02-04 21:46 UTC (permalink / raw)
  To: brauner; +Cc: viro, jack, linux-kernel, linux-fsdevel

On Tue, Feb 4, 2025 at 10:32 PM Mateusz Guzik <mjguzik@gmail.com> wrote:
>
> This costs a strlen() call when instatianating a symlink.
>
> Preferably it would be hidden behind VFS_WARN_ON (or compatible), but
> there is no such facility at the moment. With the facility in place the
> call can be patched out in production kernels.
>
> In the meantime, since the cost is being paid unconditionally, use the
> result to a fixup the bad caller.
>
> This is not expected to persist in the long run (tm).
>
> Sample splat:
> bad length passed for symlink [/tmp/syz-imagegen43743633/file0/file0] (got 131109, expected 37)
> [rest of WARN blurp goes here]
>
> Signed-off-by: Mateusz Guzik <mjguzik@gmail.com>
> ---
>
> This has a side effect of working around the panic reported in:
> https://lore.kernel.org/all/67a1e1f4.050a0220.163cdc.0063.GAE@google.com/
>
> I'm confident this merely exposed a bug in ext4, see:
> https://lore.kernel.org/all/CAGudoHEv+Diti3r0x9VmF5ixgRVKk4trYnX_skVJNkQoTMaDHg@mail.gmail.com/#t
>
> Nonethelss, should help catch future problems.
>
>  include/linux/fs.h | 13 +++++++++++++
>  1 file changed, 13 insertions(+)
>
> diff --git a/include/linux/fs.h b/include/linux/fs.h
> index be3ad155ec9f..1437a3323731 100644
> --- a/include/linux/fs.h
> +++ b/include/linux/fs.h
> @@ -791,6 +791,19 @@ struct inode {
>
>  static inline void inode_set_cached_link(struct inode *inode, char *link, int linklen)
>  {
> +       int testlen;
> +
> +       /*
> +        * TODO: patch it into a debug-only check if relevant macros show up.
> +        * In the meantime, since we are suffering strlen even on production kernels
> +        * to find the right length, do a fixup if the wrong value got passed.
> +        */
> +       testlen = strlen(link);
> +       if (testlen != linklen) {
> +               WARN_ONCE(1, "bad length passed for symlink [%s] (got %d, expected %d)",
> +                         link, linklen, testlen);

I just remembered that path_noexec thing got a CVE.

I suspect someone may try to assign one here for user-mountable disk
images triggering this warn.

Can be patched to "printk_once", but then it does not implicitly tell
which fs is messing up due to lack of a backtrace.

> +               linklen = testlen;
> +       }
>         inode->i_link = link;
>         inode->i_linklen = linklen;
>         inode->i_opflags |= IOP_CACHED_LINK;
> --
> 2.43.0
>


-- 
Mateusz Guzik <mjguzik gmail.com>

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] vfs: sanity check the length passed to inode_set_cached_link()
  2025-02-04 21:32 [PATCH] vfs: sanity check the length passed to inode_set_cached_link() Mateusz Guzik
  2025-02-04 21:46 ` Mateusz Guzik
@ 2025-02-04 22:36 ` Darrick J. Wong
  2025-02-05 12:11 ` Christian Brauner
  2025-02-05 16:12 ` Jan Kara
  3 siblings, 0 replies; 6+ messages in thread
From: Darrick J. Wong @ 2025-02-04 22:36 UTC (permalink / raw)
  To: Mateusz Guzik; +Cc: brauner, viro, jack, linux-kernel, linux-fsdevel

On Tue, Feb 04, 2025 at 10:32:07PM +0100, Mateusz Guzik wrote:
> This costs a strlen() call when instatianating a symlink.
> 
> Preferably it would be hidden behind VFS_WARN_ON (or compatible), but
> there is no such facility at the moment. With the facility in place the
> call can be patched out in production kernels.
> 
> In the meantime, since the cost is being paid unconditionally, use the
> result to a fixup the bad caller.
> 
> This is not expected to persist in the long run (tm).
> 
> Sample splat:
> bad length passed for symlink [/tmp/syz-imagegen43743633/file0/file0] (got 131109, expected 37)
> [rest of WARN blurp goes here]
> 
> Signed-off-by: Mateusz Guzik <mjguzik@gmail.com>
> ---
> 
> This has a side effect of working around the panic reported in:
> https://lore.kernel.org/all/67a1e1f4.050a0220.163cdc.0063.GAE@google.com/
> 
> I'm confident this merely exposed a bug in ext4, see:
> https://lore.kernel.org/all/CAGudoHEv+Diti3r0x9VmF5ixgRVKk4trYnX_skVJNkQoTMaDHg@mail.gmail.com/#t

Yes, ext4 should not continue to load a fast symlink where i_size >
sizeof(i_data).  I'm a little surprised that nd_terminate_link exists to
paper over this situation, but the commit adding it (035146851cfa2fe) is
from 2008 when we were all young and naïve.

But yes, would be nice to have a CONFIG_VFS_DEBUG behind which to hide
assertions so that insufficient validation in ext4 will burble up
through Ted's test infrastructure. ;)

That said, I just saw this in ext4_get_link:

	nd_terminate_link(bh->b_data, inode->i_size,
			  inode->i_sb->s_blocksize - 1);

Which is writing into a buffer head without a transaction?  Granted
symlink targets in ext4 are written out to disk with the null terminator
so I guess that's not a big deal.

--D

> 
> Nonethelss, should help catch future problems.
> 
>  include/linux/fs.h | 13 +++++++++++++
>  1 file changed, 13 insertions(+)
> 
> diff --git a/include/linux/fs.h b/include/linux/fs.h
> index be3ad155ec9f..1437a3323731 100644
> --- a/include/linux/fs.h
> +++ b/include/linux/fs.h
> @@ -791,6 +791,19 @@ struct inode {
>  
>  static inline void inode_set_cached_link(struct inode *inode, char *link, int linklen)
>  {
> +	int testlen;
> +
> +	/*
> +	 * TODO: patch it into a debug-only check if relevant macros show up.
> +	 * In the meantime, since we are suffering strlen even on production kernels
> +	 * to find the right length, do a fixup if the wrong value got passed.
> +	 */
> +	testlen = strlen(link);
> +	if (testlen != linklen) {
> +		WARN_ONCE(1, "bad length passed for symlink [%s] (got %d, expected %d)",
> +			  link, linklen, testlen);
> +		linklen = testlen;
> +	}
>  	inode->i_link = link;
>  	inode->i_linklen = linklen;
>  	inode->i_opflags |= IOP_CACHED_LINK;
> -- 
> 2.43.0
> 
> 

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] vfs: sanity check the length passed to inode_set_cached_link()
  2025-02-04 21:32 [PATCH] vfs: sanity check the length passed to inode_set_cached_link() Mateusz Guzik
  2025-02-04 21:46 ` Mateusz Guzik
  2025-02-04 22:36 ` Darrick J. Wong
@ 2025-02-05 12:11 ` Christian Brauner
  2025-02-05 16:12 ` Jan Kara
  3 siblings, 0 replies; 6+ messages in thread
From: Christian Brauner @ 2025-02-05 12:11 UTC (permalink / raw)
  To: Mateusz Guzik; +Cc: Christian Brauner, viro, jack, linux-kernel, linux-fsdevel

On Tue, 04 Feb 2025 22:32:07 +0100, Mateusz Guzik wrote:
> This costs a strlen() call when instatianating a symlink.
> 
> Preferably it would be hidden behind VFS_WARN_ON (or compatible), but
> there is no such facility at the moment. With the facility in place the
> call can be patched out in production kernels.
> 
> In the meantime, since the cost is being paid unconditionally, use the
> result to a fixup the bad caller.
> 
> [...]

Seems ok if ugly.

---

Applied to the vfs.fixes branch of the vfs/vfs.git tree.
Patches in the vfs.fixes branch should appear in linux-next soon.

Please report any outstanding bugs that were missed during review in a
new review to the original patch series allowing us to drop it.

It's encouraged to provide Acked-bys and Reviewed-bys even though the
patch has now been applied. If possible patch trailers will be updated.

Note that commit hashes shown below are subject to change due to rebase,
trailer updates or similar. If in doubt, please check the listed branch.

tree:   https://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs.git
branch: vfs.fixes

[1/1] vfs: sanity check the length passed to inode_set_cached_link()
      https://git.kernel.org/vfs/vfs/c/c1c84bb08cc7

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] vfs: sanity check the length passed to inode_set_cached_link()
  2025-02-04 21:32 [PATCH] vfs: sanity check the length passed to inode_set_cached_link() Mateusz Guzik
                   ` (2 preceding siblings ...)
  2025-02-05 12:11 ` Christian Brauner
@ 2025-02-05 16:12 ` Jan Kara
  2025-02-05 16:20   ` Mateusz Guzik
  3 siblings, 1 reply; 6+ messages in thread
From: Jan Kara @ 2025-02-05 16:12 UTC (permalink / raw)
  To: Mateusz Guzik; +Cc: brauner, viro, jack, linux-kernel, linux-fsdevel

On Tue 04-02-25 22:32:07, Mateusz Guzik wrote:
> This costs a strlen() call when instatianating a symlink.
> 
> Preferably it would be hidden behind VFS_WARN_ON (or compatible), but
> there is no such facility at the moment. With the facility in place the
> call can be patched out in production kernels.
> 
> In the meantime, since the cost is being paid unconditionally, use the
> result to a fixup the bad caller.
> 
> This is not expected to persist in the long run (tm).
> 
> Sample splat:
> bad length passed for symlink [/tmp/syz-imagegen43743633/file0/file0] (got 131109, expected 37)
> [rest of WARN blurp goes here]
> 
> Signed-off-by: Mateusz Guzik <mjguzik@gmail.com>

Yeah, it looks a bit pointless to pass the length in only to compare it
against strlen(). But as a quick fix until we figure out something more
clever it's fine I guess.

Feel free to add:

Reviewed-by: Jan Kara <jack@suse.cz>

								Honza

> ---
> 
> This has a side effect of working around the panic reported in:
> https://lore.kernel.org/all/67a1e1f4.050a0220.163cdc.0063.GAE@google.com/
> 
> I'm confident this merely exposed a bug in ext4, see:
> https://lore.kernel.org/all/CAGudoHEv+Diti3r0x9VmF5ixgRVKk4trYnX_skVJNkQoTMaDHg@mail.gmail.com/#t
> 
> Nonethelss, should help catch future problems.
> 
>  include/linux/fs.h | 13 +++++++++++++
>  1 file changed, 13 insertions(+)
> 
> diff --git a/include/linux/fs.h b/include/linux/fs.h
> index be3ad155ec9f..1437a3323731 100644
> --- a/include/linux/fs.h
> +++ b/include/linux/fs.h
> @@ -791,6 +791,19 @@ struct inode {
>  
>  static inline void inode_set_cached_link(struct inode *inode, char *link, int linklen)
>  {
> +	int testlen;
> +
> +	/*
> +	 * TODO: patch it into a debug-only check if relevant macros show up.
> +	 * In the meantime, since we are suffering strlen even on production kernels
> +	 * to find the right length, do a fixup if the wrong value got passed.
> +	 */
> +	testlen = strlen(link);
> +	if (testlen != linklen) {
> +		WARN_ONCE(1, "bad length passed for symlink [%s] (got %d, expected %d)",
> +			  link, linklen, testlen);
> +		linklen = testlen;
> +	}
>  	inode->i_link = link;
>  	inode->i_linklen = linklen;
>  	inode->i_opflags |= IOP_CACHED_LINK;
> -- 
> 2.43.0
> 
-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] vfs: sanity check the length passed to inode_set_cached_link()
  2025-02-05 16:12 ` Jan Kara
@ 2025-02-05 16:20   ` Mateusz Guzik
  0 siblings, 0 replies; 6+ messages in thread
From: Mateusz Guzik @ 2025-02-05 16:20 UTC (permalink / raw)
  To: Jan Kara; +Cc: brauner, viro, linux-kernel, linux-fsdevel

On Wed, Feb 5, 2025 at 5:12 PM Jan Kara <jack@suse.cz> wrote:
>
> On Tue 04-02-25 22:32:07, Mateusz Guzik wrote:
> > This costs a strlen() call when instatianating a symlink.
> >
> > Preferably it would be hidden behind VFS_WARN_ON (or compatible), but
> > there is no such facility at the moment. With the facility in place the
> > call can be patched out in production kernels.
> >
> > In the meantime, since the cost is being paid unconditionally, use the
> > result to a fixup the bad caller.
> >
> > This is not expected to persist in the long run (tm).
> >
> > Sample splat:
> > bad length passed for symlink [/tmp/syz-imagegen43743633/file0/file0] (got 131109, expected 37)
> > [rest of WARN blurp goes here]
> >
> > Signed-off-by: Mateusz Guzik <mjguzik@gmail.com>
>
> Yeah, it looks a bit pointless to pass the length in only to compare it
> against strlen(). But as a quick fix until we figure out something more
> clever it's fine I guess.
>

There is some dayjob stuff I need to take care of.

Once I'm both out from under the ruble *and* bored enough, I'll post
provisional support for VFS_WARN_ON and VFS_BUG_ON macros.

But that's for -next.

> Feel free to add:
>
> Reviewed-by: Jan Kara <jack@suse.cz>
>
>                                                                 Honza
>
> > ---
> >
> > This has a side effect of working around the panic reported in:
> > https://lore.kernel.org/all/67a1e1f4.050a0220.163cdc.0063.GAE@google.com/
> >
> > I'm confident this merely exposed a bug in ext4, see:
> > https://lore.kernel.org/all/CAGudoHEv+Diti3r0x9VmF5ixgRVKk4trYnX_skVJNkQoTMaDHg@mail.gmail.com/#t
> >
> > Nonethelss, should help catch future problems.
> >
> >  include/linux/fs.h | 13 +++++++++++++
> >  1 file changed, 13 insertions(+)
> >
> > diff --git a/include/linux/fs.h b/include/linux/fs.h
> > index be3ad155ec9f..1437a3323731 100644
> > --- a/include/linux/fs.h
> > +++ b/include/linux/fs.h
> > @@ -791,6 +791,19 @@ struct inode {
> >
> >  static inline void inode_set_cached_link(struct inode *inode, char *link, int linklen)
> >  {
> > +     int testlen;
> > +
> > +     /*
> > +      * TODO: patch it into a debug-only check if relevant macros show up.
> > +      * In the meantime, since we are suffering strlen even on production kernels
> > +      * to find the right length, do a fixup if the wrong value got passed.
> > +      */
> > +     testlen = strlen(link);
> > +     if (testlen != linklen) {
> > +             WARN_ONCE(1, "bad length passed for symlink [%s] (got %d, expected %d)",
> > +                       link, linklen, testlen);
> > +             linklen = testlen;
> > +     }
> >       inode->i_link = link;
> >       inode->i_linklen = linklen;
> >       inode->i_opflags |= IOP_CACHED_LINK;
> > --
> > 2.43.0
> >
> --
> Jan Kara <jack@suse.com>
> SUSE Labs, CR



-- 
Mateusz Guzik <mjguzik gmail.com>

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2025-02-05 16:20 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-02-04 21:32 [PATCH] vfs: sanity check the length passed to inode_set_cached_link() Mateusz Guzik
2025-02-04 21:46 ` Mateusz Guzik
2025-02-04 22:36 ` Darrick J. Wong
2025-02-05 12:11 ` Christian Brauner
2025-02-05 16:12 ` Jan Kara
2025-02-05 16:20   ` Mateusz Guzik

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).