[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#634261: [sparc] iceweasel: Bus Error in setbuf()



Am Sonntag, den 23.12.2012, 00:13 +0100 schrieb John Paul Adrian
Glaubitz:
> > I don't completely follow, so I'll just ask: do you mean that this is
> > a case of ABI misuse, with poor error reporting?
One could phrase it this way.

> As far I understand the problem, the Mozilla developers provide a
> version script to the linker to control which symbols get
> exported. This helps speeding up the load process of the binary and
> reduces the memory footprint.
This is correct.

> What the Mozilla developers didn't seem to put into account is that if
> you prevent the symbol _IO_stdin_used from being exported from your
> binary, parts of the ABI of the standard C library will change and it
> will behave like an older version which causes the unaligned access
> which results in a CPU trap.
And this is mostly correct. libc puts lots of effort in providing a
stable ABI. A big change in libc was the introduction of libio 2.1. It
introduced support for wide-character streams and 64 bit offsets. These
changes required an incompatible change to the FILE structure.

Because of this, the FILE APIs exist in two variants in glibc[1], if
backward compatibility is enabled. The new variant is tagged with the
version GLIBC_2.1, while the old one is tagged GLIBC_2.0. For the three
standard streams, there are two differently *named*, not just
differently *versioned* objects, namely _IO_stdin_ for the old version
and _IO_2_1_stdin_ for the new version, while the pointer stdin itself
is not version dependent. This might be to make sure that "stdin" itself
has the same value regardless of the version of libc that is imported.

If a program compiled against the glibc 2.1 (or newer) development
files, it will automatically refer to the new functions (i.e. link to
the GLIBC_2.1 version of _IO_file_setbuf and so on), while programs and
libraries compiled with old glibc 2.0 development files will refer to
the GLIBC_2.0 version of these functions.

The tricky part are the std* pointers:

If a source file is compiled with new development headers and refers to
stdin, stdout or stderr, some magic makes the compiler or linker emit a
definition of the symbol "_IO_stdin_used" in that module. glibc itself
defines it as a *weak* external symbol. The consequence is that if the
symbol is not defined anywhere, it just resolves to address 0, but if it
is defined in one or more modules, it resolves to a valid address in one
of these modules. The resolution of external global variables in ELF
systems is internally performed by a GOT lookup (which is the strange
code for &_IO_stdin_used observed on disassembling) at runtime.

The logic in glibc is that if the new libio functions are used with
stdin, there will be a reference to _IO_stdin_used. But if there are no
references to _IO_stdin_used, the compatibility layer will kick in, and
make the stdin/stdout/stderr pointers by pointers to the compatibility
objects.

As it happens, the compatibility objects do not contain any 64 bit
field, and require a 4-byte-alignent on sparc, while the modern objects
(which are in fact the compatibility objects with some extra fields
appended) have a 64 bit field containing the current file offset. This
makes gcc on sparc require an 8-byte-alignment. gcc compiles functions
that work on the new FILE structure with the internal assumption that
these objects are aligned as they should, so it expects
8-byte-alignment. The old functions on the other hand work fine with the
new structures, stricter aligned, unless the code tries to access the
vtable pointer, which is at different location in the old and new
object, and most likely the cause to have both versions.

It might have been the intention of the libio developers that (unless
vtable accesses happen) the old objects can be processed by the new
functions, and in that case, glibc is buggy, as it relies on undefined
behaviour. Aussuming that intention, it expects that a pointer to the
short file structure can be used as a pointer to the long file
structure, which is not something you are granted by the C standard.

> > Can you describe what iceweasel was doing wrong?  Is this documented
> > so future coders know not to make the same mistake?  Is the version in
> > squeeze affected?  How about the version in wheezy?
> It seems to have been fixed in Firefox 10 which is part of Wheezy:

There seems to be no official documentation on it, but hiding the
_IO_stdin_used symbol (it still is there, but not visible for dynamic
loading) violates internal glibc assumptions and breaks on sparc.

Regards,
  Michael Karcher

[1] This is why Bernhard R. Link observed the two different alignof
values. You choose between the two variants of FILE/_IO_FILE by defining
or not defining _IO_USE_OLD_IO_FILE. In oldstdfile.c, the symbol is
defined, while in genops.c, it is not defined.


Reply to: