control: reassign -1 libcap2
control: retitle -1 libcap.so.2 lacks a _IO_stdin_used symbol
control: tag -1 + patch
Hi,
On 2025-03-20 23:04, Christian Kastner wrote:
Control: reassign -1 glibc
Control: retitle -1 glibc-2.40 printf() segfaults on mips64el, powerpc Control: notforwarded -1
Control: tags -1 - ftbfs
Dear glibc maintainers,
A change introduced in glibc-2.40 seems to have introduced an
I/O-related bug on mips64el and powerpc. With mips64el being an official architecture, this was made an RC bug.
This bug was discovered by src:libcap2 starting to FTBFS last October
because tests were segfaulting on the aforementioned architectures. The segfault occurs during calls to printf().
The segfault is reproducible even with the libcap2 version in bookworm.
In a bookworm mips64el container image, I verified that everything was
still fine with glibc-2.39 from snapshots.d.o, and that the breakage
appears with glibc-2.40.
A bisect determined that the breaking commit is:
2a99e23: Use a doubly-linked list for _IO_list_all (bug 27777)
A crude attempt at reverting this commit (crude because it cannot be
reverted cleanly) resolved the issue.
The problem can is easily reproduced in a mips64el container or chroot
where libcap2 is installed:
# libcap2.so is runnable by itself, so we make a copy executable
$ sudo apt-get install libcap2
$ cp /usr/lib/mips*/libcap.so.2.* .
$ chmod +x libcap.so.2.*
# calls usage(), which calls printf(), which segfaults
$ ./libcap.so.2.* --usage
The relevant code [1] is fairly simple: the call to usage() triggers the segfault on its sole printf(). Interestingly, the call to summary() does
not trigger the segfault on the first printf(), but one of the later
ones.
No, it is not that simple given libcap.so.2 is a library that is
executable, and the way it is done is a bit tricky. The problem actually
comes from there...
The breaking commit looks simple enough that this might be an issue with
how the padding for struct _IO_FILE_complete changed. However I don't
have enough background to be confident in this assessment, and I was
hoping that you might be able to pass this on upstream.
Yep, I confirm this is indeed linked with that. The problem is that the libcap.so.2 library lacks the _IO_stdin_used symbols that is provided by Scrt1.o for executables. This symbol is used by the GNU libc determine
which version of the I/O functions should be used. When this symbol is
absent, it means that the "old" version is used, leading to possible
crashes or other issues on architectures that were supported by glibc
2.0. That is for debian: i386 and mips64el for official architectures
and alpha, hppa, m68k, powerpc, sh4 and sparc64 for ports
architectures.
With time the differences with the original structure increased. While
very basic functions were still working until recently, the commit you
pointed that went in glibc 2.40 changed the format of the libio
structures even more.
The solution is to get that symbol defined in libcap.so.2, like any
other executable. Given it is a both a library and executable, it is not possible to link with Scrt1.o like it's done with normal binaries, as
this requires a main symbol. The following patch should do that, and
that indeed fixes the issue, although I guess there might be better ways
to do that:
--- libcap2-2.75.orig/libcap/Makefile
+++ libcap2-2.75/libcap/Makefile
@@ -14,9 +14,9 @@ PSXLIBNAME=$(PSXTITLE).so
STAPSXLIBNAME=$(PSXTITLE).a
CAPFILES=cap_alloc cap_proc cap_extint cap_flag cap_text cap_file cap_syscalls -CAPMAGICOBJ=cap_magic.o
+CAPMAGICOBJ=cap_magic.o _IO_stdin_used.o
PSXFILES=../psx/psx ../psx/psx_calls ../psx/wrap/psx_wrap -PSXMAGICOBJ=psx_magic.o
+PSXMAGICOBJ=psx_magic.o _IO_stdin_used.o
# hardening=+all adds this universally, but we don't want this for the lib
CFLAGS := $(filter-out -fPIE,$(CFLAGS))
@@ -122,6 +122,9 @@ empty: empty.c
loader.txt: empty
$(OBJCOPY) --dump-section .interp=$@ $< /dev/null
+_IO_stdin_used.o: $(shell $(CC) -print-file-name=Scrt1.o)
+ $(OBJCOPY) --strip-all --keep-symbol=_IO_stdin_used $< $@
+
cap_magic.o: execable.h execable.c loader.txt libcap.h
$(CC) $(CFLAGS) $(CPPFLAGS) -DLIBRARY_VERSION=\"$(LIBTITLE)-$(VERSION).$(MINOR)\" -DSHARED_LOADER=\"$(shell cat loader.txt)\" -include ./libcap.h -c execable.c -o $@
Note th