reference counting bug in swscanf et al
Adhemerval Zanella Netto
adhemerval.zanella@linaro.org
Thu Mar 19 16:20:52 GMT 2026
On 18/03/26 14:59, DJ Delorie wrote:
>
> 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
My understanding is the refcount is still required because the
__shlib_handle struct can be shared amount multiple threads and the
possible fix would to add _IO_new_fclose internal interface that expects
an stack-allocated FILE * (so no _IO_deallocate_file call).
> 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