Libseccomp: RFE: genauer definierte Fehlerrückgabewerte

Erstellt am 7. Okt. 2017  ·  24Kommentare  ·  Quelle: seccomp/libseccomp

Wenn man sich die libseccomp-Fehlerrückgabewerte zu Systemaufrufen ansieht, ist es nicht möglich festzustellen, welche der folgenden Gründe die Fehler verursacht haben:

  1. Syscall existiert auf einigen Archs nicht
  2. syscall kann auf einigen Archs nicht abgeglichen werden (weil es gemultiplext ist, denken Sie an Socket/Socketcall)
  3. andere Fehlerfälle

Beim Erstellen des seccomp-Filters kann der Aufrufer einige dieser Gründe als schwerwiegend betrachten, die anderen jedoch nicht, sodass detailliertere Fehlerinformationen (größerer Satz von Fehlerwerten als nur -EINVAL ) erforderlich wären.

Siehe auch systemd PR 6952 .

enhancement prioritmedium

Hilfreichster Kommentar

Vielen Dank. Ich habe ein unausgegorenes Patchset, das ich fertigstellen und als PR zur Überprüfung einreichen werde.

Alle 24 Kommentare

Hallo @topimiettinen , es tut mir leid, dass es so lange gedauert hat, aber ich denke, es ist an der Zeit, dass wir das beheben.

@drakenclimber das wird ein Trottel. Alle libseccomp-APIs sollten an dieser Stelle Manpages haben (wenn nicht, müssen wir dafür ein Problem erstellen), wobei alle Manpages einige handgewellte Kommentare "negative Werte bei Fehler" im Abschnitt RETURN VALUE enthalten. Ich denke, wir müssen Folgendes tun:

  • jeden API-Aufruf manuell prüfen, um eine Liste möglicher Rückgabewerte zu generieren
  • Entscheiden Sie, ob diese Rückgabewerte sinnvoll sind, ändern Sie den Code, wenn dies nicht der Fall ist
  • Dokumentieren Sie jeden möglichen Rückgabewert in der zugehörigen Hilfeseite mit einer kurzen Erläuterung dessen, was der Fehlercode anzeigt

Gedanken?

Hallo @topimiettinen , es tut mir leid, dass es so lange gedauert hat, aber ich denke, es ist an der Zeit, dass wir das beheben.

@drakenclimber das wird ein Trottel. Alle libseccomp-APIs sollten an dieser Stelle Manpages haben (wenn nicht, müssen wir dafür ein Problem erstellen), wobei alle Manpages einige handgewellte Kommentare "negative Werte bei Fehler" im Abschnitt RETURN VALUE enthalten. Ich denke, wir müssen Folgendes tun:

* manually audit each API call to generate a list of possible return values

* decide if these return values make sense, modify the code if they don't

* document each possible return value in the associated manpage with a brief explanation of what the error code indicates

Gedanken?

Verdammt ... Ich stimme widerwillig allem zu, was Sie oben geschrieben haben, insbesondere dem erforderlichen Aufwand :). Die Rückgabecodes richtig zu bekommen und sie dann zu dokumentieren, wird eine wirklich große Anstrengung sein.

Und ja, obwohl es keine glamouröse Arbeit ist, denke ich, dass es kritisch ist. Ich habe an einigen Cgroup-Sachen gearbeitet und wir sind kürzlich auf eine Container-Implementierung gestoßen, die eine Cgroup-Funktion völlig missverstanden hat ... aber es gab kein endgültiges Dokument im Kernel oder anderswo, um den Weg richtig zu weisen, also haben sie dafür gesorgt, dass es so gut wie möglich funktioniert sie könnten.

Es ist großartig, Aktivitäten zu diesem Thema zu sehen! Ich bin nicht gegen eine gründliche Überprüfung, aber die ursprüngliche Anfrage beschränkte sich nur darauf, verschiedene Fehlermodi unterscheiden zu können, was etwas orthogonal ist. Die Überprüfung würde sicherlich helfen, bis zu einem gewissen Grad sogar vorausgesetzt, denke ich.

Es ist großartig, Aktivitäten zu diesem Thema zu sehen! Ich bin nicht gegen eine gründliche Überprüfung, aber die ursprüngliche Anfrage beschränkte sich nur darauf, verschiedene Fehlermodi unterscheiden zu können, was etwas orthogonal ist. Die Überprüfung würde sicherlich helfen, bis zu einem gewissen Grad sogar vorausgesetzt, denke ich.

Wir müssen die Überprüfung irgendwann durchführen, und das könnte genauso gut jetzt sein. Je länger wir warten, desto weniger nützlich werden die Fehlercodes für Anrufer, und der ganze Sinn von libseccomp besteht darin, dieses Zeug benutzerfreundlicher zu machen :)

Verdammt ... Ich stimme widerwillig allem zu, was Sie oben geschrieben haben, insbesondere dem erforderlichen Aufwand :). Die Rückgabecodes richtig zu bekommen und sie dann zu dokumentieren, wird eine wirklich große Anstrengung sein.

Ja, das ist einer der Gründe, warum dieses Problem so lange gesessen hat, aber ich habe es lange genug aufgeschoben (zumindest kann @drakenclimber sagen, dass er es für weniger als ein Jahr aufgeschoben hat!). Später heute (morgen?) werde ich dies in Stücke/mehrere Probleme aufteilen (mit einigen Vorschlägen), um es ein bisschen einfacher zu machen, es in Stücken anzugehen.

Etwas positiv ist, dass es so aussieht, als würde libseccomp wirklich nur neun eindeutige errno-Werte verwenden (gemäß einer sehr groben Überprüfung):

# grep -e "-E[A-Z0-9]\+" src/*.{h,c} | sed 's/.*-\(E[A-Z0-9]\+\).*/\1/' | sort -u
EACCES
EDOM
EEXIST
EFAULT
EINVAL
ENOMEM
EOPNOTSUPP
EPERM
ESRCH

... dies sollte dazu beitragen, den Problemraum ein wenig zu verkleinern, insbesondere wenn wir uns auf einen gemeinsamen semantischen Wert für jeden Fehlercode in der gesamten Bibliothek einigen können (was wir auf jeden Fall tun sollten).

Dies ist etwas später als beabsichtigt, aber hier ist eine vollständige Liste der Funktionen, aus denen die libseccomp-API besteht:

const struct scmp_version *seccomp_version(void)
unsigned int seccomp_api_get(void)
int seccomp_api_set(unsigned int level)
scmp_filter_ctx seccomp_init(uint32_t def_action)
int seccomp_reset(scmp_filter_ctx ctx, uint32_t def_action)
void seccomp_release(scmp_filter_ctx ctx)
int seccomp_merge(scmp_filter_ctx ctx_dst, scmp_filter_ctx ctx_src)
uint32_t seccomp_arch_resolve_name(const char *arch_name)
uint32_t seccomp_arch_native(void)
int seccomp_arch_exist(const scmp_filter_ctx ctx, uint32_t arch_token)
int seccomp_arch_add(scmp_filter_ctx ctx, uint32_t arch_token)
int seccomp_arch_remove(scmp_filter_ctx ctx, uint32_t arch_token)
int seccomp_load(const scmp_filter_ctx ctx)
int seccomp_attr_get(const scmp_filter_ctx ctx, enum scmp_filter_attr attr, uint32_t *value)
int seccomp_attr_set(scmp_filter_ctx ctx, enum scmp_filter_attr attr, uint32_t value)
char *seccomp_syscall_resolve_num_arch(uint32_t arch_token, int num)
int seccomp_syscall_resolve_name_arch(uint32_t arch_token, const char *name)
int seccomp_syscall_resolve_name_rewrite(uint32_t arch_token, const char *name)
int seccomp_syscall_resolve_name(const char *name)
int seccomp_syscall_priority(scmp_filter_ctx ctx, int syscall, uint8_t priority)
int seccomp_rule_add_array(scmp_filter_ctx ctx, uint32_t action, int syscall, unsigned int arg_cnt, const struct scmp_arg_cmp *arg_array)
int seccomp_rule_add(scmp_filter_ctx ctx, uint32_t action, int syscall, unsigned int arg_cnt, ...)
int seccomp_rule_add_exact_array(scmp_filter_ctx ctx, uint32_t action, int syscall, unsigned int arg_cnt, const struct scmp_arg_cmp *arg_array)
int seccomp_rule_add_exact(scmp_filter_ctx ctx, uint32_t action, int syscall, unsigned int arg_cnt, ...)
int seccomp_notify_alloc(struct seccomp_notif **req, struct seccomp_notif_resp **resp)
void seccomp_notify_free(struct seccomp_notif *req, struct seccomp_notif_resp *resp)
int seccomp_notify_receive(int fd, struct seccomp_notif *req)
int seccomp_notify_respond(int fd, struct seccomp_notif_resp *resp)
int seccomp_notify_id_valid(int fd, uint64_t id)
int seccomp_notify_fd(const scmp_filter_ctx ctx)
int seccomp_export_pfc(const scmp_filter_ctx ctx, int fd)
int seccomp_export_bpf(const scmp_filter_ctx ctx, int fd)

... dieser Funktionen müssen wir uns nur um die Funktionen kümmern, die "int" zurückgeben.

Mögliche Gruppierungen von Funktionen, die ähnliche Codepfade und Rückgabewerte haben sollten.

  • Gruppe A
int seccomp_arch_exist(const scmp_filter_ctx ctx, uint32_t arch_token)
int seccomp_arch_add(scmp_filter_ctx ctx, uint32_t arch_token)
int seccomp_arch_remove(scmp_filter_ctx ctx, uint32_t arch_token)
  • Gruppe B
int seccomp_attr_get(const scmp_filter_ctx ctx, enum scmp_filter_attr attr, uint32_t *value)
int seccomp_attr_set(scmp_filter_ctx ctx, enum scmp_filter_attr attr, uint32_t value)
  • Gruppe C
int seccomp_syscall_resolve_name_arch(uint32_t arch_token, const char *name)
int seccomp_syscall_resolve_name_rewrite(uint32_t arch_token, const char *name)
int seccomp_syscall_resolve_name(const char *name)
  • Gruppe D
int seccomp_rule_add_array(scmp_filter_ctx ctx, uint32_t action, int syscall, unsigned int arg_cnt, const struct scmp_arg_cmp *arg_array)
int seccomp_rule_add(scmp_filter_ctx ctx, uint32_t action, int syscall, unsigned int arg_cnt, ...)
int seccomp_rule_add_exact_array(scmp_filter_ctx ctx, uint32_t action, int syscall, unsigned int arg_cnt, const struct scmp_arg_cmp *arg_array)
int seccomp_rule_add_exact(scmp_filter_ctx ctx, uint32_t action, int syscall, unsigned int arg_cnt, ...)
  • Gruppe E
int seccomp_notify_receive(int fd, struct seccomp_notif *req)
int seccomp_notify_respond(int fd, struct seccomp_notif_resp *resp)
int seccomp_notify_id_valid(int fd, uint64_t id)
int seccomp_notify_fd(const scmp_filter_ctx ctx)
  • Gruppe F
int seccomp_load(const scmp_filter_ctx ctx)
int seccomp_export_bpf(const scmp_filter_ctx ctx, int fd)

... wenn sich die Funktion nicht in einer der oben genannten Gruppen befindet, ist sie wahrscheinlich in ihrem Codepfad und/oder Rückgabewert eindeutig.

Gruppe C gibt nur __NR_SCMP_ERROR zurück.

Gruppe D kann EINVAL, EPERM, EOPNOTSUPP, ENOMEM, EDOM, EFAULT, EEXIST zurückgeben.

seccomp_load() kann EINVAL, ENOMEM, ESRCH und auch errno-Werte von prctl() (nur EACCES, EFAULT, EINVAL für PR_SET_NO_NEW_PRIVS und PR_SET_SECCOMP) und seccomp() (EACCES, EFAULT, EINVAL, ENOMEM, ESRCH) Systemaufrufe zurückgeben, gemäß deren Handbuch Seiten.

Wenn es darum geht, dass libseccomp Systemaufruffehler an den Aufrufer zurückgibt, z. B. prctl() und seccomp(), denke ich, dass wir diese nur hinter einem einzelnen errno-Wert (vielleicht ENOSYS?) verstecken müssen, damit libseccomp davon nicht betroffen ist alle Änderungen im Kernel (oder ABI-Unterschiede).

Wenn dies ein Problem für das Debuggen wird, könnten wir vielleicht ein neues attr einführen, das den errno-Wert direkt an den Aufrufer zurückgibt.

Möglich, aber dann wäre es schön sicherzustellen, dass es keinen ABI/API-Bruch gibt, wenn die Benutzer von libseccomp bereits bestimmte Fehlerwerte erwarten. Das ursprüngliche Problem war, dass einige Systemaufrufe nur auf einigen Architekturen existieren (wie ugetrlimit nur auf x86_32), aber dies konnte nicht von einem falsch geschriebenen Systemaufrufnamen unterschieden werden, sodass andere (möglicherweise synthetische) Fehlercodes benötigt würden .

Nun, wie wir bereits gesagt haben, garantieren wir derzeit nicht wirklich bestimmte Errno-Werte, sondern nur "negative Werte bei Fehlern". Obwohl es unglücklich wäre, Benutzer zu brechen, die derzeit Annahmen über bestimmte Errno-Werte treffen, denke ich, dass ich die Dinge ändern würde Es ist ein lohnender Kompromiss, in Zukunft eine starke Errno-Garantie über Kernel-Versionen und ABIs hinweg bereitzustellen.

Nun, wie wir bereits gesagt haben, garantieren wir derzeit nicht wirklich bestimmte Errno-Werte, sondern nur "negative Werte bei Fehlern". Obwohl es unglücklich wäre, Benutzer zu brechen, die derzeit Annahmen über bestimmte Errno-Werte treffen, denke ich, dass ich die Dinge ändern würde Es ist ein lohnender Kompromiss, in Zukunft eine starke Errno-Garantie über Kernel-Versionen und ABIs hinweg bereitzustellen.

Ich stimme zu.

Sobald diese Bewertung abgeschlossen ist und wir die errno-Werte bei Fehlern aktualisieren, würde ich mich wohler fühlen, eine Art Garantie für die zurückgegebenen errno-Werte zu geben.

Die Gruppierungsidee war vielleicht nicht die beste, also fangen wir mit einer Liste an, um das alles im Auge zu behalten _(ich werde das im Laufe der Zeit aktualisieren)_:

  • [x] seccomp_reset
    Gibt derzeit zurück: EINVAL, ENOMEM.

  • [x] seccomp_merge
    Gibt derzeit zurück: EINVAL, EDOM, EEXIST, ENOMEM.

  • [x] seccomp_arch_exist
    Gibt derzeit zurück: EINVAL, EEXIST.

  • [x] seccomp_arch_add
    Gibt derzeit zurück: EINVAL, EEXIST, ENOMEM, EDOM.

  • [x] seccomp_arch_remove
    Gibt derzeit zurück: EINVAL, EEXIST.

  • [x] seccomp_load
    Gibt derzeit zurück: EINVAL, ENOMEM, ESRCH, ECANCELED.

  • [x] seccomp_attr_get
    Gibt derzeit zurück: EINVAL, EEXIST.

  • [x] seccomp_attr_set
    Gibt derzeit zurück: EINVAL, EACCES, EOPNOTSUPP, EEXIST.

  • [x] seccomp_syscall_resolve_name_arch
    Bereits gut definiert, gibt den Syscall-Wert oder __NR_SCMP_ERROR bei einem Fehler zurück.

  • [x] seccomp_syscall_resolve_name_rewrite
    Bereits gut definiert, gibt den Syscall-Wert oder __NR_SCMP_ERROR bei einem Fehler zurück.

  • [x] seccomp_syscall_resolve_name
    Bereits gut definiert, gibt den Syscall-Wert oder __NR_SCMP_ERROR bei einem Fehler zurück.

  • [x] seccomp_syscall_priority
    Gibt derzeit zurück: EINVAL, EDOM, EFAULT, ENOMEM.

  • [x] seccomp_rule_add_array
    Gibt derzeit zurück: EINVAL, EOPNOTSUPP, ENOMEM, EDOM, EFAULT, EEXIST.

  • [x] seccomp_rule_add
    Gibt derzeit zurück: EINVAL, EOPNOTSUPP, ENOMEM, EDOM, EFAULT, EEXIST.

  • [x] seccomp_rule_add_exact_array
    Gibt derzeit zurück: EINVAL, EOPNOTSUPP, ENOMEM, EDOM, EFAULT, EEXIST.

  • [x] seccomp_rule_add_exact
    Gibt derzeit zurück: EINVAL, EOPNOTSUPP, ENOMEM, EDOM, EFAULT, EEXIST.

  • [x] seccomp_notify_alloc
    Gibt derzeit zurück: EOPNOTSUPP, ENOMEM, EFAULT, ECANCELED. Die Manpage gibt bereits -1 on error an, was sich wahrscheinlich nur auf die seccomp()-Errno bezieht.

  • [x] seccomp_notify_receive
    Gibt derzeit zurück: EOPNOTSUPP und ECANCELED. Die Manpage gibt bereits -1 on error an, was sich wahrscheinlich nur auf die seccomp()-Errno bezieht.

  • [x] seccomp_notify_respond
    Gibt derzeit zurück: EOPNOTSUPP und ECANCELED. Die Manpage gibt bereits -1 on error an, was sich wahrscheinlich nur auf die seccomp()-Errno bezieht.

  • [x] seccomp_notify_id_valid
    Gibt derzeit zurück: EOPNOTSUPP und ECANCELED. Die Manpage gibt bereits -ENOENT bei Fehler (ungültige ID) an, was sich wahrscheinlich nur auf die Fehlernummer seccomp() bezieht.

  • [x] seccomp_notify_fd
    Bereits gut definiert, gibt die Benachrichtigung fd zurück.

  • [x] seccomp_export_pfc
    Gibt derzeit zurück: EINVAL und ECANCELED.

  • [x] seccomp_export_bpf
    Gibt derzeit zurück: EINVAL, ENOMEM und ECANCELED.

Jetzt, da wir eine Liste haben, welche Funktionen welche Fehlercodes zurückgeben, fühle ich mich etwas besser, zumal wir bereits ziemlich konsistent sind, wie wir unsere Fehlercodes verwenden. Das letzte bisschen sollte enorm helfen.

Ich werde eine PR starten, damit wir anfangen können, Korrekturen und Feedback zu den Änderungen zu sammeln, ich werde das bald hier posten.

Angesichts der eher "besonderen" Natur von ENOSYS habe ich Vorbehalte gegen die Verwendung der errno als unser Kernel/Libc-Catch-All. Ich muss mir andere Werte ansehen, hat jemand starke Gefühle/Gedanken dazu?

Es fehlt noch viel, hauptsächlich Manpage-Bearbeitungen und Codekommentare (ganz zu schweigen von Tests), aber Sie können sich den folgenden Zweig ansehen, um eine Vorstellung davon zu bekommen, was ich denke:

Angesichts der eher "besonderen" Natur von ENOSYS habe ich Vorbehalte gegen die Verwendung der errno als unser Kernel/Libc-Catch-All. Ich muss mir andere Werte ansehen, hat jemand starke Gefühle/Gedanken dazu?

Was ist mit EIO?

Was ist mit EIO?

Ich weiß nicht, ob das den Fehler viel besser umsetzbar machen würde. Wie wäre es stattdessen, die API um Funktionen zu erweitern, die keine errno-Werte verwenden, sondern zum Beispiel:

  • SCMP_ERROR_UNKNOWN_SYSCALL: der Syscall ist libseccomp nicht bekannt: Aufrufer kann dies verwenden, um Benutzereingaben abzulehnen (z. B. Tippfehler im Syscall-Namen)
  • SCMP_ERROR_SYSCALL_NOT_FOR_THIS_ARCH: Der Systemaufruf ist libseccomp bekannt, aber hier nicht verfügbar: Der Aufrufer kann diesen Fehler nur für diese Architektur ignorieren
  • SCMP_ERROR_API_USAGE: libseccomp erkennt ein Problem mit der Aufruflogik, das in korrekt geschriebenem Code nicht auftreten sollte: Aufrufer könnte assert() auslösen
  • SCMP_ERROR_KERNEL_OTHER: libseccomp war mit der Eingabe und der Reihenfolge der Aufrufe in Ordnung, aber der Kernel gab bestimmte gut definierte Fehler zurück, z. B. ENOMEM, EPERM, ENOSYS: Der Aufrufer sollte die Fehlernummer auf Aktion prüfen. Falls libseccomp bekannt ist, dass der Grund für den Fehler auf einen vom Aufrufer verursachten Fehler zurückzuführen ist (z. B. EFAULT), könnte stattdessen vielleicht SCMP_ERROR_API_USAGE verwendet werden.
  • SCMP_ERROR_KERNEL_API_USAGE: libseccomp war mit der Eingabe usw. in Ordnung, aber der Kernel mochte es aus nicht so klaren und offensichtlichen Gründen nicht. Dies könnte auf eine Kerneländerung, eine deaktivierte Konfiguration, einen Fehler in libseccomp oder Aufrufer, sehr seltsame Benutzereingaben usw. hinweisen. Die Aktion für den Aufrufer könnte darin bestehen, das Ereignis und die Fehlernummer mit der Aufforderung zu protokollieren, die Informationen an die Entwickler (Aufrufer und/oder libseccomp) weiterzuleiten weitere Analyse, nicht behauptbar.

Alternativ könnte die API mit Fehlern unverändert bleiben, aber eine neue Funktion könnte verwendet werden, um den obigen Fehlercode anzufordern.

Vielleicht könnten @poettering oder @keszybz auch kommentieren.

Ich weiß nicht, ob das den Fehler viel besser umsetzbar machen würde. Wie wäre es stattdessen, die API um Funktionen zu erweitern, die keine Errno-Werte verwenden ...

Nun, bevor wir überhaupt in Betracht ziehen könnten, so etwas zu tun (und ich bin mir nicht sicher, ob wir das tun wollen), müssen wir uns auf stabile und unterstützte Rückkehrcodes einigen. Daran arbeiten wir hier und streben v2.5 an.

Lassen Sie uns die stabilen/unterstützten Rückgabecodes in v2.5 abrufen und sehen, wie das geht. Wenn wir etwas Zusätzliches tun müssen, können wir es für v2.6 in Betracht ziehen.

Was ist mit EIO?

Ich wurde wegen anderer Arbeitsprioritäten und einiger Kernel-Sachen für eine Weile davon abgehalten, aber jetzt, wo ich wieder bei libseccomp bin, wird mir klar, dass die Verwendung von EIO hier falsch erscheint. Lassen Sie mich noch etwas darüber nachdenken.

Was ist mit ECANCELED als Catch-All-Kernel-Fehlercode?

Ich bin gespannt, was du denkst @drakenclimber , du warst eine Weile still dazu.

Ich wurde wegen anderer Arbeitsprioritäten und einiger Kernel-Sachen für eine Weile davon abgehalten, aber jetzt, wo ich wieder bei libseccomp bin, wird mir klar, dass die Verwendung von EIO hier falsch erscheint. Lassen Sie mich noch etwas darüber nachdenken.

Hier gilt das gleiche. In letzter Zeit war es etwas hektisch :/.

Ich bin gespannt, was du denkst @drakenclimber , du warst eine Weile still dazu.

Sicher. Ich möchte mir den ganzen Thread noch einmal durchlesen und dann melde ich mich.

Was ist mit ECANCELED als Catch-All-Kernel-Fehlercode?

Ich gebe zu, dass ich mit ECANCELED nicht sehr vertraut war. Ich habe kurz die Kernelquellen nach ihrer Verwendung durchgesehen und auch eine Google-Suche durchgeführt. ECANCELED hat keine Kollisionen mit früheren libseccomp, prctl() oder anderen APIs, die wir verwendet haben, und ich denke, es kann jeden Fehler, den der Kernel auf uns wirft, vernünftig kapseln.

tl;dr - Ich finde ECANCELED als unseren Sammelpunkt für Kernelfehler cool.

Vielen Dank. Ich habe ein unausgegorenes Patchset, das ich fertigstellen und als PR zur Überprüfung einreichen werde.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen

Verwandte Themen

pcmoore picture pcmoore  ·  23Kommentare

drakenclimber picture drakenclimber  ·  10Kommentare

erdumbledore picture erdumbledore  ·  3Kommentare

Xyene picture Xyene  ·  15Kommentare

grubeli picture grubeli  ·  3Kommentare