linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3] exfat: fix out-of-bounds in exfat_nls_to_ucs2()
@ 2025-10-13 13:47 Jeongjun Park
  2025-10-13 16:46 ` Pali Rohár
  0 siblings, 1 reply; 5+ messages in thread
From: Jeongjun Park @ 2025-10-13 13:47 UTC (permalink / raw)
  To: Namjae Jeon, Sungjong Seo
  Cc: Yuezhang Mo, Al Viro, pali, linux-fsdevel, linux-kernel, stable,
	syzbot+98cc76a76de46b3714d4, Jeongjun Park

Since the len argument value passed to exfat_ioctl_set_volume_label()
from exfat_nls_to_utf16() is passed 1 too large, an out-of-bounds read
occurs when dereferencing p_cstring in exfat_nls_to_ucs2() later.

And because of the NLS_NAME_OVERLEN macro, another error occurs when
creating a file with a period at the end using utf8 and other iocharsets,
so the NLS_NAME_OVERLEN macro should be removed and the len argument value
should be passed as FSLABEL_MAX - 1.

Cc: <stable@vger.kernel.org>
Reported-by: syzbot+98cc76a76de46b3714d4@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=98cc76a76de46b3714d4
Fixes: 370e812b3ec1 ("exfat: add nls operations")
Signed-off-by: Jeongjun Park <aha310510@gmail.com>
---
 fs/exfat/file.c | 2 +-
 fs/exfat/nls.c  | 3 ---
 2 files changed, 1 insertion(+), 4 deletions(-)

diff --git a/fs/exfat/file.c b/fs/exfat/file.c
index f246cf439588..7ce0fb6f2564 100644
--- a/fs/exfat/file.c
+++ b/fs/exfat/file.c
@@ -521,7 +521,7 @@ static int exfat_ioctl_set_volume_label(struct super_block *sb,
 
 	memset(&uniname, 0, sizeof(uniname));
 	if (label[0]) {
-		ret = exfat_nls_to_utf16(sb, label, FSLABEL_MAX,
+		ret = exfat_nls_to_utf16(sb, label, FSLABEL_MAX - 1,
 					 &uniname, &lossy);
 		if (ret < 0)
 			return ret;
diff --git a/fs/exfat/nls.c b/fs/exfat/nls.c
index 8243d94ceaf4..57db08a5271c 100644
--- a/fs/exfat/nls.c
+++ b/fs/exfat/nls.c
@@ -616,9 +616,6 @@ static int exfat_nls_to_ucs2(struct super_block *sb,
 		unilen++;
 	}
 
-	if (p_cstring[i] != '\0')
-		lossy |= NLS_NAME_OVERLEN;
-
 	*uniname = '\0';
 	p_uniname->name_len = unilen;
 	p_uniname->name_hash = exfat_calc_chksum16(upname, unilen << 1, 0,
--

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

* Re: [PATCH v3] exfat: fix out-of-bounds in exfat_nls_to_ucs2()
  2025-10-13 13:47 [PATCH v3] exfat: fix out-of-bounds in exfat_nls_to_ucs2() Jeongjun Park
@ 2025-10-13 16:46 ` Pali Rohár
  2025-10-13 23:29   ` Namjae Jeon
  0 siblings, 1 reply; 5+ messages in thread
From: Pali Rohár @ 2025-10-13 16:46 UTC (permalink / raw)
  To: Jeongjun Park
  Cc: Ethan Ferguson, Namjae Jeon, Sungjong Seo, Yuezhang Mo, Al Viro,
	linux-fsdevel, linux-kernel, stable, syzbot+98cc76a76de46b3714d4

On Monday 13 October 2025 22:47:08 Jeongjun Park wrote:
> Since the len argument value passed to exfat_ioctl_set_volume_label()
> from exfat_nls_to_utf16() is passed 1 too large, an out-of-bounds read
> occurs when dereferencing p_cstring in exfat_nls_to_ucs2() later.
> 
> And because of the NLS_NAME_OVERLEN macro, another error occurs when
> creating a file with a period at the end using utf8 and other iocharsets,
> so the NLS_NAME_OVERLEN macro should be removed and the len argument value
> should be passed as FSLABEL_MAX - 1.
> 
> Cc: <stable@vger.kernel.org>
> Reported-by: syzbot+98cc76a76de46b3714d4@syzkaller.appspotmail.com
> Closes: https://syzkaller.appspot.com/bug?extid=98cc76a76de46b3714d4
> Fixes: 370e812b3ec1 ("exfat: add nls operations")

Fixes: line is for sure wrong as the affected
exfat_ioctl_set_volume_label function is not available in the mentioned
commit.

I guess it should be commit d01579d590f72d2d91405b708e96f6169f24775a.

Now I have looked at that commit and I think I finally understood what
was the issue. exfat_nls_to_utf16() function is written in a way that
it expects null-term string and its strlen as 3rd argument.

This was achieved for all code paths except the new one introduced in
that commit. "label" is declared as char label[FSLABEL_MAX]; so the
FSLABEL_MAX argument in exfat_nls_to_utf16() is effectively
sizeof(label). And here comes the problem, it should have been
strlen(label) (or rather strnlen(label, sizeof(label)-1) in case
userspace pass non-nul term string).

So the change below to FSLABEL_MAX - 1 effectively fix the overflow
problem. But not the usage of exfat_nls_to_utf16.

API of FS_IOC_SETFSLABEL is defined to always take nul-term string:
https://man7.org/linux/man-pages/man2/fs_ioc_setfslabel.2const.html

And size of buffer is not the length of nul-term string. We should
discard anything after nul-term byte.

So in my opinion exfat_ioctl_set_volume_label() should be fixed in a way
it would call exfat_nls_to_utf16() with 3rd argument passed as:

  strnlen(label, sizeof(label) - 1)

or

  strnlen(label, FSLABEL_MAX - 1)

Or personally I prefer to store this length into new variable (e.g.
label_len) and then passing it to exfat_nls_to_utf16() function.
For example:

  ret = exfat_nls_to_utf16(sb, label, label_len, &uniname, &lossy);

Adding Ethan to CC as author of the mentioned commit.


And about NLS_NAME_OVERLEN, it is being used by the
__exfat_resolve_path() function. So removal of the "setting" of
NLS_NAME_OVERLEN bit but still checking if the NLS_NAME_OVERLEN bit is
set is quite wrong.


Namjae, could you re-check my analysis? Just to be sure that I have not
misunderstood something. It is better to do proper analysis than having
incomplete or incorrect fix.

> Signed-off-by: Jeongjun Park <aha310510@gmail.com>
> ---
>  fs/exfat/file.c | 2 +-
>  fs/exfat/nls.c  | 3 ---
>  2 files changed, 1 insertion(+), 4 deletions(-)
> 
> diff --git a/fs/exfat/file.c b/fs/exfat/file.c
> index f246cf439588..7ce0fb6f2564 100644
> --- a/fs/exfat/file.c
> +++ b/fs/exfat/file.c
> @@ -521,7 +521,7 @@ static int exfat_ioctl_set_volume_label(struct super_block *sb,
>  
>  	memset(&uniname, 0, sizeof(uniname));
>  	if (label[0]) {
> -		ret = exfat_nls_to_utf16(sb, label, FSLABEL_MAX,
> +		ret = exfat_nls_to_utf16(sb, label, FSLABEL_MAX - 1,
>  					 &uniname, &lossy);
>  		if (ret < 0)
>  			return ret;
> diff --git a/fs/exfat/nls.c b/fs/exfat/nls.c
> index 8243d94ceaf4..57db08a5271c 100644
> --- a/fs/exfat/nls.c
> +++ b/fs/exfat/nls.c
> @@ -616,9 +616,6 @@ static int exfat_nls_to_ucs2(struct super_block *sb,
>  		unilen++;
>  	}
>  
> -	if (p_cstring[i] != '\0')
> -		lossy |= NLS_NAME_OVERLEN;
> -
>  	*uniname = '\0';
>  	p_uniname->name_len = unilen;
>  	p_uniname->name_hash = exfat_calc_chksum16(upname, unilen << 1, 0,
> --

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

* Re: [PATCH v3] exfat: fix out-of-bounds in exfat_nls_to_ucs2()
  2025-10-13 16:46 ` Pali Rohár
@ 2025-10-13 23:29   ` Namjae Jeon
  2025-10-14  6:04     ` Jeongjun Park
  0 siblings, 1 reply; 5+ messages in thread
From: Namjae Jeon @ 2025-10-13 23:29 UTC (permalink / raw)
  To: Pali Rohár
  Cc: Jeongjun Park, Ethan Ferguson, Sungjong Seo, Yuezhang Mo, Al Viro,
	linux-fsdevel, linux-kernel, stable, syzbot+98cc76a76de46b3714d4

On Tue, Oct 14, 2025 at 1:46 AM Pali Rohár <pali@kernel.org> wrote:
>
> On Monday 13 October 2025 22:47:08 Jeongjun Park wrote:
> > Since the len argument value passed to exfat_ioctl_set_volume_label()
> > from exfat_nls_to_utf16() is passed 1 too large, an out-of-bounds read
> > occurs when dereferencing p_cstring in exfat_nls_to_ucs2() later.
> >
> > And because of the NLS_NAME_OVERLEN macro, another error occurs when
> > creating a file with a period at the end using utf8 and other iocharsets,
> > so the NLS_NAME_OVERLEN macro should be removed and the len argument value
> > should be passed as FSLABEL_MAX - 1.
> >
> > Cc: <stable@vger.kernel.org>
> > Reported-by: syzbot+98cc76a76de46b3714d4@syzkaller.appspotmail.com
> > Closes: https://syzkaller.appspot.com/bug?extid=98cc76a76de46b3714d4
> > Fixes: 370e812b3ec1 ("exfat: add nls operations")
>
> Fixes: line is for sure wrong as the affected
> exfat_ioctl_set_volume_label function is not available in the mentioned
> commit.
>
> I guess it should be commit d01579d590f72d2d91405b708e96f6169f24775a.
>
> Now I have looked at that commit and I think I finally understood what
> was the issue. exfat_nls_to_utf16() function is written in a way that
> it expects null-term string and its strlen as 3rd argument.
>
> This was achieved for all code paths except the new one introduced in
> that commit. "label" is declared as char label[FSLABEL_MAX]; so the
> FSLABEL_MAX argument in exfat_nls_to_utf16() is effectively
> sizeof(label). And here comes the problem, it should have been
> strlen(label) (or rather strnlen(label, sizeof(label)-1) in case
> userspace pass non-nul term string).
>
> So the change below to FSLABEL_MAX - 1 effectively fix the overflow
> problem. But not the usage of exfat_nls_to_utf16.
>
> API of FS_IOC_SETFSLABEL is defined to always take nul-term string:
> https://man7.org/linux/man-pages/man2/fs_ioc_setfslabel.2const.html
>
> And size of buffer is not the length of nul-term string. We should
> discard anything after nul-term byte.
>
> So in my opinion exfat_ioctl_set_volume_label() should be fixed in a way
> it would call exfat_nls_to_utf16() with 3rd argument passed as:
>
>   strnlen(label, sizeof(label) - 1)
>
> or
>
>   strnlen(label, FSLABEL_MAX - 1)
>
> Or personally I prefer to store this length into new variable (e.g.
> label_len) and then passing it to exfat_nls_to_utf16() function.
> For example:
>
>   ret = exfat_nls_to_utf16(sb, label, label_len, &uniname, &lossy);
Right, I agree.
>
> Adding Ethan to CC as author of the mentioned commit.
>
>
> And about NLS_NAME_OVERLEN, it is being used by the
> __exfat_resolve_path() function. So removal of the "setting" of
> NLS_NAME_OVERLEN bit but still checking if the NLS_NAME_OVERLEN bit is
> set is quite wrong.
Right, The use of NLS_NAME_OVERLEN in __exfat_resolve_path() and
in the header should also be removed.
>
>
> Namjae, could you re-check my analysis? Just to be sure that I have not
> misunderstood something. It is better to do proper analysis than having
> incomplete or incorrect fix.
Yes, I agree with your analysis.
Thanks!
>
> > Signed-off-by: Jeongjun Park <aha310510@gmail.com>
> > ---
> >  fs/exfat/file.c | 2 +-
> >  fs/exfat/nls.c  | 3 ---
> >  2 files changed, 1 insertion(+), 4 deletions(-)
> >
> > diff --git a/fs/exfat/file.c b/fs/exfat/file.c
> > index f246cf439588..7ce0fb6f2564 100644
> > --- a/fs/exfat/file.c
> > +++ b/fs/exfat/file.c
> > @@ -521,7 +521,7 @@ static int exfat_ioctl_set_volume_label(struct super_block *sb,
> >
> >       memset(&uniname, 0, sizeof(uniname));
> >       if (label[0]) {
> > -             ret = exfat_nls_to_utf16(sb, label, FSLABEL_MAX,
> > +             ret = exfat_nls_to_utf16(sb, label, FSLABEL_MAX - 1,
> >                                        &uniname, &lossy);
> >               if (ret < 0)
> >                       return ret;
> > diff --git a/fs/exfat/nls.c b/fs/exfat/nls.c
> > index 8243d94ceaf4..57db08a5271c 100644
> > --- a/fs/exfat/nls.c
> > +++ b/fs/exfat/nls.c
> > @@ -616,9 +616,6 @@ static int exfat_nls_to_ucs2(struct super_block *sb,
> >               unilen++;
> >       }
> >
> > -     if (p_cstring[i] != '\0')
> > -             lossy |= NLS_NAME_OVERLEN;
> > -
> >       *uniname = '\0';
> >       p_uniname->name_len = unilen;
> >       p_uniname->name_hash = exfat_calc_chksum16(upname, unilen << 1, 0,
> > --

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

* Re: [PATCH v3] exfat: fix out-of-bounds in exfat_nls_to_ucs2()
  2025-10-13 23:29   ` Namjae Jeon
@ 2025-10-14  6:04     ` Jeongjun Park
  2025-10-14  6:09       ` Namjae Jeon
  0 siblings, 1 reply; 5+ messages in thread
From: Jeongjun Park @ 2025-10-14  6:04 UTC (permalink / raw)
  To: Namjae Jeon
  Cc: Pali Rohár, Ethan Ferguson, Sungjong Seo, Yuezhang Mo,
	Al Viro, linux-fsdevel, linux-kernel, stable,
	syzbot+98cc76a76de46b3714d4

Hi Namjae,

Namjae Jeon <linkinjeon@kernel.org> wrote:
>
> On Tue, Oct 14, 2025 at 1:46 AM Pali Rohár <pali@kernel.org> wrote:
> >
> > On Monday 13 October 2025 22:47:08 Jeongjun Park wrote:
> > > Since the len argument value passed to exfat_ioctl_set_volume_label()
> > > from exfat_nls_to_utf16() is passed 1 too large, an out-of-bounds read
> > > occurs when dereferencing p_cstring in exfat_nls_to_ucs2() later.
> > >
> > > And because of the NLS_NAME_OVERLEN macro, another error occurs when
> > > creating a file with a period at the end using utf8 and other iocharsets,
> > > so the NLS_NAME_OVERLEN macro should be removed and the len argument value
> > > should be passed as FSLABEL_MAX - 1.
> > >
> > > Cc: <stable@vger.kernel.org>
> > > Reported-by: syzbot+98cc76a76de46b3714d4@syzkaller.appspotmail.com
> > > Closes: https://syzkaller.appspot.com/bug?extid=98cc76a76de46b3714d4
> > > Fixes: 370e812b3ec1 ("exfat: add nls operations")
> >
> > Fixes: line is for sure wrong as the affected
> > exfat_ioctl_set_volume_label function is not available in the mentioned
> > commit.
> >
> > I guess it should be commit d01579d590f72d2d91405b708e96f6169f24775a.
> >
> > Now I have looked at that commit and I think I finally understood what
> > was the issue. exfat_nls_to_utf16() function is written in a way that
> > it expects null-term string and its strlen as 3rd argument.
> >
> > This was achieved for all code paths except the new one introduced in
> > that commit. "label" is declared as char label[FSLABEL_MAX]; so the
> > FSLABEL_MAX argument in exfat_nls_to_utf16() is effectively
> > sizeof(label). And here comes the problem, it should have been
> > strlen(label) (or rather strnlen(label, sizeof(label)-1) in case
> > userspace pass non-nul term string).
> >
> > So the change below to FSLABEL_MAX - 1 effectively fix the overflow
> > problem. But not the usage of exfat_nls_to_utf16.
> >
> > API of FS_IOC_SETFSLABEL is defined to always take nul-term string:
> > https://man7.org/linux/man-pages/man2/fs_ioc_setfslabel.2const.html
> >
> > And size of buffer is not the length of nul-term string. We should
> > discard anything after nul-term byte.
> >
> > So in my opinion exfat_ioctl_set_volume_label() should be fixed in a way
> > it would call exfat_nls_to_utf16() with 3rd argument passed as:
> >
> >   strnlen(label, sizeof(label) - 1)
> >
> > or
> >
> >   strnlen(label, FSLABEL_MAX - 1)
> >
> > Or personally I prefer to store this length into new variable (e.g.
> > label_len) and then passing it to exfat_nls_to_utf16() function.
> > For example:
> >
> >   ret = exfat_nls_to_utf16(sb, label, label_len, &uniname, &lossy);
> Right, I agree.
> >
> > Adding Ethan to CC as author of the mentioned commit.
> >
> >
> > And about NLS_NAME_OVERLEN, it is being used by the
> > __exfat_resolve_path() function. So removal of the "setting" of
> > NLS_NAME_OVERLEN bit but still checking if the NLS_NAME_OVERLEN bit is
> > set is quite wrong.
> Right, The use of NLS_NAME_OVERLEN in __exfat_resolve_path() and
> in the header should also be removed.

I'll write a patch that reflects this analysis and send it to you right
away.

However, if do this, NLS_NAME_OVERLEN macro is no longer used anywhere
in exfat, so does that mean should also remove the macro define itself?

Regards,
Jeongjun Park

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

* Re: [PATCH v3] exfat: fix out-of-bounds in exfat_nls_to_ucs2()
  2025-10-14  6:04     ` Jeongjun Park
@ 2025-10-14  6:09       ` Namjae Jeon
  0 siblings, 0 replies; 5+ messages in thread
From: Namjae Jeon @ 2025-10-14  6:09 UTC (permalink / raw)
  To: Jeongjun Park
  Cc: Pali Rohár, Ethan Ferguson, Sungjong Seo, Yuezhang Mo,
	Al Viro, linux-fsdevel, linux-kernel, stable,
	syzbot+98cc76a76de46b3714d4

On Tue, Oct 14, 2025 at 3:04 PM Jeongjun Park <aha310510@gmail.com> wrote:
>
> Hi Namjae,
>
> Namjae Jeon <linkinjeon@kernel.org> wrote:
> >
> > On Tue, Oct 14, 2025 at 1:46 AM Pali Rohár <pali@kernel.org> wrote:
> > >
> > > On Monday 13 October 2025 22:47:08 Jeongjun Park wrote:
> > > > Since the len argument value passed to exfat_ioctl_set_volume_label()
> > > > from exfat_nls_to_utf16() is passed 1 too large, an out-of-bounds read
> > > > occurs when dereferencing p_cstring in exfat_nls_to_ucs2() later.
> > > >
> > > > And because of the NLS_NAME_OVERLEN macro, another error occurs when
> > > > creating a file with a period at the end using utf8 and other iocharsets,
> > > > so the NLS_NAME_OVERLEN macro should be removed and the len argument value
> > > > should be passed as FSLABEL_MAX - 1.
> > > >
> > > > Cc: <stable@vger.kernel.org>
> > > > Reported-by: syzbot+98cc76a76de46b3714d4@syzkaller.appspotmail.com
> > > > Closes: https://syzkaller.appspot.com/bug?extid=98cc76a76de46b3714d4
> > > > Fixes: 370e812b3ec1 ("exfat: add nls operations")
> > >
> > > Fixes: line is for sure wrong as the affected
> > > exfat_ioctl_set_volume_label function is not available in the mentioned
> > > commit.
> > >
> > > I guess it should be commit d01579d590f72d2d91405b708e96f6169f24775a.
> > >
> > > Now I have looked at that commit and I think I finally understood what
> > > was the issue. exfat_nls_to_utf16() function is written in a way that
> > > it expects null-term string and its strlen as 3rd argument.
> > >
> > > This was achieved for all code paths except the new one introduced in
> > > that commit. "label" is declared as char label[FSLABEL_MAX]; so the
> > > FSLABEL_MAX argument in exfat_nls_to_utf16() is effectively
> > > sizeof(label). And here comes the problem, it should have been
> > > strlen(label) (or rather strnlen(label, sizeof(label)-1) in case
> > > userspace pass non-nul term string).
> > >
> > > So the change below to FSLABEL_MAX - 1 effectively fix the overflow
> > > problem. But not the usage of exfat_nls_to_utf16.
> > >
> > > API of FS_IOC_SETFSLABEL is defined to always take nul-term string:
> > > https://man7.org/linux/man-pages/man2/fs_ioc_setfslabel.2const.html
> > >
> > > And size of buffer is not the length of nul-term string. We should
> > > discard anything after nul-term byte.
> > >
> > > So in my opinion exfat_ioctl_set_volume_label() should be fixed in a way
> > > it would call exfat_nls_to_utf16() with 3rd argument passed as:
> > >
> > >   strnlen(label, sizeof(label) - 1)
> > >
> > > or
> > >
> > >   strnlen(label, FSLABEL_MAX - 1)
> > >
> > > Or personally I prefer to store this length into new variable (e.g.
> > > label_len) and then passing it to exfat_nls_to_utf16() function.
> > > For example:
> > >
> > >   ret = exfat_nls_to_utf16(sb, label, label_len, &uniname, &lossy);
> > Right, I agree.
> > >
> > > Adding Ethan to CC as author of the mentioned commit.
> > >
> > >
> > > And about NLS_NAME_OVERLEN, it is being used by the
> > > __exfat_resolve_path() function. So removal of the "setting" of
> > > NLS_NAME_OVERLEN bit but still checking if the NLS_NAME_OVERLEN bit is
> > > set is quite wrong.
> > Right, The use of NLS_NAME_OVERLEN in __exfat_resolve_path() and
> > in the header should also be removed.
>
> I'll write a patch that reflects this analysis and send it to you right
> away.
>
> However, if do this, NLS_NAME_OVERLEN macro is no longer used anywhere
> in exfat, so does that mean should also remove the macro define itself?
Yes, So I replied that it should be removed in the header.
Thanks.
>
> Regards,
> Jeongjun Park

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

end of thread, other threads:[~2025-10-14  6:10 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-10-13 13:47 [PATCH v3] exfat: fix out-of-bounds in exfat_nls_to_ucs2() Jeongjun Park
2025-10-13 16:46 ` Pali Rohár
2025-10-13 23:29   ` Namjae Jeon
2025-10-14  6:04     ` Jeongjun Park
2025-10-14  6:09       ` Namjae Jeon

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).