[1/2] libio: Fix gconv module reference counter overflow in swscanf

Message ID 20260429145934.278803-2-fberat@redhat.com (mailing list archive)
State Committed
Commit 0981c03c2752b5f12ede24e6e696d5a29f7c6396
Headers
Series Fix gconv reference count overflow in swscanf |

Checks

Context Check Description
redhat-pt-bot/TryBot-apply_patch success Patch applied to master at the time it was sent

Commit Message

Frederic Berat April 29, 2026, 2:59 p.m. UTC
  The swscanf family of functions creates a wide-oriented FILE stream
on the stack. Initialization of this stream invokes `_IO_fwide`, which
clones the global locale's gconv transformation steps via
`__wcsmbs_clone_conv`. This increments the reference counter (`__counter`)
of the gconv module.

Because the FILE stream is stack-allocated, `fclose` cannot be called,
and so `__gconv_release_step` is never invoked. The counter leaks,
eventually hitting the 32-bit integer overflow limit and aborting the
process.

To resolve this, we introduce `_IO_wstrfile_fclose_stack`, a dedicated
cleanup function for stack-allocated FILE streams. This function invokes
`_IO_FINISH` and correctly releases the gconv steps via
`__gconv_release_step` without attempting to `free` the FILE pointer.
This cleanup function is then hooked into all variants of swscanf right
before they return.
---
 libio/iofwide.c                                   | 15 +++++++++++++++
 libio/iovswscanf.c                                |  4 +++-
 libio/libioP.h                                    |  1 +
 libio/swscanf.c                                   |  2 +-
 .../ldbl-128ibm-compat/ieee128-isoc23_swscanf.c   |  2 +-
 .../ldbl-128ibm-compat/ieee128-isoc23_vswscanf.c  |  4 +++-
 .../ldbl-128ibm-compat/ieee128-isoc99_swscanf.c   |  2 +-
 .../ldbl-128ibm-compat/ieee128-isoc99_vswscanf.c  |  4 +++-
 .../ieee754/ldbl-128ibm-compat/ieee128-swscanf.c  |  2 +-
 .../ieee754/ldbl-128ibm-compat/ieee128-vswscanf.c |  4 +++-
 sysdeps/ieee754/ldbl-opt/nldbl-compat.c           | 12 +++++++++---
 wcsmbs/isoc23_swscanf.c                           |  2 +-
 wcsmbs/isoc23_vswscanf.c                          |  4 +++-
 wcsmbs/isoc99_swscanf.c                           |  2 +-
 wcsmbs/isoc99_vswscanf.c                          |  4 +++-
 15 files changed, 49 insertions(+), 15 deletions(-)
  

Comments

Adhemerval Zanella Netto April 30, 2026, 6:42 p.m. UTC | #1
On 29/04/26 11:59, Frédéric Bérat wrote:
> The swscanf family of functions creates a wide-oriented FILE stream
> on the stack. Initialization of this stream invokes `_IO_fwide`, which
> clones the global locale's gconv transformation steps via
> `__wcsmbs_clone_conv`. This increments the reference counter (`__counter`)
> of the gconv module.
> 
> Because the FILE stream is stack-allocated, `fclose` cannot be called,
> and so `__gconv_release_step` is never invoked. The counter leaks,
> eventually hitting the 32-bit integer overflow limit and aborting the
> process.
> 
> To resolve this, we introduce `_IO_wstrfile_fclose_stack`, a dedicated
> cleanup function for stack-allocated FILE streams. This function invokes
> `_IO_FINISH` and correctly releases the gconv steps via
> `__gconv_release_step` without attempting to `free` the FILE pointer.
> This cleanup function is then hooked into all variants of swscanf right
> before they return.

LGTM, thanks.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>

> ---
>  libio/iofwide.c                                   | 15 +++++++++++++++
>  libio/iovswscanf.c                                |  4 +++-
>  libio/libioP.h                                    |  1 +
>  libio/swscanf.c                                   |  2 +-
>  .../ldbl-128ibm-compat/ieee128-isoc23_swscanf.c   |  2 +-
>  .../ldbl-128ibm-compat/ieee128-isoc23_vswscanf.c  |  4 +++-
>  .../ldbl-128ibm-compat/ieee128-isoc99_swscanf.c   |  2 +-
>  .../ldbl-128ibm-compat/ieee128-isoc99_vswscanf.c  |  4 +++-
>  .../ieee754/ldbl-128ibm-compat/ieee128-swscanf.c  |  2 +-
>  .../ieee754/ldbl-128ibm-compat/ieee128-vswscanf.c |  4 +++-
>  sysdeps/ieee754/ldbl-opt/nldbl-compat.c           | 12 +++++++++---
>  wcsmbs/isoc23_swscanf.c                           |  2 +-
>  wcsmbs/isoc23_vswscanf.c                          |  4 +++-
>  wcsmbs/isoc99_swscanf.c                           |  2 +-
>  wcsmbs/isoc99_vswscanf.c                          |  4 +++-
>  15 files changed, 49 insertions(+), 15 deletions(-)
> 
> diff --git a/libio/iofwide.c b/libio/iofwide.c
> index 8e71fdd21b..d016aa33ea 100644
> --- a/libio/iofwide.c
> +++ b/libio/iofwide.c
> @@ -251,3 +251,18 @@ __libio_codecvt_length (struct _IO_codecvt *codecvt, __mbstate_t *statep,
>  
>    return result;
>  }
> +
> +void
> +_IO_wstrfile_fclose_stack (FILE *fp)
> +{
> +  _IO_FINISH (fp);
> +  if (fp->_mode > 0)
> +    {
> +      struct _IO_codecvt *cc = fp->_codecvt;
> +
> +      __libc_lock_lock (__gconv_lock);
> +      __gconv_release_step (cc->__cd_in.step);
> +      __gconv_release_step (cc->__cd_out.step);
> +      __libc_lock_unlock (__gconv_lock);
> +    }
> +}
> diff --git a/libio/iovswscanf.c b/libio/iovswscanf.c
> index a97f4eed1e..986d94178d 100644
> --- a/libio/iovswscanf.c
> +++ b/libio/iovswscanf.c
> @@ -38,6 +38,8 @@ __vswscanf (const wchar_t *string, const wchar_t *format, va_list args)
>    _IO_strfile sf;
>    struct _IO_wide_data wd;
>    FILE *f = _IO_strfile_readw (&sf, &wd, string);
> -  return __vfwscanf_internal (f, format, args, 0);
> +  int done = __vfwscanf_internal (f, format, args, 0);
> +  _IO_wstrfile_fclose_stack (f);
> +  return done;
>  }
>  ldbl_weak_alias (__vswscanf, vswscanf)
> diff --git a/libio/libioP.h b/libio/libioP.h
> index 1485d22619..1d4217e82a 100644
> --- a/libio/libioP.h
> +++ b/libio/libioP.h
> @@ -645,6 +645,7 @@ extern FILE* _IO_new_file_fopen (FILE *, const char *, const char *,
>  				     int);
>  extern void _IO_no_init (FILE *, int, int, struct _IO_wide_data *,
>  			 const struct _IO_jump_t *) __THROW;
> +extern void _IO_wstrfile_fclose_stack (FILE *) attribute_hidden;
>  extern void _IO_new_file_init_internal (struct _IO_FILE_plus *)
>    __THROW attribute_hidden;
>  extern FILE* _IO_new_file_setbuf (FILE *, char *, ssize_t);
> diff --git a/libio/swscanf.c b/libio/swscanf.c
> index d36ef48258..583253a7dc 100644
> --- a/libio/swscanf.c
> +++ b/libio/swscanf.c
> @@ -37,7 +37,7 @@ __swscanf (const wchar_t *s, const wchar_t *format, ...)
>    va_start (arg, format);
>    done = __vfwscanf_internal (f, format, arg, 0);
>    va_end (arg);
> -
> +  _IO_wstrfile_fclose_stack (f);
>    return done;
>  }
>  ldbl_strong_alias (__swscanf, swscanf)
> diff --git a/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-isoc23_swscanf.c b/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-isoc23_swscanf.c
> index 6b3915357f..2e95cdf010 100644
> --- a/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-isoc23_swscanf.c
> +++ b/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-isoc23_swscanf.c
> @@ -35,7 +35,7 @@ ___ieee128_isoc23_swscanf (const wchar_t *string, const wchar_t *format, ...)
>    va_start (ap, format);
>    done = __vfwscanf_internal (fp, format, ap, mode_flags);
>    va_end (ap);
> -
> +  _IO_wstrfile_fclose_stack (fp);
>    return done;
>  }
>  strong_alias (___ieee128_isoc23_swscanf, __isoc23_swscanfieee128)
> diff --git a/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-isoc23_vswscanf.c b/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-isoc23_vswscanf.c
> index 295321147c..857903b2a6 100644
> --- a/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-isoc23_vswscanf.c
> +++ b/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-isoc23_vswscanf.c
> @@ -28,6 +28,8 @@ ___ieee128_isoc23_vswscanf (wchar_t *string, const wchar_t *format, va_list ap)
>    FILE *fp = _IO_strfile_readw (&sf, &wd, string);
>    int mode_flags =
>      SCANF_ISOC99_A | SCANF_ISOC23_BIN_CST | SCANF_LDBL_USES_FLOAT128;
> -  return __vfwscanf_internal (fp, format, ap, mode_flags);
> +  int done = __vfwscanf_internal (fp, format, ap, mode_flags);
> +  _IO_wstrfile_fclose_stack (fp);
> +  return done;
>  }
>  strong_alias (___ieee128_isoc23_vswscanf, __isoc23_vswscanfieee128)
> diff --git a/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-isoc99_swscanf.c b/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-isoc99_swscanf.c
> index 19b71940dd..2280bae1d0 100644
> --- a/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-isoc99_swscanf.c
> +++ b/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-isoc99_swscanf.c
> @@ -34,7 +34,7 @@ ___ieee128_isoc99_swscanf (const wchar_t *string, const wchar_t *format, ...)
>    va_start (ap, format);
>    done = __vfwscanf_internal (fp, format, ap, mode_flags);
>    va_end (ap);
> -
> +  _IO_wstrfile_fclose_stack (fp);
>    return done;
>  }
>  strong_alias (___ieee128_isoc99_swscanf, __isoc99_swscanfieee128)
> diff --git a/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-isoc99_vswscanf.c b/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-isoc99_vswscanf.c
> index a1b51cb5b6..21cf25610a 100644
> --- a/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-isoc99_vswscanf.c
> +++ b/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-isoc99_vswscanf.c
> @@ -27,6 +27,8 @@ ___ieee128_isoc99_vswscanf (wchar_t *string, const wchar_t *format, va_list ap)
>    struct _IO_wide_data wd;
>    FILE *fp = _IO_strfile_readw (&sf, &wd, string);
>    int mode_flags = SCANF_ISOC99_A | SCANF_LDBL_USES_FLOAT128;
> -  return __vfwscanf_internal (fp, format, ap, mode_flags);
> +  int done = __vfwscanf_internal (fp, format, ap, mode_flags);
> +  _IO_wstrfile_fclose_stack (fp);
> +  return done;
>  }
>  strong_alias (___ieee128_isoc99_vswscanf, __isoc99_vswscanfieee128)
> diff --git a/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-swscanf.c b/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-swscanf.c
> index 7124ecb67c..fdfdf2b65c 100644
> --- a/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-swscanf.c
> +++ b/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-swscanf.c
> @@ -34,7 +34,7 @@ ___ieee128_swscanf (const wchar_t *string, const wchar_t *format, ...)
>    done = __vfwscanf_internal (fp, format, ap,
>  			      SCANF_LDBL_USES_FLOAT128);
>    va_end (ap);
> -
> +  _IO_wstrfile_fclose_stack (fp);
>    return done;
>  }
>  strong_alias (___ieee128_swscanf, __swscanfieee128)
> diff --git a/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-vswscanf.c b/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-vswscanf.c
> index 8294582c48..b860a8e87d 100644
> --- a/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-vswscanf.c
> +++ b/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-vswscanf.c
> @@ -27,6 +27,8 @@ ___ieee128_vswscanf (const wchar_t *string, const wchar_t *format,
>    _IO_strfile sf;
>    struct _IO_wide_data wd;
>    FILE *fp = _IO_strfile_readw (&sf, &wd, string);
> -  return __vfwscanf_internal (fp, format, ap, SCANF_LDBL_USES_FLOAT128);
> +  int done = __vfwscanf_internal (fp, format, ap, SCANF_LDBL_USES_FLOAT128);
> +  _IO_wstrfile_fclose_stack (fp);
> +  return done;
>  }
>  strong_alias (___ieee128_vswscanf, __vswscanfieee128)
> diff --git a/sysdeps/ieee754/ldbl-opt/nldbl-compat.c b/sysdeps/ieee754/ldbl-opt/nldbl-compat.c
> index dcd5649200..e239cd5bc5 100644
> --- a/sysdeps/ieee754/ldbl-opt/nldbl-compat.c
> +++ b/sysdeps/ieee754/ldbl-opt/nldbl-compat.c
> @@ -391,7 +391,9 @@ __nldbl_vswscanf (const wchar_t *s, const wchar_t *fmt, va_list ap)
>    struct _IO_wide_data wd;
>    FILE *f = _IO_strfile_readw (&sf, &wd, s);
>  
> -  return __vfwscanf_internal (f, fmt, ap, SCANF_LDBL_IS_DBL);
> +  int ret = __vfwscanf_internal (f, fmt, ap, SCANF_LDBL_IS_DBL);
> +  _IO_wstrfile_fclose_stack (f);
> +  return ret;
>  }
>  libc_hidden_def (__nldbl_vswscanf)
>  
> @@ -955,7 +957,9 @@ __nldbl___isoc99_vswscanf (const wchar_t *s, const wchar_t *fmt, va_list ap)
>    struct _IO_wide_data wd;
>    FILE *f = _IO_strfile_readw (&sf, &wd, s);
>  
> -  return __vfwscanf_internal (f, fmt, ap, SCANF_LDBL_IS_DBL | SCANF_ISOC99_A);
> +  int ret = __vfwscanf_internal (f, fmt, ap, SCANF_LDBL_IS_DBL | SCANF_ISOC99_A);
> +  _IO_wstrfile_fclose_stack (f);
> +  return ret;
>  }
>  libc_hidden_def (__nldbl___isoc99_vswscanf)
>  
> @@ -1115,9 +1119,11 @@ __nldbl___isoc23_vswscanf (const wchar_t *s, const wchar_t *fmt, va_list ap)
>    struct _IO_wide_data wd;
>    FILE *f = _IO_strfile_readw (&sf, &wd, s);
>  
> -  return __vfwscanf_internal (f, fmt, ap,
> +  int ret = __vfwscanf_internal (f, fmt, ap,
>  			      SCANF_LDBL_IS_DBL | SCANF_ISOC99_A
>  			      | SCANF_ISOC23_BIN_CST);
> +  _IO_wstrfile_fclose_stack (f);
> +  return ret;
>  }
>  libc_hidden_def (__nldbl___isoc23_vswscanf)
>  
> diff --git a/wcsmbs/isoc23_swscanf.c b/wcsmbs/isoc23_swscanf.c
> index 66b5290b3c..568ab138b4 100644
> --- a/wcsmbs/isoc23_swscanf.c
> +++ b/wcsmbs/isoc23_swscanf.c
> @@ -33,6 +33,6 @@ __isoc23_swscanf (const wchar_t *s, const wchar_t *format, ...)
>    done = __vfwscanf_internal (f, format, arg,
>  			      SCANF_ISOC99_A | SCANF_ISOC23_BIN_CST);
>    va_end (arg);
> -
> +  _IO_wstrfile_fclose_stack (f);
>    return done;
>  }
> diff --git a/wcsmbs/isoc23_vswscanf.c b/wcsmbs/isoc23_vswscanf.c
> index d84efb9350..89b7241a7a 100644
> --- a/wcsmbs/isoc23_vswscanf.c
> +++ b/wcsmbs/isoc23_vswscanf.c
> @@ -24,7 +24,9 @@ __isoc23_vswscanf (const wchar_t *string, const wchar_t *format, va_list args)
>    _IO_strfile sf;
>    struct _IO_wide_data wd;
>    FILE *f = _IO_strfile_readw (&sf, &wd, string);
> -  return __vfwscanf_internal (f, format, args,
> +  int done = __vfwscanf_internal (f, format, args,
>  			      SCANF_ISOC99_A | SCANF_ISOC23_BIN_CST);
> +  _IO_wstrfile_fclose_stack (f);
> +  return done;
>  }
>  libc_hidden_def (__isoc23_vswscanf)
> diff --git a/wcsmbs/isoc99_swscanf.c b/wcsmbs/isoc99_swscanf.c
> index 493fe01520..c94f19ca41 100644
> --- a/wcsmbs/isoc99_swscanf.c
> +++ b/wcsmbs/isoc99_swscanf.c
> @@ -32,6 +32,6 @@ __isoc99_swscanf (const wchar_t *s, const wchar_t *format, ...)
>    va_start (arg, format);
>    done = __vfwscanf_internal (f, format, arg, SCANF_ISOC99_A);
>    va_end (arg);
> -
> +  _IO_wstrfile_fclose_stack (f);
>    return done;
>  }
> diff --git a/wcsmbs/isoc99_vswscanf.c b/wcsmbs/isoc99_vswscanf.c
> index 326f7bfd24..6bc134a807 100644
> --- a/wcsmbs/isoc99_vswscanf.c
> +++ b/wcsmbs/isoc99_vswscanf.c
> @@ -33,6 +33,8 @@ __isoc99_vswscanf (const wchar_t *string, const wchar_t *format, va_list args)
>    _IO_strfile sf;
>    struct _IO_wide_data wd;
>    FILE *f = _IO_strfile_readw (&sf, &wd, string);
> -  return __vfwscanf_internal (f, format, args, SCANF_ISOC99_A);
> +  int done = __vfwscanf_internal (f, format, args, SCANF_ISOC99_A);
> +  _IO_wstrfile_fclose_stack (f);
> +  return done;
>  }
>  libc_hidden_def (__isoc99_vswscanf)
  

Patch

diff --git a/libio/iofwide.c b/libio/iofwide.c
index 8e71fdd21b..d016aa33ea 100644
--- a/libio/iofwide.c
+++ b/libio/iofwide.c
@@ -251,3 +251,18 @@  __libio_codecvt_length (struct _IO_codecvt *codecvt, __mbstate_t *statep,
 
   return result;
 }
+
+void
+_IO_wstrfile_fclose_stack (FILE *fp)
+{
+  _IO_FINISH (fp);
+  if (fp->_mode > 0)
+    {
+      struct _IO_codecvt *cc = fp->_codecvt;
+
+      __libc_lock_lock (__gconv_lock);
+      __gconv_release_step (cc->__cd_in.step);
+      __gconv_release_step (cc->__cd_out.step);
+      __libc_lock_unlock (__gconv_lock);
+    }
+}
diff --git a/libio/iovswscanf.c b/libio/iovswscanf.c
index a97f4eed1e..986d94178d 100644
--- a/libio/iovswscanf.c
+++ b/libio/iovswscanf.c
@@ -38,6 +38,8 @@  __vswscanf (const wchar_t *string, const wchar_t *format, va_list args)
   _IO_strfile sf;
   struct _IO_wide_data wd;
   FILE *f = _IO_strfile_readw (&sf, &wd, string);
-  return __vfwscanf_internal (f, format, args, 0);
+  int done = __vfwscanf_internal (f, format, args, 0);
+  _IO_wstrfile_fclose_stack (f);
+  return done;
 }
 ldbl_weak_alias (__vswscanf, vswscanf)
diff --git a/libio/libioP.h b/libio/libioP.h
index 1485d22619..1d4217e82a 100644
--- a/libio/libioP.h
+++ b/libio/libioP.h
@@ -645,6 +645,7 @@  extern FILE* _IO_new_file_fopen (FILE *, const char *, const char *,
 				     int);
 extern void _IO_no_init (FILE *, int, int, struct _IO_wide_data *,
 			 const struct _IO_jump_t *) __THROW;
+extern void _IO_wstrfile_fclose_stack (FILE *) attribute_hidden;
 extern void _IO_new_file_init_internal (struct _IO_FILE_plus *)
   __THROW attribute_hidden;
 extern FILE* _IO_new_file_setbuf (FILE *, char *, ssize_t);
diff --git a/libio/swscanf.c b/libio/swscanf.c
index d36ef48258..583253a7dc 100644
--- a/libio/swscanf.c
+++ b/libio/swscanf.c
@@ -37,7 +37,7 @@  __swscanf (const wchar_t *s, const wchar_t *format, ...)
   va_start (arg, format);
   done = __vfwscanf_internal (f, format, arg, 0);
   va_end (arg);
-
+  _IO_wstrfile_fclose_stack (f);
   return done;
 }
 ldbl_strong_alias (__swscanf, swscanf)
diff --git a/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-isoc23_swscanf.c b/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-isoc23_swscanf.c
index 6b3915357f..2e95cdf010 100644
--- a/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-isoc23_swscanf.c
+++ b/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-isoc23_swscanf.c
@@ -35,7 +35,7 @@  ___ieee128_isoc23_swscanf (const wchar_t *string, const wchar_t *format, ...)
   va_start (ap, format);
   done = __vfwscanf_internal (fp, format, ap, mode_flags);
   va_end (ap);
-
+  _IO_wstrfile_fclose_stack (fp);
   return done;
 }
 strong_alias (___ieee128_isoc23_swscanf, __isoc23_swscanfieee128)
diff --git a/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-isoc23_vswscanf.c b/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-isoc23_vswscanf.c
index 295321147c..857903b2a6 100644
--- a/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-isoc23_vswscanf.c
+++ b/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-isoc23_vswscanf.c
@@ -28,6 +28,8 @@  ___ieee128_isoc23_vswscanf (wchar_t *string, const wchar_t *format, va_list ap)
   FILE *fp = _IO_strfile_readw (&sf, &wd, string);
   int mode_flags =
     SCANF_ISOC99_A | SCANF_ISOC23_BIN_CST | SCANF_LDBL_USES_FLOAT128;
-  return __vfwscanf_internal (fp, format, ap, mode_flags);
+  int done = __vfwscanf_internal (fp, format, ap, mode_flags);
+  _IO_wstrfile_fclose_stack (fp);
+  return done;
 }
 strong_alias (___ieee128_isoc23_vswscanf, __isoc23_vswscanfieee128)
diff --git a/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-isoc99_swscanf.c b/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-isoc99_swscanf.c
index 19b71940dd..2280bae1d0 100644
--- a/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-isoc99_swscanf.c
+++ b/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-isoc99_swscanf.c
@@ -34,7 +34,7 @@  ___ieee128_isoc99_swscanf (const wchar_t *string, const wchar_t *format, ...)
   va_start (ap, format);
   done = __vfwscanf_internal (fp, format, ap, mode_flags);
   va_end (ap);
-
+  _IO_wstrfile_fclose_stack (fp);
   return done;
 }
 strong_alias (___ieee128_isoc99_swscanf, __isoc99_swscanfieee128)
diff --git a/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-isoc99_vswscanf.c b/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-isoc99_vswscanf.c
index a1b51cb5b6..21cf25610a 100644
--- a/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-isoc99_vswscanf.c
+++ b/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-isoc99_vswscanf.c
@@ -27,6 +27,8 @@  ___ieee128_isoc99_vswscanf (wchar_t *string, const wchar_t *format, va_list ap)
   struct _IO_wide_data wd;
   FILE *fp = _IO_strfile_readw (&sf, &wd, string);
   int mode_flags = SCANF_ISOC99_A | SCANF_LDBL_USES_FLOAT128;
-  return __vfwscanf_internal (fp, format, ap, mode_flags);
+  int done = __vfwscanf_internal (fp, format, ap, mode_flags);
+  _IO_wstrfile_fclose_stack (fp);
+  return done;
 }
 strong_alias (___ieee128_isoc99_vswscanf, __isoc99_vswscanfieee128)
diff --git a/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-swscanf.c b/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-swscanf.c
index 7124ecb67c..fdfdf2b65c 100644
--- a/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-swscanf.c
+++ b/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-swscanf.c
@@ -34,7 +34,7 @@  ___ieee128_swscanf (const wchar_t *string, const wchar_t *format, ...)
   done = __vfwscanf_internal (fp, format, ap,
 			      SCANF_LDBL_USES_FLOAT128);
   va_end (ap);
-
+  _IO_wstrfile_fclose_stack (fp);
   return done;
 }
 strong_alias (___ieee128_swscanf, __swscanfieee128)
diff --git a/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-vswscanf.c b/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-vswscanf.c
index 8294582c48..b860a8e87d 100644
--- a/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-vswscanf.c
+++ b/sysdeps/ieee754/ldbl-128ibm-compat/ieee128-vswscanf.c
@@ -27,6 +27,8 @@  ___ieee128_vswscanf (const wchar_t *string, const wchar_t *format,
   _IO_strfile sf;
   struct _IO_wide_data wd;
   FILE *fp = _IO_strfile_readw (&sf, &wd, string);
-  return __vfwscanf_internal (fp, format, ap, SCANF_LDBL_USES_FLOAT128);
+  int done = __vfwscanf_internal (fp, format, ap, SCANF_LDBL_USES_FLOAT128);
+  _IO_wstrfile_fclose_stack (fp);
+  return done;
 }
 strong_alias (___ieee128_vswscanf, __vswscanfieee128)
diff --git a/sysdeps/ieee754/ldbl-opt/nldbl-compat.c b/sysdeps/ieee754/ldbl-opt/nldbl-compat.c
index dcd5649200..e239cd5bc5 100644
--- a/sysdeps/ieee754/ldbl-opt/nldbl-compat.c
+++ b/sysdeps/ieee754/ldbl-opt/nldbl-compat.c
@@ -391,7 +391,9 @@  __nldbl_vswscanf (const wchar_t *s, const wchar_t *fmt, va_list ap)
   struct _IO_wide_data wd;
   FILE *f = _IO_strfile_readw (&sf, &wd, s);
 
-  return __vfwscanf_internal (f, fmt, ap, SCANF_LDBL_IS_DBL);
+  int ret = __vfwscanf_internal (f, fmt, ap, SCANF_LDBL_IS_DBL);
+  _IO_wstrfile_fclose_stack (f);
+  return ret;
 }
 libc_hidden_def (__nldbl_vswscanf)
 
@@ -955,7 +957,9 @@  __nldbl___isoc99_vswscanf (const wchar_t *s, const wchar_t *fmt, va_list ap)
   struct _IO_wide_data wd;
   FILE *f = _IO_strfile_readw (&sf, &wd, s);
 
-  return __vfwscanf_internal (f, fmt, ap, SCANF_LDBL_IS_DBL | SCANF_ISOC99_A);
+  int ret = __vfwscanf_internal (f, fmt, ap, SCANF_LDBL_IS_DBL | SCANF_ISOC99_A);
+  _IO_wstrfile_fclose_stack (f);
+  return ret;
 }
 libc_hidden_def (__nldbl___isoc99_vswscanf)
 
@@ -1115,9 +1119,11 @@  __nldbl___isoc23_vswscanf (const wchar_t *s, const wchar_t *fmt, va_list ap)
   struct _IO_wide_data wd;
   FILE *f = _IO_strfile_readw (&sf, &wd, s);
 
-  return __vfwscanf_internal (f, fmt, ap,
+  int ret = __vfwscanf_internal (f, fmt, ap,
 			      SCANF_LDBL_IS_DBL | SCANF_ISOC99_A
 			      | SCANF_ISOC23_BIN_CST);
+  _IO_wstrfile_fclose_stack (f);
+  return ret;
 }
 libc_hidden_def (__nldbl___isoc23_vswscanf)
 
diff --git a/wcsmbs/isoc23_swscanf.c b/wcsmbs/isoc23_swscanf.c
index 66b5290b3c..568ab138b4 100644
--- a/wcsmbs/isoc23_swscanf.c
+++ b/wcsmbs/isoc23_swscanf.c
@@ -33,6 +33,6 @@  __isoc23_swscanf (const wchar_t *s, const wchar_t *format, ...)
   done = __vfwscanf_internal (f, format, arg,
 			      SCANF_ISOC99_A | SCANF_ISOC23_BIN_CST);
   va_end (arg);
-
+  _IO_wstrfile_fclose_stack (f);
   return done;
 }
diff --git a/wcsmbs/isoc23_vswscanf.c b/wcsmbs/isoc23_vswscanf.c
index d84efb9350..89b7241a7a 100644
--- a/wcsmbs/isoc23_vswscanf.c
+++ b/wcsmbs/isoc23_vswscanf.c
@@ -24,7 +24,9 @@  __isoc23_vswscanf (const wchar_t *string, const wchar_t *format, va_list args)
   _IO_strfile sf;
   struct _IO_wide_data wd;
   FILE *f = _IO_strfile_readw (&sf, &wd, string);
-  return __vfwscanf_internal (f, format, args,
+  int done = __vfwscanf_internal (f, format, args,
 			      SCANF_ISOC99_A | SCANF_ISOC23_BIN_CST);
+  _IO_wstrfile_fclose_stack (f);
+  return done;
 }
 libc_hidden_def (__isoc23_vswscanf)
diff --git a/wcsmbs/isoc99_swscanf.c b/wcsmbs/isoc99_swscanf.c
index 493fe01520..c94f19ca41 100644
--- a/wcsmbs/isoc99_swscanf.c
+++ b/wcsmbs/isoc99_swscanf.c
@@ -32,6 +32,6 @@  __isoc99_swscanf (const wchar_t *s, const wchar_t *format, ...)
   va_start (arg, format);
   done = __vfwscanf_internal (f, format, arg, SCANF_ISOC99_A);
   va_end (arg);
-
+  _IO_wstrfile_fclose_stack (f);
   return done;
 }
diff --git a/wcsmbs/isoc99_vswscanf.c b/wcsmbs/isoc99_vswscanf.c
index 326f7bfd24..6bc134a807 100644
--- a/wcsmbs/isoc99_vswscanf.c
+++ b/wcsmbs/isoc99_vswscanf.c
@@ -33,6 +33,8 @@  __isoc99_vswscanf (const wchar_t *string, const wchar_t *format, va_list args)
   _IO_strfile sf;
   struct _IO_wide_data wd;
   FILE *f = _IO_strfile_readw (&sf, &wd, string);
-  return __vfwscanf_internal (f, format, args, SCANF_ISOC99_A);
+  int done = __vfwscanf_internal (f, format, args, SCANF_ISOC99_A);
+  _IO_wstrfile_fclose_stack (f);
+  return done;
 }
 libc_hidden_def (__isoc99_vswscanf)