reference counting bug in swscanf et al

DJ Delorie dj@redhat.com
Wed Mar 18 17:59:56 GMT 2026


In swscanf.c we have this:

  FILE *f = _IO_strfile_readw (&sf, &wd, s);

  . . .

  return done;

_IO_strfile_readw calls:

  _IO_no_init (&sf->_sbf._f, _IO_USER_LOCK, 0, wd, &_IO_wstr_jumps);
  _IO_fwide (&sf->_sbf._f, 1);

This eventually ends up in __wcsmbs_clone_conv(), where it
reference-count-locks the shared object:

   bool overflow = false;
  if (copy->towc->__shlib_handle != NULL)
    overflow |= __builtin_add_overflow (copy->towc->__counter, 1,
                                        &copy->towc->__counter);
  if (copy->tomb->__shlib_handle != NULL)
    overflow |= __builtin_add_overflow (copy->tomb->__counter, 1,
                                        &copy->tomb->__counter);

However, the only place this refcount is released is in _IO_fclose(),
where it does this:

  if (fp->_mode > 0)
    {
      /* This stream has a wide orientation.  This means we have to free
         the conversion functions.  */
      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);


in swscanf.c fclose is never called, nor can it be, since fclose also
free's the FILE*, and in swscanf we're using a FILE struct on the
stack.

Possible solutions?  First, do we need to refcount these still?  The
reproducer works just fine other than the overflow.  Second, could we
add some _IO_no_uninit() that can clean up the locks without free'ing
the FILE* ?  Note that vswprintf() used to have this problem too, but
its internals were completely replaced with a different algorith that
doesn't.


#include <stdio.h>
#include <wchar.h>
#include <locale.h>

int main(int argc, char *argv[])
{
	long	i;
	int j;
	setlocale(LC_ALL, "en_US");
	wchar_t buf[32] = L"123";
	for (i = 0; i < 0x80000000; i++)
		swscanf(buf, L"%d", &j);
	return 0;
}



More information about the Libc-alpha mailing list