Fragen? Antworten! Siehe auch: Alternativlos
Allerdings gibt es halt beim Selberbauen immer mal wieder Stress.
Eine Weile gab es Konflikte, weil Firefox auf einer über 10 Jahre alten Version von GNU autoconf bestand, die ich nicht hatte.
Dann gab es Ärger, weil Firefox H.264-Playback (Youtube und co) nur abspielen wollte, wenn man gstreamer installierte — aber es bestand auf einer 10 Jahre alten Gammelversion, die schon längst nicht mehr gewartet wurde.
Inzwischen hat Firefox auf ffmpeg umgestellt, und das tat auch eine Weile ganz gut, aber vor ein paar Tagen stellte es die Arbeit ein. Ich bin dann auf die Vorversion und zurück und habe mir das für das Wochenende vorgenommen. Jetzt ist Wochenende, und ich habe erstmal die neue glibc 2.26 installiert (die endlich reallocarray von OpenBSD übernommen hat, und einen per-Thread malloc-Cache hat, was die Performance massiv verbessern sollte in Programmen mit mehreren Threads).
Das brach mir dann erstmal alles.
Die Shell konnte plötzlich nicht mehr meinen Usernamen herausfinden, screen wurde konkreter und sagte, getpwuid könne meinen User nicht auflösen. Sowas gibt es bei glibc häufiger, weil die gerne mal Dinge als "deprecated" markieren, für die es keinen Ersatz gibt. Zum Beispiel pt_chown vor einer Weile. Das war halt unsicher, also haben sie es weggemacht. pt_chown ist das Vehikel, über das die einschlägigen libc-Routinen (grantpt) ein PTY allozieren, und es liegt in /usr/libexec, wenn es denn überhaupt liegt. Und plötzlich kam mein X nicht mehr hoch, weil mein X solange läuft, wie mein Terminal in X läuft, und das konnte kein PTY mehr allozieren.
Später hat glibc dann Sun RPC rausgeschmissen, und dann ließ sich mount(8) nicht mehr bauen. Ja super. Und jetzt haben sie nsl für obsolet erklärt und in nss ausgemistet (NIS rausgekantet und so). Gut, NIS verwende ich nicht. Als das nicht ging, habe ich halt neu gebaut, diesmal mit --enable-obsolete-nsl, aber das brauchte auch nichts. Stellt sich raus: in meine /etc/nsswitch.conf stand drin:
passwd: compatUnd das sorgte dafür, dass die glibc libnss_compat.so oder so zu laden versuchte, und das gibt es nicht mehr. Die glückbringende Änderung war dann:
shadow: compat
group: compat
passwd: filesSeufz. Bei solchen Gelegenheiten bin ich ja immer froh, dass mein getty und mein login gegen dietlibc gelinkt sind, und für Notfälle noch eine diet-shell rumliegt. So komm ich auch nach solchen glibc-Sabotageakten immer noch irgendwie ans System ran und kann Dinge fixen.
shadow: files
group: files
Aber eigentlich wollte ich ja von Firefox erzählen. Nach dem glibc-Update baut auch Firefox nicht mehr, weil glibc in sys/ucontext.h eine struct von struct ucontext zu struct ucontext_t umbenannt hat. Ich bin mir sicher, dass es da Gründe für gab, aber meine Phantasie reicht nicht, um mir welche auszudenken. Was für ein Scheiß ist DAS denn bitte?! Firefox hat einen Crash Reporter, und der will halt in diesen Strukturen herumfuhrwerken, weil man das tun muss, wenn man sehen will, in welchem Zustand das Programm beim Crashen war.
Gut, nur ein Dutzend Dateien musste ich anfassen, dann baute Firefox wieder. Aber H.264 war immer noch kaputt.
Und die Story ist richtig interessant, daher will ich sie hier mal bloggen. Wenn man so ein Problem hat, dann ist unter Linux eine der ersten und besten Debugmöglichkeiten, dass man strace benutzt. Programme unter Linux laufen im User Space, und die interagieren mit dem Kernel über Syscalls. strace fängt die Syscalls ab und gibt ihre Argumente und Ergebnisse aus. Hier ist ein Beispiel:
$ strace /opt/diet/bin/cat true.cHier sieht man ganz gut, was cat tut. execve gehört noch nicht zu dem cat selber, das ist der Aufruf von dem cat. arch_prctl ist ein Implementationsdetail, das man auf x86_64 macht, um thread local storage einzurichten (die libc weiß an der Stelle nicht, dass cat das nicht benutzt). strace auf Firefox ist natürlich viel umfangreicher, aber mit ein bisschen Geduld kann man da trotzdem sehen, was passiert, bzw. was nicht passiert. Und zwar sah ich das hier:
execve("/opt/diet/bin/cat", ["/opt/diet/bin/cat", "true.c"], [/* 60 vars */]) = 0
arch_prctl(ARCH_SET_FS, 0x7fff5e690f70) = 0
open("true.c", O_RDONLY) = 3
read(3, "int main() {\n return 0;\n}\n", 65536) = 27
write(1, "int main() {\n return 0;\n}\n", 27int main() {
return 0;
}
) = 27
read(3, "", 65536) = 0
close(3) = 0
exit(0) = ?
+++ exited with 0 +++
7194 openat(AT_FDCWD, "/usr/lib64/libavcodec-ffmpeg.so.57", O_RDONLY|O_CLOEXEC) = 257 7194 --- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_errno=EEXIST, si_call_addr=0x7fc218252840, si_syscall=__NR_openat, si_arch=AUDIT_ARCH_X86_64} --- 7194 socketpair(AF_UNIX, SOCK_SEQPACKET, 0, [39, 40]) = 0 7194 sendmsg(36, {msg_name=NULL, msg_namelen=0, msg_iov=[{iov_base="\0\0\0\0\0\0\10\0\0\0\0\0\0\0\0\0", iov_len=16}, {iov_base="/usr/lib64/libavcodec-ffmpeg.so."…, iov_len=35}, {iov_base=NULL, iov_len=0}], msg_iovlen=3, msg_control=[{cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=SCM_RIGHTS, cmsg_data=[40]}], msg_controllen=24, msg_flags=0}, MSG_NOSIGNAL <unfinished …> 7196 <… recvmsg resumed> {msg_name=NULL, msg_namelen=0, msg_iov=[{iov_base="\0\0\0\0\0\0\10\0\0\0\0\0\0\0\0\0", iov_len=16}, {iov_base="/usr/lib64/libavcodec-ffmpeg.so."…, iov_len=8194}], msg_iovlen=2, msg_control=[{cmsg_len=20, cmsg_level=SOL_SOCKET,cmsg_type=SCM_RIGHTS, cmsg_data=[66]}], msg_controllen=24, msg_flags=MSG_CMSG_CLOEXEC}, MSG_CMSG_CLOEXEC) = 51 7194 <… sendmsg resumed> ) = 51 7196 openat(AT_FDCWD, "/usr/lib64/libavcodec-ffmpeg.so.57", O_RDONLY|O_NOCTTY|O_CLOEXEC <unfinished …> 7194 close(40 <unfinished …> 7196 <… openat resumed> ) = 95 7194 <… close resumed> ) = 0 7196 sendmsg(66, {msg_name=NULL, msg_namelen=0, msg_iov=[{iov_base="\0\0\0\0", iov_len=4}], msg_iovlen=1, msg_control=[{cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=SCM_RIGHTS, cmsg_data=[95]}], msg_controllen=24, msg_flags=0}, MSG_NOSIGNAL <unfinished …> 7194 recvmsg(39, <unfinished …> 7196 <… sendmsg resumed> ) = 4 7194 <… recvmsg resumed> {msg_name=NULL, msg_namelen=0, msg_iov=[{iov_base="\0\0\0\0", iov_len=4}], msg_iovlen=1, msg_control=[{cmsg_len=20, cmsg_level=SOL_SOCKET, cmsg_type=SCM_RIGHTS, cmsg_data=[40]}], msg_controllen=24, msg_flags=MSG_CMSG_CLOEXEC}, MSG_CMSG_CLOEXEC) = 4Die Ausgabe ist ein bisschen verwirrend mit dem unfinished und resumed; das kommt daher, dass Firefox mehr als einen Prozess/Thread aufmacht, und die miteinander reden, und ich alle von denen auf einmal beobachte. Aber mal grob: Prozess A ruft openat(AT_FDCWD,…) auf, das ist äquivalent zu open(…). Kriegt als Ergebnis 257 und direkt ein Signal SIGSYS mit si_code SYS_SECCOMP.
Das ist ziemlich coole Scheiße, weil das genau das ist, was ich auf dem 32c3 in meinem Vortrag "Check your privileges" als Broker-Architektur vorgestellt hatte. Die haben das aber nicht über Überladen von openat gemacht, sondern die haben das so gemacht, dass SECCOMP nicht den Prozess abbricht sondern dieses Signal schickt. Das Signal fangen sie dann ab, und der Handler von dem Signal kann dann nachvollziehen, was der Code zu tun versucht hatte, in diesem Fall openat, und das anders lösen — nämlich über sendmsg an den Broker, wie wir in dem strace schön sehen können. Der Broker macht recvmsg, kriegt die Anfrage (und wir sehen in den Daten von dem sendmsg auch schön den Dateinamen), und öffnet die dann. Die Zahl am Anfang ist übrigens die PID bzw. Thread-ID.
Der Broker macht dann selber nochmal openat, hat keinen SECCOMP-Filter installiert, das openat läuft durch, und dann schickt der Broker mit dem zweiten sendmsg oben den Deskriptor 95 zurück an den anfragenden Prozess in der Sandbox. Der kriegt das dann in dem recvmsg als Deskriptor 40 reingereicht (Deskriptoren sind immer relativ zum Prozess, daher findet hier im Kernel eine Übersetzung statt). Das ist mal echt coole Scheiße, und ich bin einigermaßen schockiert, dass Firefox so Bleeding-Edge-Kram überhaupt implementiert hat. Sehr cool!
Warum ich das hier alles erwähne: weil das bei mir auf die Nase fiel, denn zum Abspielen von H.264 lädt Firefox libavcodec von ffmpeg, und ffmpeg ist gegen einen Haufen Codecs und Libraries gelinkt, und die liegen bei mir eben nicht alle in /usr/lib64, sondern beispielsweise liegt libva in /usr/X11R7/lib64 (libva macht hardware-assistierte Dekodierung und Anzeige von u.a. H.264-Videos). Und es stellt sich raus, dass Firefox in dem Broker eine Liste von erlaubten Verzeichnissen hat, aus denen Dateien geöffnet werden können, und die wird nicht aus /etc/ld.so.conf oder so generiert, sondern die ist hardkodiert im Quellcode. Falls jemand mal selbst gucken will: Das ist in security/sandbox/linux/broker/SandboxBrokerPolicyFactory.cpp (nach policy->AddDir gucken).
Nachdem ich da meine Pfade eingetragen habe, kann der Firefox ffmpeg laden — aber das Video immer noch nicht spielen. Das sei jetzt korrupt, sagt er. Ist es natürlich nicht. Mist. Na mal schauen. Fortschritt ist immer nur ein Schritt zur Zeit.
Update: Ein Einsender zur glibc-Situation:
Das Update habe ich für unsere cross-gebauten Umgebungen diese Woche auch gemacht. Mal so ein paar Sachen, die aufgefallen sind:
- gcc 5.4 baut dann nicht mehr (ucontext_t, auch ein paar Stack-Sachen), betrifft auch neuere Versionen von gcc
- iproute2 4.11 baut auch nicht mehr (4.12 schon, also mir egal). IIRC haben sie irgendwo vergessen stdint.h für UINT16_MAX einzubinden, und das muss vorher zufällig über irgendeinen anderen Header mit reingekommen sein (also ganz klar nicht die Schuld von glibc)
- gdb 8.0 baut nicht mehr auf x64 (ARM geht), irgendwas mit putc(), das nur ein Argument bekommt oder so.
Ganz großes Kino.
In der Tat. o_O