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,
©->towc->__counter);
if (copy->tomb->__shlib_handle != NULL)
overflow |= __builtin_add_overflow (copy->tomb->__counter, 1,
©->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