2017-09-08 1 views
2

In Nicholas Ormrod's talk auf der CppCon 2016 erwähnte er einen heimtückischen Bug bei Facebook, wo ein einzelnes Byte von einer nicht initialisierten (ungeschriebenen) Seite zweimal gelesen wurde, so dass es Fälle gab Der zweite Lesevorgang hat einen (von Null verschiedenen) Wert zurückgegeben, der sich vom ersten Lesewert (Null) unterscheidet.Das Lesen von nicht initialisiertem Speicher gibt jedesmal unterschiedliche Antworten zurück

Er erwähnte, dass sie jemalloc und I also presume sie unter Linux ausgeführt wurden. jemalloc's manpage sagt, dass es immer mmap() über sbrk() bevorzugt.

Nun jemalloc's only mmap() call verwendet die Fahnen MAP_PRIVATE | MAP_ANONYMOUS mit der gelegentlichen Aufnahme von MAP_FIXED, und insbesondere ist es nicht MAP_UNINITIALIZED verwenden. Dies bedeutet, dass die Seiten always zero-initialized zugewiesen sind.

Darüber hinaus wird auch madvise() with MADV_DONTNEED, für anonyme Zuordnungen, "zero-fill-on-demand pages" für anonyme Zuordnungen zurückgeben, die ich als "zero-initialisierte Seiten."

Meine Frage ist: Wie ist es möglich, dass die zweite Lesung jemals einen Wert ungleich Null zurückgeben würde, was ihren Fehler verursacht?

+0

Um das Video zu paraphrasieren: der Fehler Nicholas beschreibt passiert, wenn eine Seite bedingt zum Kernel zurückgegeben wird. Wenn Sie also das Byte von der Seite lesen, sagt der Kernel, es ist nicht initialisiert Speicher, so dass es 0 zurückgeben kann. Im zweiten Lesevorgang schreiben Sie jedoch * anstatt * zu lesen, damit der Kernel erkennt, dass Sie tatsächlich den Speicher benötigen und die tatsächlich dort gespeicherten Daten abrufen. – Justin

+0

Diese Frage bietet nicht genügend Kontext, um die Frage beantworten zu können. Es fehlt ein [mcve]. Um diese Frage zu beantworten, müssten die Beantworter das Video – Justin

+0

@Justin ansehen, das ich mit dem Video für zusätzliche Informationen verlinkt habe, aber ich habe hier alle Details des Fehlers erwähnt. –

Antwort

1

Welche Erklärung auch immer diese Jungs zur Verfügung stellen, ist komplett gebrochen (zumindest im gegebenen Kontext). Und der Code hat undefiniertes Verhalten, egal was passiert.

Wenn data auf den Chunk zeigt, der mit mindestens size() + 1 Größe zugewiesen wurde, hat der Code aufgrund der Race Condition ein undefiniertes Verhalten (er erwähnt die Verwendung von Threads vorher).

Wenn die Größe der data kleiner ist als diese (z. B. gleich size()), hat der Code ein undefiniertes Verhalten aufgrund von Zugriff außerhalb der Grenzen (und die Race Condition wird zu einem strittigen Punkt).

+0

Es hört sich an, als würden sie alle Arten von Abkürzungen bei FB machen, aber da Kerle wie Andrei Alexandrescu (CPP Committee) an diesem Code arbeiten, gebe ich ihnen die Vorteil des Zweifels, dass sie wissen, was sie tun, obwohl sie UB ausgelöst haben (und er berührt das auch früher im Gespräch). Also versuche ich, seine Erklärung zu verstehen und nicht aus dem Fenster zu werfen. –

+0

Auch das Lesen von nicht initialisiertem Speicher ist bereits undefiniertes Verhalten an sich. –

+0

@YamMarcovic das ist richtig, aber ich weiß nicht, ob der Speicher nicht initialisiert ist, da ich nicht weiß, wie der Speicher an erster Stelle initialisiert wurde. Ich habe das Video nur von dem Punkt in Ihrem Link aus gesehen.Eine vernünftige Annahme wäre jedoch, dass dies nicht der Fall ist, also ist dies der dritte undef-Verhaltenspunkt. – SergeyA

Verwandte Themen