Frage

Ich las DJB „Einige Gedanken über die Sicherheit nach zehn Jahren Qmail 1.0“ und er notiert diese Funktion für einen Dateideskriptor zu bewegen:

int fd_move(to,from)
int to;
int from;
{
  if (to == from) return 0;
  if (fd_copy(to,from) == -1) return -1;
  close(from);
  return 0;
}

Es fiel mir ein, dass dieser Code nicht den Rückgabewert von knapp überprüft, so dass ich die Manpage der Nähe (2) lesen, und es scheint, es können nicht mit EINTR, wobei in diesem Fall das entsprechende Verhalten zu sein scheint, mit dem gleichen Argumente wieder schließen zu nennen.

Da dieser Code von einer Person mit weit mehr Erfahrung als ich in C und UNIX, geschrieben wurde und zusätzlich stand seit mehr als einem Jahrzehnt unverändert in qmail hat, gehe ich davon aus es muss eine Nuance sein, dass ich fehle, dass dieser Code macht richtig. Kann jemand diese Nuance mir das erklären?

War es hilfreich?

Lösung

Wenn ein Dateideskriptor dup'd wird, wie es in der fd_copy oder dup2 Funktion ist, werden Sie mit mehr als einer Datei am Ende Descriptor auf dasselbe beziehen (das heißt die gleiche struct file im Kernel). Schließen einer von ihnen wird nur der Referenzzähler dekrementieren. Kein Betrieb wird auf dem zugrunde liegende Objekt durchgeführt, sofern es die letzte in der Nähe ist. Als Ergebnis Bedingungen wie EINTR und EIO sind nicht möglich.

Andere Tipps

Ich habe zwei Antworten bekommen:

  1. Er versucht, einen Punkt über Factoring aus gemeinsamem Code zu machen und oft solche Beispiele auslassen Fehler der Kürze und Klarheit zu überprüfen.
  2. in der Nähe (2) kann EINTER zurück, aber tut es in der Praxis, und wenn ja, was würden Sie vernünftigerweise tun? Wiederholen Sie einmal? Wiederholen, bis Erfolg? Was passiert, wenn Sie EIO bekommen? Das könnte fast alles bedeuten, so dass Sie wirklich keinen vernünftigen Gebrauch machen, außer es Protokollierung und Bewegen auf. Wenn Sie nach einer EIO wiederholen, könnte erhalten Sie EBADF, was dann? Es sei angenommen, dass der Deskriptor geschlossen ist und weitermachen?

Jeder Systemaufruf kann EINTR zurückkehren, escpecially man, dass Blöcke lesen sie wie (2) auf einem langsamen menschlichen warten. Dies ist ein wahrscheinlicheres Szenario und ein gutes „erhalten Eingang von Klemme“ Routine wird in der Tat für diese überprüfen. Das bedeutet auch, dass der Schreib (2) kann auch fehlschlagen, wenn eine Protokolldatei zu schreiben. Versuchen Sie den Fehler zu protokollieren, dass der Logger erzeugt oder sollten Sie einfach aufgeben?

Eine andere Möglichkeit besteht darin, dass seine Funktion nur in einer Anwendung verwendet wird (oder ein Teil von einem), das etwas getan hat, um sicherzustellen, dass der Anruf nicht durch ein Signal unterbrochen werden. Wenn Sie nichts Wichtiges mit Signalen tun werden, dann müssen Sie nicht auf sie reagieren, und es könnte sinnvoll sein, sie alle zu maskieren, anstatt wickelt jeden einzelnen Sperrsystemaufruf in einem EINTR erneut versuchen kann. Außer natürlich denjenigen, die Sie töten, so SIGKILL und häufig SIGPIPE, wenn Sie es handhaben, indem verlassen, zusammen mit SIGSEGV und ähnlich fatalen Fehlern, die in jedem Fall nie zu einem richtigen User-Space-App geliefert werden.

Wie auch immer, wenn alles, was er redet Sicherheit ist, dann möglicherweise ist er nicht zu wiederholen close hat. Wenn die Nähe mit EIO ausgefallen ist, dann wäre er nicht in der Lage sein, es zu wiederholen, wäre es ein permanenter Fehler sein. Daher ist es nicht notwendig, für die Richtigkeit seines Programms, dass close erfolgreich ist. Es kann gut sein, dass es nicht notwendig, für die Richtigkeit seines Programms ist, dass close auf EINTR erneut versucht werden, auch nicht.

In der Regel wollen Sie Ihr Programm einen besten Anstrengungen unternehmen, um erfolgreich zu sein, und das bedeutet Retrying auf EINTR. Aber das ist eine separate Sorge von Sicherheit. Wenn sich Ihr Programm so konzipiert, dass eine bestimmte Funktion aus irgendeinem Grund versagt kein Sicherheitslücke ist, dann insbesondere die Tatsache, dass es passiert haben EINTR gescheitert, anstatt für einen dauerhaften Grund ist nicht ein Fehler. DJB ist bekannt, ziemlich eigenwillig sein, so würde ich nicht überrascht sein, wenn er aus irgendeinem Grunde bewiesen hat, warum er nicht Notwendigkeit zu wiederholen, und deshalb nicht stört, auch wenn dies erlauben würde, sein Programm in Spülen den Griffes in bestimmten Situationen erfolgreich zu sein, wo vielleicht noch (wie wird explizit ein harmloses Signal mit kill durch den Benutzer in einem entscheidenden Moment gesendet) es funktioniert nicht.

Edit: es fällt mir ein, dass möglicherweise auf EINTR Retrying könnte sich eine Sicherheitslücke unter bestimmten Bedingungen sein. Eingeführt wird ein neues Verhalten zu diesem Abschnitt des Codes: es Schleife auf unbestimmte Zeit in Reaktion auf ein Signal Flut kann, wo vorher wäre es einen Versuch, close zu machen und dann zurück. Ich weiß es nicht sicher, dass dies qmail Problemen führen würde (immerhin close selbst keine Garantie macht, wie schnell es zurück). Aber wenn nach einem Versuch Aufgeben den Code einfacher macht dann zu analysieren, könnte es plausibel ein kluger Schachzug sein. Oder auch nicht.

Man könnte denken, dass Neue Versuch verhindert, dass eine DoS-Schwachstelle, wo ein Signal, das einen unechten Fehler verursacht. Aber retrying ermöglicht eine andere (schwieriger) DoS-Schwachstelle, wo ein Signal Flut einen unbestimmten Stall verursacht. Im Hinblick auf dem binären „kann diese App dosierbar?“, Die die Art der absoluten Sicherheitsfrage ist DJB in interessiert war, als er schrieb qmail und djbdns, macht es keinen Unterschied. Wenn etwas einmal passieren kann, dann in der Regel, dass Mittel kann es oft passieren.

Nur gebrochen Unices jemals EINTR zurückzukehren, ohne sie explizit zu fragen. Die sane Semantik für signal() ermöglichen anlauffähigen Systemaufrufe ( „BSD style“). Wenn ein Programm auf einem System mit der sysv Semantik Aufbau (unterbrechen Signale), sollen Sie immer Anrufe signal() ersetzen mit Anrufen zu bsd_signal(), die Sie selbst in Bezug auf sigaction() definieren können, wenn sie nicht existieren.

Es ist ferner zu beachten, dass keine Systeme EINTR auf Signalempfang zurück, wenn Sie Signal-Handler installiert haben. Wenn die Standardaktion wird belassen, oder wenn das Signal auf keine Aktion gesetzt wird, ist es unmöglich für Systemaufrufe unterbrochen werden.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top