Libseccomp: BUG: gen_bpf_generate() behandelt Fehler nicht richtig

Erstellt am 28. Mai 2020  ·  15Kommentare  ·  Quelle: seccomp/libseccomp

Hallo,

Zunächst einmal vielen Dank für libseccomp -- wir verwenden es seit mehreren Jahren gerne in der Produktion und sind (bis jetzt) ​​auf keine Probleme gestoßen. Ich bin mir nicht sicher, ob dies ein Fehler in unserem Code, ein Missverständnis der Dokumentation oder etwas anderes ist - aber ich habe den letzten Monat vergeblich versucht, dies herauszufinden.

Wir haben kürzlich Pakete in unseren Docker-Containern aktualisiert, darunter ein Upgrade von libseccomp 2.3.3 (Version in stabilen Debian-Repos) auf 2.4.3. Es gab auch andere Systempakete, die ebenfalls aktualisiert wurden, aber ich habe sie nicht aufgezeichnet. Unser Kernel wurde nicht aktualisiert und hat die Version 4.19.0-8-amd64.

Wir verwenden SCMP_ACT_TRACE und bauen einen Filter, der nur aus SCMP_ACT_ALLOW Regeln besteht, die unter Verwendung nativer Systemaufrufnummern anstelle der Pseudonummern von libseccomp hinzugefügt werden. Wir spalten einen 64-Bit-Hilfsprozess ab, der den seccomp-Filter erstellt und lädt, bevor exec weitere 64-Bit-Binärdatei erstellt.

Als Referenz ist dies die Gesamtheit unserer seccomp-Initialisierungsroutine, die eine ähnliche Fehlerprüfung wie die seccomp_rule_add Manpage verwendet .

Unser Aufruf an seccomp_load hat jedoch begonnen, -EINVAL in der Größenordnung von 1 / 100.000 Prozessinitialisierungen zurückzugeben. (Da wir es nicht zuverlässig reproduzieren konnten, war das Debuggen mühsam.) Während dieser Zeit gab es keine Codeänderungen an unserer Anwendung. Die dem Filter hinzugefügten Systemaufrufe sind bei allen Durchläufen identisch.

Irgendwelche Ideen, was schief laufen könnte (oder sogar, wie man weiter in das schief läuft) oder ob dies in irgendeiner Weise erwartet wird? Es gibt nicht viele dynamische bewegliche Teile, und ich konnte in der Dokumentation nichts darüber finden, warum dies passieren könnte.

bug prioritmedium

Hilfreichster Kommentar

Leider noch nicht. Nachdem ein Patch zu seccomp_export_pfc hinzugefügt wurde, war es still. Gestern habe ich diesen Patch auf alle unsere VMs (und nicht nur auf einen Test) übertragen, in der Hoffnung, das Problem zu beheben, wenn es schließlich auftritt.

Ich finde die Stille seltsam, aber im Moment schreibe ich sie dem Zufall zu, da die gesamte Debugging- / Exportlogik _nach_ fehlgeschlagen ist seccomp_load , also sollte sie den Fehler selbst nicht beeinflussen.

Alle 15 Kommentare

Hallo @Xyene ,

Es gibt nicht viele Stellen, die -EINVAL im Codepfad seccomp_load() zurückgeben. Basierend auf einer kurzen Untersuchung des Codes von libseccomp v2.4.3 sieht es so aus, als ob es entweder an einem ungültigen scmp_filter_ctx oder sich der Kernel über den prctl(...) Aufruf beschwert, der den Filter lädt.

Angesichts der Tatsache, dass v2.4.3 im Allgemeinen funktioniert und Sie Ihren Kernel nicht geändert haben, erscheint es zweifelhaft, dass der Aufruf von prctl(...) die Ursache ist, die uns zu einem ungültigen Filterkontext führt. Haben Sie seit dem Upgrade irgendwelche anderen merkwürdigen Verhaltensweisen in Ihrem Programm bemerkt? Ich frage mich, ob es an anderer Stelle ein Speicherbeschädigungsproblem gibt, das das Problem verursacht.

Obwohl es immer möglich ist, dass der Fehler bei libseccomp liegt, führen wir jede Version durch eine Reihe von Prüfungen, die Valgrind-Läufe für alle unsere Regressionstests sowie statische Analysen mit Clang und Coverity umfassen.

Auch wenn dies für v2.4.3 nicht hilft, ist eine der Verbesserungen, die wir für das fast fertige v2.5.0-Release anstreben, eine verbesserte Dokumentation und Handhabung von Fehlercodes.

Wir haben kürzlich Pakete in unseren Docker-Containern aktualisiert, darunter ein Upgrade von libseccomp 2.3.3 (Version in stabilen Debian-Repos) auf 2.4.3. Es gab auch andere Systempakete, die ebenfalls aktualisiert wurden, aber ich habe sie nicht aufgezeichnet. Unser Kernel wurde nicht aktualisiert und hat die Version 4.19.0-8-amd64.

Vielen Dank, dass Sie sich vergewissern, dass sich Ihr Code und der zugrunde liegende Kernel nicht geändert haben. Das sollte helfen, das Problem aufzuspüren.

Als Referenz ist dies die Gesamtheit unserer seccomp-Initialisierungsroutine, die eine ähnliche Fehlerprüfung wie die seccomp_rule_add Manpage verwendet .

Dein Filter sieht für mich vernünftig aus.

Irgendwelche Ideen, was schief laufen könnte (oder sogar, wie man weiter in das schief läuft) oder ob dies in irgendeiner Weise erwartet wird? Es gibt nicht viele dynamische bewegliche Teile, und ich konnte in der Dokumentation nichts darüber finden, warum dies passieren könnte.

Ich habe den seccomp_load() Code der Version 2.4.3 durchgesehen und denke, es gibt nur zwei Stellen, an denen libseccomp einen Rückgabecode von -EINVAL generiert:

Beide der oben genannten Fehler werden effektiv durch einen ungültigen Filter verursacht. Das scheint mir aufgrund Ihres Filtercodes unwahrscheinlich.

Es ist erwähnenswert, dass der Standard-Rückgabewert des Kernels in seccomp_set_mode_filter() -EINVAL ist. Sie erwähnen, dass Sie Docker ausführen; Deaktivierst du den standardmäßigen Docker-Seccomp-Filter?

Ich wäre versucht, Ihrem Code mehr Debugging hinzuzufügen, wenn nach seccomp_load() fehlschlägt. Zum Beispiel könnten wir den PFC und/oder den BPF des Filters selbst ausgeben, um zu überprüfen, ob er vernünftig aussieht. Siehe seccomp_export_pfc() und seccomp_export_bpf() .

Ich habe den seccomp_load() Code der Version 2.4.3 durchgesehen und denke, es gibt nur zwei Stellen, an denen libseccomp einen Rückgabecode von -EINVAL generiert:

Denken Sie daran, dass alle Fehler, die in gen_bpf_generate(...) oder darunter gefunden werden, effektiv in -ENOMEM von sys_filter_load(...) auf src/system.c:267 zusammengefasst werden .

Ich hasse es, auf "Speicherkorruption" zurückzufallen! so schnell, aber es sieht so aus, als ob das hier der Fall sein könnte.

Danke für die schnellen und ausführlichen Antworten! Sie haben mehrere Wege der Erforschung geschaffen :slightly_smiling_face:

Haben Sie seit dem Upgrade irgendwelche anderen merkwürdigen Verhaltensweisen in Ihrem Programm bemerkt? Ich frage mich, ob es an anderer Stelle ein Speicherbeschädigungsproblem gibt, das das Problem verursacht.

Nein, nur das. Unsere Unit- und Integrationstests bestehen weiterhin und abgesehen von diesem sehr seltenen EINVAL werden keine Fehler in prod. Dies macht es sicherlich rätselhaft; Ich habe auch eine Speicherbeschädigung vermutet, konnte aber keine Beweise dafür finden :slightly_frowning_face:

Für etwas mehr Kontext:

  • Das Programm ist eine Python-App, die über Cython in C++ aufruft (die GIL wird während dieser Zeit gehalten, sodass Python keine Allocs auf anderen Threads vornimmt)
  • die C++-Seitengabeln und im untergeordneten Element den seccomp-Filter vor der Ausführung einrichten
  • alle Speicherzuweisungen, die post-fork, pre-exec passieren, stammen von libseccomp selbst, in seccomp_init usw.
  • Es gibt genau 4 Array-Mallocs zwischen dem Aufrufen in C++-Code und dem Abzweigen, sie sind alle in Cython und haben beim Schreiben entsprechende Bereiche (Zeile 435 ruft den zuvor verknüpften seccomp-Code auf) -- alle anderen Zuweisungen/Schreibvorgänge/etc. befinden sich im Python-Interpreter

Beim Eintippen hatte ich eine Idee: Ich habe Horrorgeschichten darüber gehört, dass malloc nach dem Forking unsicher ist, und wir haben einige in libseccomp selbst. Die Python-App selbst _ist_ multithreaded, aber wir halten die GIL immer im nativen Code, daher sollte dies sicher sein (?). Ich habe jedoch nur von Deadlocks gehört, die durch malloc-after-fork auftreten. (Ich schätze, dadurch wird der nächste Geschäftsauftrag seccomp_init et al. vor der Gabelung verschoben, nur seccomp_load nach der Gabelung aufgerufen und geprüft, ob die Fehler weiterhin auftreten.)

Ich wäre versucht, Ihrem Code innerhalb von if after seccomp_load() etwas mehr Debugging hinzuzufügen.

Danke für den Vorschlag! Ich habe einen Aufruf von seccomp_export_pfc hinzugefügt und den Inhalt der Eingabe in den Filter ausgegeben ( config->syscall_whitelist ). Ich werde nachfassen, wenn dies das nächste Mal fehlschlägt.

Hallo @Xyene - da es ungefähr eine Woche her ist, wollte ich nur

Leider noch nicht. Nachdem ein Patch zu seccomp_export_pfc hinzugefügt wurde, war es still. Gestern habe ich diesen Patch auf alle unsere VMs (und nicht nur auf einen Test) übertragen, in der Hoffnung, das Problem zu beheben, wenn es schließlich auftritt.

Ich finde die Stille seltsam, aber im Moment schreibe ich sie dem Zufall zu, da die gesamte Debugging- / Exportlogik _nach_ fehlgeschlagen ist seccomp_load , also sollte sie den Fehler selbst nicht beeinflussen.

Fortschritt!

Es stellte sich heraus, dass der Grund für die Stille darin besteht, dass seccomp_export_bpf Segfaulting verursachte (sollte es, wenn es nach seccomp_load aufgerufen wird?) Und das wurde an anderer Stelle gemeldet und nicht dort, wo ich nach Seccomp-Fehlern suchte. Noch wichtiger ist, dass ich auf einen Fall gestoßen bin, in dem ich das Problem in ~ 150 Aufrufen zuverlässig reproduzieren kann, sodass ich mit etwas Klempnerarbeit in der Lage sein sollte, einige Core-Dumps zu extrahieren.

Okay, ich habe einen Coredump herausgezogen und das war die Spur: https://gist.github.com/Xyene/920f1cb098784a031f53c66a2f49d167

Dies war ein wenig verdächtig, da es innerhalb der realloc Routine von jemalloc abstürzt. Darüber hinaus behebt die Verwendung von glibc malloc stattdessen das Problem (leider ist dies in diesem Fall aufgrund von Fragmentierungsproblemen keine langfristige Option).

Als nächstes zog ich jemalloc ein, kompilierte es mit -O0 und Debugging-Symbolen und ließ die Reproduktion erneut laufen. Dieses Mal stürzte es in seccomp_load und nicht danach! Ich habe diesen Trace hier hochgeladen: https://gist.github.com/Xyene/5da56168bcea337da85b2cd30704d12e

Ein Ausschnitt dieser Spur:

#9  0x00007ff962698495 in free (ptr=0x5a5a5a5a5a5a5a5a) at src/jemalloc.c:2867
No locals.
#10 0x00007ff96062d087 in _program_free (prg=prg@entry=0x7ff95e963010) at gen_bpf.c:511
No locals.
#11 0x00007ff96062f605 in gen_bpf_release (program=program@entry=0x7ff95e963010) at gen_bpf.c:1986
No locals.
#12 0x00007ff96062c04f in sys_filter_load (col=col@entry=0x7ff95e9a5000) at system.c:293
        rc = -1
        prgm = 0x7ff95e963010
#13 0x00007ff96062b666 in seccomp_load (ctx=ctx@entry=0x7ff95e9a5000) at api.c:286
        col = 0x7ff95e9a5000

Bei der Suche nach jemalloc sieht es so aus, als ob 0x5a verwendet wird, um freie Bytes als frei zu markieren , mit der spezifischen Absicht, Code zum Absturz zu bringen, der versucht, etwas freizugeben, das bereits freigegeben wurde.

gen_bpf.c:511 in v2.4.3 ist: https://github.com/seccomp/libseccomp/blob/1dde9d94e0848e12da20602ca38032b91d521427/src/gen_bpf.c#L505 -L513

Dies macht jedoch nicht viel Sinn, da die Lebensdauer des Programms nur der Körper von sys_filter_load :

https://github.com/seccomp/libseccomp/blob/1dde9d94e0848e12da20602ca38032b91d521427/src/system.c#L260 -L296

Ich glaube, ich habe mindestens ein Problem entdeckt. In gen_bpf_generate ;

https://github.com/seccomp/libseccomp/blob/1dde9d94e0848e12da20602ca38032b91d521427/src/gen_bpf.c#L1963 -L1966

state.bpf = prgm solange zmalloc nicht fehlgeschlagen ist. Als nächstes wird _gen_bpf_build_bpf aufgerufen, und basierend auf rc wird state.bpf auf NULL .

https://github.com/seccomp/libseccomp/blob/1dde9d94e0848e12da20602ca38032b91d521427/src/gen_bpf.c#L1968 -L1971

Betrachtet man den Fall, in dem rc != 0 , state.bpf zum Zeitpunkt des Aufrufs von _state_release immer noch auf prgm _state_release . Dadurch wird der Speicher freigegeben, auf den prgm .

https://github.com/seccomp/libseccomp/blob/1dde9d94e0848e12da20602ca38032b91d521427/src/gen_bpf.c#L539

Als nächstes wird gen_bpf_generate return prgm , was, obwohl es freigegeben wurde, immer noch ein Zeiger ungleich Null ist.

https://github.com/seccomp/libseccomp/blob/1dde9d94e0848e12da20602ca38032b91d521427/src/gen_bpf.c#L1971 -L1974

Zurück in sys_filter_load , gen_bpf_generate kehrt zurück und prgm ist nicht NULL also geht es weiter.

https://github.com/seccomp/libseccomp/blob/1dde9d94e0848e12da20602ca38032b91d521427/src/system.c#L265 -L267

Schließlich wird am Ende von sys_filter_load gen_bpf_release für das bereits freie prgm aufgerufen.

https://github.com/seccomp/libseccomp/blob/1dde9d94e0848e12da20602ca38032b91d521427/src/system.c#L292 -L295

Dies geht nicht auf die Besorgnis ein, warum _gen_bpf_build_bpf überhaupt fehlschlagen würde, aber es scheint etwas Schlimmes zu sein, das passieren könnte, wenn dies der Fall wäre.

Bearbeiten: Tatsächlich sieht dies so aus, als ob es wahrscheinlich als Nebeneffekt von https://github.com/seccomp/libseccomp/commit/3a1d1c977065f204b96293ccfe7d3e5aa0d7ace behoben wurde

Betrachtet man den Fall, dass rc != 0 ist, ist state.bpf zum Zeitpunkt des Aufrufs von _state_release immer noch auf prgm gesetzt. Dadurch wird der Speicher freigegeben, auf den prgm verweist.

Ah ha! Guter Fang @Xyene!

Ich denke, wir müssen dies über 3a1d1c977065f204b96293cccfe7d3e5aa0d7ace hinaus beheben, lassen Sie mich eine Minute darüber nachdenken ... Ich glaube nicht, dass die Lösung zu schwer sein wird ... und sehen Sie, ob ich mit einer PR aufwarten kann.

Ich denke, wir müssen dies über 3a1d1c9 hinaus beheben, lassen Sie mich eine Minute darüber nachdenken ... Ich glaube nicht, dass die Lösung zu schwer sein wird ... und sehen Sie, ob ich eine PR herausbringen kann.

Ups, ich habe mir alten Code angesehen, als ich das geschrieben habe; Ja, ich glaube, dass 3a1d1c9 das für uns behebt, aber wir brauchen einen Patch für den Release-2.4-Zweig. Daran werde ich jetzt arbeiten.

_(Meta: Ich werde diese Nachricht weiterhin mit meinen Ergebnissen aktualisieren, damit ich sie irgendwo aufschreiben kann, ohne euch E-Mails zu spammen :)_

In Ordnung, zurück auf 2.4.3 mit dem angewendeten Patch konnte ich den Filter entfernen, der fehlgeschlagen war: link .

Die gemeldete Ursache ist jetzt ENOMEM anstelle von EINVAL , was vermutlich erwartet wird, da _gen_bpf_build_bpf fehlschlägt und ein NULL Programm zurückgibt. Der PFC druckt aber gut aus. Wenn Sie den Seccomp-Code ändern, um den Rückgabewert von _gen_bpf_build_bpf zu melden, wird EFAULT als Ursache angezeigt.

Als schnellen Hack habe ich :%s/return -EFAULT/abort() über src/gen_bpf.c und konnte diesen Stack-Trace extrahieren:

EFAULT-Stacktrace

(gdb) bt full
#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
        set = {__val = {0, 140084028365964, 140083248439464, 140083248438968, 140083248431088, 140084028368143, 28659884033, 140083965300736, 
            140083248439464, 140083248438968, 140083248431088, 140084028351031, 140084019988760, 140083248439624, 140083248431200, 140084028372597}}
        pid = <optimized out>
        tid = <optimized out>
        ret = <optimized out>
#1  0x00007f67daa4d55b in __GI_abort () at abort.c:79
        save_stage = 1
        act = {__sigaction_handler = {sa_handler = 0x7f67d6f3eec0, sa_sigaction = 0x7f67d6f3eec0}, sa_mask = {__val = {140083965300736, 
              140083965300736, 0, 0, 140083248438968, 140083248438968, 140083248439464, 140083248431504, 140084028417173, 140083964793344, 
              140083965300736, 140083248431552, 140083994791895, 140083248431552, 140083994787642, 140083965300736}}, sa_flags = -1404894496, 
          sa_restorer = 0x0}
        sigs = {__val = {32, 0 <repeats 15 times>}}
#2  0x00007f67d8bfd455 in _gen_bpf_build_bpf (state=0x7f67ac4302e0, col=0x7f67d6f63040) at gen_bpf.c:1943
        rc = 0
        iter = 1
        h_val = 1425818561
        res_cnt = 0
        jmp_len = 0
        arch_x86_64 = 0
        arch_x32 = -1
        instr = {op = 32, jt = {tgt = {imm_j = 0 '\000', imm_k = 0, hash = 0, db = 0x0, blk = 0x0, nxt = 0}, type = TGT_NONE}, jf = {tgt = {
              imm_j = 0 '\000', imm_k = 0, hash = 0, db = 0x0, blk = 0x0, nxt = 0}, type = TGT_NONE}, k = {tgt = {imm_j = 4 '\004', imm_k = 4, 
              hash = 4, db = 0x4, blk = 0x4, nxt = 4}, type = TGT_K}}
        i_iter = 0x7f67d6fdcb60
        b_badarch = 0x7f67d6fd9000
        b_default = 0x7f67d6fd9060
        b_head = 0x7f67d6fda1a0
        b_tail = 0x7f67d6fd9000
        b_iter = 0x0
        b_new = 0x7f67d6fe3300
        b_jmp = 0x0
        db_secondary = 0x0
        pseudo_arch = {token = 0, token_bpf = 0, size = ARCH_SIZE_UNSPEC, endian = ARCH_ENDIAN_LITTLE, syscall_resolve_name = 0x0, 
          syscall_resolve_num = 0x0, syscall_rewrite = 0x0, rule_add = 0x0}
#3  0x00007f67d8bfd560 in gen_bpf_generate (col=0x7f67d6f63040) at gen_bpf.c:1971
        rc = 0
        state = {htbl = {0x0 <repeats 256 times>}, attr = 0x7f67d6f63044, bad_arch_hsh = 889798935, def_hsh = 742199527, arch = 0x7f67ac4301e0, 
          bpf = 0x7f67d6f64010}
        prgm = 0x7f67d6f64010
#4  0x00007f67d8bf64a7 in sys_filter_load (col=0x7f67d6f63040) at system.c:265
        rc = 32615
        prgm = 0x0
#5  0x00007f67d8bf4f10 in seccomp_load (ctx=0x7f67d6f63040) at api.c:287
        col = 0x7f67d6f63040

Das entspricht Zeile 1943:

https://github.com/seccomp/libseccomp/blob/1dde9d94e0848e12da20602ca38032b91d521427/src/gen_bpf.c#L1935 -L1943

Angesichts der Art der Ersetzung denke ich, dass wir jedes EFAULT in jeder Hilfsfunktion ausschließen können, da diese zuerst abgebrochen worden wären.

Danach habe ich versucht, dasselbe mit HEAD zu reproduzieren - es passiert immer noch. Als nächstes %s:/goto build_bpf_free_blks/abort() und wiederholen. Die Ursache war:

https://github.com/seccomp/libseccomp/blob/34bf78abc9567b66c72dbe67e7f243072162a25f/src/gen_bpf.c#L2219 -L2220

Zum Glück war diese Funktion kurz und hatte nur eine Handvoll Fehlerpunkte. Eine weitere Runde von abort Einfügungen später;

Verfolgen

(gdb) bt full
#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
        set = {__val = {0, 140050183343588, 0, 448, 140049402494880, 140049402509040, 140049402494832, 140050183342988, 140049402495088, 
            140049402509040, 140049402494896, 140050183343588, 4294967296, 140049402509040, 140049402509040, 140049402509040}}
        pid = <optimized out>
        tid = <optimized out>
        ret = <optimized out>
#1  0x00007f5ff953055b in __GI_abort () at abort.c:79
        save_stage = 1
        act = {__sigaction_handler = {sa_handler = 0x7f5ff595d260, sa_sigaction = 0x7f5ff595d260}, sa_mask = {__val = {139642271694862, 
              140050119389792, 0, 0, 140049402502840, 0, 140049402503336, 140049402502888, 140049402502840, 112, 384, 140049402502840, 140050149861504, 
              140049402495328, 140050149857273, 392}}, sa_flags = 448, sa_restorer = 0x7f5ff595d240}
        sigs = {__val = {32, 0 <repeats 15 times>}}
#2  0x00007f5ff76edee5 in _bpf_append_blk (prg=0x7f5ff5964010, blk=0x7f5ff59df1a0) at gen_bpf.c:452
        rc = -12
        i_new = 0x0
        i_iter = 0x7f5ff59fa178
        old_cnt = 48
        iter = 1
#3  0x00007f5ff76f3716 in _gen_bpf_build_bpf (state=0x7f5fcae302d0, col=0x7f5ff59c5000) at gen_bpf.c:2223
        rc = 0
        iter = 1
        h_val = 1425818561
        res_cnt = 0
        jmp_len = 0
        arch_x86_64 = 0
        arch_x32 = -1
        instr = {op = 32, jt = {tgt = {imm_j = 0 '\000', imm_k = 0, hash = 0, db = 0x0, blk = 0x0, nxt = 0}, type = TGT_NONE}, jf = {tgt = {
              imm_j = 0 '\000', imm_k = 0, hash = 0, db = 0x0, blk = 0x0, nxt = 0}, type = TGT_NONE}, k = {tgt = {imm_j = 4 '\004', imm_k = 4, 
              hash = 4, db = 0x4, blk = 0x4, nxt = 4}, type = TGT_K}}
        i_iter = 0x7f5ff59e1b60
        b_badarch = 0x7f5ff59de000
        b_default = 0x7f5ff59de060
        b_head = 0x7f5ff59df1a0
        b_tail = 0x7f5ff59de000
        b_iter = 0x7f5ff59df1a0
        b_new = 0x7f5ff59e8300
        b_jmp = 0x7f5ff59df0e0
        db_secondary = 0x0
        pseudo_arch = {token = 0, token_bpf = 0, size = ARCH_SIZE_UNSPEC, endian = ARCH_ENDIAN_LITTLE, syscall_resolve_name = 0x0, 
          syscall_resolve_num = 0x0, syscall_rewrite = 0x0, rule_add = 0x0}
#4  0x00007f5ff76f3874 in gen_bpf_generate (col=0x7f5ff59c5000, prgm_ptr=0x7f5fcae30b40) at gen_bpf.c:2270
        rc = 0
        state = {htbl = {0x0, 0x7f5ff593ef80, 0x7f5ff593efe0, 0x7f5ff593efc0, 0x0, 0x7f5ff595d000, 0x7f5ff593ef60, 0x7f5ff593ef00, 
            0x0 <repeats 248 times>}, attr = 0x7f5ff59c5004, bad_arch_hsh = 889798935, def_hsh = 742199527, bpf = 0x7f5ff5964010, 
          arch = 0x7f5fcae301c0, b_head = 0x7f5ff59e8300, b_tail = 0x7f5ff59de120, b_new = 0x7f5ff59e8300}
        prgm = <optimized out>
#5  0x00007f5ff76eb275 in sys_filter_load (col=0x7f5ff59c5000, rawrc=false) at system.c:307
        rc = 0
        prgm = 0x0
#6  0x00007f5ff76e9505 in seccomp_load (ctx=0x7f5ff59c5000) at api.c:386
        col = 0x7f5ff59c5000
        rawrc = false

https://github.com/seccomp/libseccomp/blob/34bf78abc9567b66c72dbe67e7f243072162a25f/src/gen_bpf.c#L449 -L452

Also schlägt realloc erneut fehl und _bpf_append_blk gibt -ENOMEM , das von _gen_bpf_build_bpf maskiert und in -EFAULT . Das ist keine große Sache, aber da Sie sagten, eine bessere Fehlerberichterstattung sei ein Ziel von 2,5, dachte ich, ich würde es erwähnen, da dies im Geltungsbereich aussieht :slightly_smiling_face:

Einige stochern mit GDB:

(gdb) f 2
#2  0x00007f5ff76edee5 in _bpf_append_blk (prg=0x7f5ff5964010, blk=0x7f5ff59df1a0) at gen_bpf.c:452
452         abort();
(gdb) info args
prg = 0x7f5ff5964010
blk = 0x7f5ff59df1a0
(gdb) print prg->blks
$4 = (bpf_instr_raw *) 0x7f5ff59fa000
(gdb) x/32bx &prg->blks
0x7f5ff5964018: 0x00    0xa0    0x9f    0xf5    0x5f    0x7f    0x00    0x00
0x7f5ff5964020: 0x5a    0x5a    0x5a    0x5a    0x5a    0x5a    0x5a    0x5a
0x7f5ff5964028: 0x5a    0x5a    0x5a    0x5a    0x5a    0x5a    0x5a    0x5a
0x7f5ff5964030: 0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
(gdb) print ((prg)->blk_cnt * sizeof(*((prg)->blks)))
$5 = 392
(gdb) print prg->blk_cnt
$6 = 49

Dies sieht wirklich nach einem Ausfall des Allokators aus ...

Aha, diese Geschichte hat endlich ihren _spannenden_ Abschluss erreicht – ich habe herausgefunden, was passiert ist, und eine Lösung überprüft :slightly_smiling_face:

Da dies eine interessante Geschichte sein könnte, ist sie hier:

Der Hauptprozess, der den Worker abzweigt, befindet sich normalerweise bei ~80 MB RSS. Nachdem es gegabelt wurde, beschränkt es die Speichernutzung über rlimit , manchmal auf 64 MB. Dies bringt es in eine Position, in der seine aktuelle Speichernutzung sein Limit überschreitet, aber dies wird von rlimit . _Meistens_ wird der Speicherzuordner genügend freien Speicher haben, um die Initialisierungsroutinen von libseccomp zu bedienen, ohne mehr vom Kernel anzufordern. Aber wenn dies _nicht_ ist und Platz für eine zusätzliche Arena oder ähnliches benötigt wird, stellt der Kernel ihn nicht zur Verfügung, da der Prozess bereits seine Grenzen überschritten hat.

In 2.4.3 manifestierte sich dieser Fehler beim Abrufen von Speicher in EINVAL und einem Double-free. Im Master-Post- https://github.com/seccomp/libseccomp/commit/3a1d1c977065f204b96293cccfe7d3e5aa0d7ace wird stattdessen EFAULT gemeldet. Mit https://github.com/seccomp/libseccomp/pull/257 wird ENOMEM korrekt gemeldet.

Der Grund, warum dies so selten vorkommt, wird dann offensichtlich: Es hängt ganz davon ab, ob der Allocator genügend Speicher hat, um das BPF-Programm zu bauen, ohne vom Kernel mehr anzufordern. Der Allocator von glibc ist lockerer, wenn es darum geht, Fragmentierung aufzubauen, so dass dies nie passiert ist, wenn er vorhanden ist. jemalloc setzt engere Grenzen und führt zu einer erhöhten Wahrscheinlichkeit, dass während seccomp_load Speicher angefordert werden muss – gerade genug, um die resultierenden Fehler zu bemerken, aber es ist immer noch ärgerlich, sie aufzuspüren.

Die Lösung besteht also darin, alle setrlimit Aufrufe nach _after_ seccomp_load . Dadurch schlägt realloc in _bpf_append_blk nicht mehr fehl und der Filter wird erfolgreich geladen. Dies bedeutet, dass der Filter setrlimit zulassen muss, aber in meinem Fall war dies akzeptabel. Allgemeiner gesagt denke ich, dass dieses Problem durch etwas wie https://github.com/seccomp/libseccomp/issues/123 gelöst werden würde

@pcmoore , @drakenclimber -- nochmals

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen