Bug#966173: libc6: __atan2_finite reference in dlopened module no longer found in executable linked to libm

2020-07-26 Thread Aurelien Jarno
On 2020-07-24 15:23, Simon McVittie wrote:
> On Fri, 24 Jul 2020 at 14:36:54 +0200, Bastian Blank wrote:
> > On Fri, Jul 24, 2020 at 10:11:04AM +0100, Simon McVittie wrote:
> > > The bug (#966150) is that a version of uix86_64.so compiled with a 
> > > slightly
> > > older (2020-02-18) toolchain fails to load on an up-to-date sid system, 
> > > with:
> > > undefined symbol: __atan2_finite
> > 
> > Because the binary was not linked with -lm, the linker never saw the
> > real symbol __atan2_finite@GLIBC2_16, so the linke only emitted a reference
> > to __atan2_finite.
> 
> Right. However, note that there's no mention of __atan2_finite() in the
> source code - it's only used because older glibc would replace atan2()
> with a reference to __atan2_finite() when building with -ffast-math.

I do not see what does it change. atan2 also need to be linked with
-libm. If it is not, issues like the one you encountered might happen.
The change from atan2 to __atan2_finite when -ffast-math is in used is
purely done in the preprocessor.

> > At least dpkg-shlibdeps or so should warn about that.
> 
> For at least openarena, it doesn't seem to. I'm not sure why not.
> 
> For the next update to openarena I'm going to build it with -Wl,-z,defs
> so that missing dependencies are always fatal. However, that isn't
> always applicable: some plugin architectures (like Python extensions)
> rely on being able to pick up symbols exported by the executable, which
> are not necessarily programmatically distinguishable from symbols that
> are defined by libraries used by the executable.

This is indeed an issue, under-linking is sometimes difficult to find.
Given we now the list of affected symbols, I'll try to check if other
binaries are affected so that they can be fixed even if no users report
an issue.

> > > I've been trying to put together a standalone reproducer that only uses
> > > libdl and libm, but so far I have not been successful.
> > 
> > Something like that?
> > 
> > | % cat test.c
> > | void __atan2_finite(void);
> > | void test(void) {
> > |   __atan2_finite();
> > | }
> 
> I was aiming for something a bit closer to openarena's situation,
> where there is no explicit reference to __atan2_finite() in the source
> code: it calls atan2(), and cc -ffast-math rewrites that into a call
> to __atan2_finite(). I've now managed to make this work: see attached.
> 
> Compile them and run ./prog in a buster environment (or an outdated
> bullseye/sid environment with glibc < 2.31), then run ./prog in an
> up-to-date bullseye/sid environment without recompiling.
> 
> libmymodule.so will get a dynamic reference to __atan2_finite.
> 
> The historical result is that prog outputs 0.463648, twice.
> 
> The result in up-to-date bullseye/sid is that prog outputs 0.463648,
> once, and then fails with "undefined symbol: __atan2_finite".
> 
> Using __FINITE_MATH_ONLY__ (which is defined by -ffast-math) is necessary
> to be able to reproduce the bug this way.
> 
> If you consider this sort of thing to be too niche to be supportable,
> please feel free to close the bug.

I do consider it a bug on the openarena side, as it's basically using a
non-versioned symbol due to under-linking. However from the user point
of view, we should prevent that to happen, so I'll add the corresponding
Breaks: entry on the glibc side to ensure a flawless upgrade for the
users.

Aurelien

-- 
Aurelien Jarno  GPG: 4096R/1DDD8C9B
aurel...@aurel32.net http://www.aurel32.net



Bug#966173: libc6: __atan2_finite reference in dlopened module no longer found in executable linked to libm

2020-07-24 Thread Simon McVittie
On Fri, 24 Jul 2020 at 14:36:54 +0200, Bastian Blank wrote:
> On Fri, Jul 24, 2020 at 10:11:04AM +0100, Simon McVittie wrote:
> > The bug (#966150) is that a version of uix86_64.so compiled with a slightly
> > older (2020-02-18) toolchain fails to load on an up-to-date sid system, 
> > with:
> > undefined symbol: __atan2_finite
> 
> Because the binary was not linked with -lm, the linker never saw the
> real symbol __atan2_finite@GLIBC2_16, so the linke only emitted a reference
> to __atan2_finite.

Right. However, note that there's no mention of __atan2_finite() in the
source code - it's only used because older glibc would replace atan2()
with a reference to __atan2_finite() when building with -ffast-math.

> At least dpkg-shlibdeps or so should warn about that.

For at least openarena, it doesn't seem to. I'm not sure why not.

For the next update to openarena I'm going to build it with -Wl,-z,defs
so that missing dependencies are always fatal. However, that isn't
always applicable: some plugin architectures (like Python extensions)
rely on being able to pick up symbols exported by the executable, which
are not necessarily programmatically distinguishable from symbols that
are defined by libraries used by the executable.

> > I've been trying to put together a standalone reproducer that only uses
> > libdl and libm, but so far I have not been successful.
> 
> Something like that?
> 
> | % cat test.c
> | void __atan2_finite(void);
> | void test(void) {
> |   __atan2_finite();
> | }

I was aiming for something a bit closer to openarena's situation,
where there is no explicit reference to __atan2_finite() in the source
code: it calls atan2(), and cc -ffast-math rewrites that into a call
to __atan2_finite(). I've now managed to make this work: see attached.

Compile them and run ./prog in a buster environment (or an outdated
bullseye/sid environment with glibc < 2.31), then run ./prog in an
up-to-date bullseye/sid environment without recompiling.

libmymodule.so will get a dynamic reference to __atan2_finite.

The historical result is that prog outputs 0.463648, twice.

The result in up-to-date bullseye/sid is that prog outputs 0.463648,
once, and then fails with "undefined symbol: __atan2_finite".

Using __FINITE_MATH_ONLY__ (which is defined by -ffast-math) is necessary
to be able to reproduce the bug this way.

If you consider this sort of thing to be too niche to be supportable,
please feel free to close the bug.

smcv
all = prog libmymodule.so

CFLAGS = -ffast-math

check: $(all)
objdump -Tx libmymodule.so
./prog

all: $(all)

prog: prog.c Makefile
$(CC) $(CFLAGS) -Wl,--no-as-needed -o $@ $< -ldl -lm

# Note that this cannot be compiled with -Wl,-z,defs: it deliberately has
# undefined references to symbols from libm
libmymodule.so: module.c Makefile
$(CC) $(CFLAGS) -shared -o $@ $<

clean:
rm -f $(all)
#include 
#include 
#include 
#include 

#if !defined(__FINITE_MATH_ONLY__) || !__FINITE_MATH_ONLY__
#warning Not using finite-only mathematics
#endif

int main (void)
{
  void *module;
  double (*my_atan2) (double, double);

  printf ("%f\n", atan2 (1, 2));

  module = dlopen ("${ORIGIN}/libmymodule.so", RTLD_NOW);
  if (module == NULL)
errx(1, "%s", dlerror ());

  my_atan2 = (double (*) (double, double)) dlsym (module, "my_atan2");
  if (my_atan2 == NULL)
errx(1, "%s", dlerror ());

  printf ("%f\n", my_atan2 (1, 2));

  return 0;
}
#if !defined(__FINITE_MATH_ONLY__) || !__FINITE_MATH_ONLY__
#warning Not using finite-only mathematics
#endif

#include 

double my_atan2 (double x, double y)
{
  return atan2 (x, y);
}


Bug#966173: libc6: __atan2_finite reference in dlopened module no longer found in executable linked to libm

2020-07-24 Thread Bastian Blank
On Fri, Jul 24, 2020 at 10:11:04AM +0100, Simon McVittie wrote:
> The bug (#966150) is that a version of uix86_64.so compiled with a slightly
> older (2020-02-18) toolchain fails to load on an up-to-date sid system, with:
> undefined symbol: __atan2_finite

Because the binary was not linked with -lm, the linker never saw the
real symbol __atan2_finite@GLIBC2_16, so the linke only emitted a reference
to __atan2_finite.  At least dpkg-shlibdeps or so should warn about that.

> I've been trying to put together a standalone reproducer that only uses
> libdl and libm, but so far I have not been successful.

Something like that?

| % cat test.c 
| void __atan2_finite(void);
| void test(void) {
|   __atan2_finite();
| }
| % gcc --shared -o test.so test.c -Wall -W
| % objdump -x test.so | grep atan 
|  *UND*   __atan2_finite

Regards,
Bastian

-- 
Men will always be men -- no matter where they are.
-- Harry Mudd, "Mudd's Women", stardate 1329.8



Bug#966173: libc6: __atan2_finite reference in dlopened module no longer found in executable linked to libm

2020-07-24 Thread Simon McVittie
Package: libc6
Version: 2.31-1
Severity: normal

I've encountered an odd bug in openarena (#966150) which I'm concerned
might be a glibc regression affecting other packages.

Some background: openarena is a game running on the ioquake3 engine
(main executable: /usr/lib/ioquake3/ioquake3). During startup, the engine
dlopens some modules, which implement the actual openarena game and UI.
One of those modules is uix86_64.so.

uix86_64.so uses mathematical functions from libm, but is not itself
linked to libm. At runtime (at least on older systems) it works as
intended, because the ioquake3 executable *is* linked to libm. I'm aware
that this is not the most robust setup, and uix86_64.so would ideally be
linked with -lm to make it self-contained; but it's documented as being
expected to work, and has always worked in the past:

Symbol references in the shared object are resolved using (in order):
symbols in the link map of objects loaded for the main program and its
dependencies; [... and some more places ...]
— dlopen(3)

The bug (#966150) is that a version of uix86_64.so compiled with a slightly
older (2020-02-18) toolchain fails to load on an up-to-date sid system, with:

undefined symbol: __atan2_finite

If I recompile openarena in a sid chroot, *with no source code changes*
(in particular uix86_64.so is still not linked to -lm!), then it starts
to work again. The recompiled uix86_64.so has an undefined reference
to atan2, but no reference to __atan2_finite any more.

I'm going to address this in bullseye by making openarena more robust
(explicitly linking to -lm). After I've done that, the updated version of
openarena will not be suitable as a reproducer for this bug report, but
the buster version of openarena will still be suitable.

If you believe this is not a significant regression in glibc and should
only be fixed by changes in openarena, I have no problem with doing that
and just closing this bug report. However, I wanted to raise this in
case it affects other previously-built binaries.

This can be reproduced somewhat conveniently as follows:

* Have a buster virtual machine
* Install openarena and enough of a desktop to get a terminal in an X11
  environment
* Run openarena
* It succeeds
* To exit quickly: Shift+Escape, type "/quit", Enter
* Add a bullseye apt source and "apt update", but do not upgrade everything
* Upgrade libc6 from 2.28-10 to 2.31-1, while upgrading as few other
  packages as possible
* I used aptitude, which made me also upgrade gcc-9 and related
  packages, removing gcc-8
* Run openarena
* It fails as described in #966150
* Downgrade libc6 and closely-related packages from 2.31-1 to 2.28-10
* In my case this meant downgrading libc-dev-bin, libc6-dev, libc6
  and libc-bin, and removing libcrypt-dev and libcrypt1
* Run openarena
* It succeeds again, confirming that this was a glibc behaviour change

I've been trying to put together a standalone reproducer that only uses
libdl and libm, but so far I have not been successful.

I believe this is related to a change in the representation of
the __atan2_finite symbol, which is used (at least by versions of
openarena compiled against older glibc) because openarena is compiled
with -ffast-math. In 2.28-10, that symbol was not hidden:

$ objdump -Tx /lib/x86_64-linux-gnu/libm.so.6
...
00028280 g   iD  .text  0046  GLIBC_2.15  __atan2_finite

In 2.31-1, it is hidden, and there is no non-hidden definition (default
symbol-version):

$ objdump -Tx /lib/x86_64-linux-gnu/libm.so.6
...
0002a1e0 g   iD  .text  0049 (GLIBC_2.15) __atan2_finite

Because uix86_64.so is not directly linked to -lm, it has an undefined
reference to __atan2_finite with no particular version:

$ objdump -Tx /usr/lib/openarena/baseoa/pak6-patch088/uix86_64.so
...
  D  *UND*    __atan2_finite

As far as I can work out, this unversioned undefined reference can be
satisfied by __atan2_finite@@GLIBC_2.15 in the global namespace from
the old libm, but not by the hidden version __atan2_finite@GLIBC_2.15
in the new libm.

Thanks,
smcv