[PATCH v2] libio: validate the wide vtable via a bounds-checked index
Alessandro Schino
7991aleschino@gmail.com
Tue Sep 8 08:17:15 GMT 2026
Thanks Avinal, good catch.
You're right that _IO_vtable_check can return without aborting (when
IO_accept_foreign_vtables is set, in secondary namespaces, and in the
static dlopen case) so an out-of-range index would fall through to
the &__io_vtables[index] access, which is UB.
Looking at the wide path, my understanding is that this scenario cannot
legitimately occur there: the wide vtable is only ever set to an
internal table (&_IO_wfile_jumps and friends), and the foreign-vtable
acceptance handled by _IO_vtable_check applies to the narrow vtable of
the standard streams (see check_stdfiles_vtables), not to the wide
dispatch. If that reading is correct, an out-of-range index on this
path is always a genuine error rather than a legitimate foreign vtable.
If so, one option would be to terminate directly with __libc_fatal,
which is noreturn, so a bad index can never reach the array access. I'd
also rename the helpers to IO_wide_* for consistency as you suggested.
Does that approach sound reasonable, or is there a wide-path case I'm
missing where a foreign vtable could legitimately be in use?
Thanks,
Alessandro
Il giorno lun 7 set 2026 alle ore 11:53 Avinal Kumar
<avinal.xlvii@gmail.com> ha scritto:
>
> On Mon, Aug 24, 2026 at 10:27 PM Alessandro Schino
> <7991aleschino@gmail.com> wrote:
> >
> > +/* Resolve a stored wide vtable index to its jump table, bounds-checked. */
> > +static inline const struct _IO_jump_t *
> > +WIO_validate_index (unsigned int index)
> > +{
> > + if (__glibc_unlikely (index >= IO_VTABLES_NUM))
> > + _IO_vtable_check ();
>
> In cases where _IO_vtable_check() returns without aborting, like
> IO_accept_foreign_vtables is set, the index can be out-of-range, which
> may result in UB. There can be other cases too where the function just
> returned cleanly. My question is, is this scenario not possible in the
> current case?
>
> > + return &__io_vtables[index];
> > +}
> > +
> > +/* Convert a wide vtable pointer (which must point at the start of a table
> > + inside __io_vtables) to its index for storage. */
> > +static inline unsigned int
> > +WIO_vtable_to_index (const struct _IO_jump_t *vtable)
> > +{
> > + uintptr_t offset = (uintptr_t) vtable - (uintptr_t) &__io_vtables;
> > + /* The pointer must be aligned to the start of a jump table and lie
> > + within the section; otherwise the stored index would be bogus. */
> > + if (__glibc_unlikely (offset >= IO_VTABLES_LEN
> > + || offset % sizeof (struct _IO_jump_t) != 0))
> > + _IO_vtable_check ();
>
> Same doubt as above.
>
> > + return offset / sizeof (struct _IO_jump_t);
> > +}
> > +#endif /* IS_IN (libc) */
> > +
>
> If the scenario described above is possible, we will need to add a
> test to check the fallthrough condition. The rest looks good to me.
>
> One small nitpick, new additions uses WIO_ prefix, it would be
> preferable to have something similar to IO_wide_* to maintain naming
> consistency.
>
> Thanks
> - Avinal
More information about the Libc-alpha
mailing list