Was bedeutet das?: *(int32 *) 0 = 0;

Lesezeit: 7 Minuten

Benutzeravatar von NlightNFotis
NightNFotis

Was tut im folgenden Codeabschnitt *(int32 *) 0 = 0; bedeuten?

void
function (void)
{
  ...

  for (;;)
     *(int32 *) 0 = 0;     /* What does this line do? */
}

Ein paar Anmerkungen:

  • Der Code scheint nicht erreichbar zu sein, da vor diesem bestimmten Codeabschnitt eine Exit-Anweisung steht.
  • int32 ist typedef‘ed, aber Sie sollten sich nicht zu sehr darum kümmern.
  • Dieses Stück Code stammt aus der Laufzeit einer Sprache in einem Compiler, für alle Interessierten.

  • Es ist eine Endlosschleife, die 4 Bytes Null an den Stellen 0-3 speichert. Es sieht aus wie ein absichtlicher Abbruch oder absichtliches Aufhängen.

    – Mike Dunlavey

    23. August 2013 um 16:16 Uhr


  • @Habib ja das weiß ich natürlich for (;;) ist eine Endlosschleife. Das Stück darunter ist das, was ich nicht verstehe. Sorry für die Zweideutigkeit.

    – NightNFotis

    23. August 2013 um 16:17 Uhr


  • @Curtis Ich denke nicht, dass es eine schlechte Sache ist. Dies wurde von erfahrenen Compiler-Ingenieuren geschrieben und muss aus einem bestimmten Grund vorhanden sein. Eine anormale Ausführung für den Fall, dass etwas schief geht, scheint angesichts der bisherigen Antworten ein plausibler Grund zu sein.

    – NightNFotis

    23. August 2013 um 16:22 Uhr

  • Es bedeutet undefiniertes Verhalten.

    – David G

    23. August 2013 um 19:30 Uhr

Benutzeravatar von Scotty Bauer
Scott Bauer

Der Code macht folgendes:

   for (;;) // while(true)
     *(int32 *) 0 = 0; // Treat 0 as an address, de-reference the 0 address and try and store 0 into it.

Dies sollte segfault, Nullzeiger dereferenzieren.

BEARBEITEN

Kompiliert und ausgeführt für weitere Informationen:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

int main(void){
  *(int32_t *) 0 = 0;
  printf("done\n");
  return 0;
}

gcc -g null.c; ./a.out

Program received signal SIGSEGV, Segmentation fault.
0x00000000004004cd in main () at null.c:7
7         *(int32_t *) 0 = 0;

  • @Hrishi: Auf jedem vernünftigen System (außer vielleicht einigen sehr kleinen eingebetteten Plattformen) betrifft der Fehler nur dieses eine Programm.

    – Keith Thompson

    23. August 2013 um 17:24 Uhr

  • @KeithThompson Ich dachte, das Betriebssystem würde eine Zeigertabelle zu seinen Routinen in den unteren Adressen im Speicher speichern? gilt das nicht mehr in modernen Betriebssystemen?

    – Hrishi

    23. August 2013 um 17:38 Uhr

  • @Hrishi: Ich habe keine Ahnung – aber auf jedem modernen Multi-Processing-Betriebssystem (Unix, Linux, Windows, …) kann ein nicht privilegiertes Programm keine Datenstrukturen auf Betriebssystemebene beschädigen (es sei denn, es gibt einen Fehler im Betriebssystem, aber so etwas es ist unwahrscheinlich, dass ein solcher trivialer Code einen Fehler auslöst). Normalerweise hat jeder Prozess seinen eigenen virtuellen Adressraum; Adresse 0 für Ihren Prozess, falls vorhanden, hat nichts mit Adresse 0 für den Kernel oder für irgendeinen anderen Prozess zu tun.

    – Keith Thompson

    23. August 2013 um 17:40 Uhr


  • @Hrishi: Aufgrund der Funktionsweise von Bootloadern wird das Betriebssystem normalerweise in den Adressraum des physischen Speichers „niedrig“ geladen. Es gab auch Fälle, in denen wenig physischer Speicher für DRM wertvoll war. Die meisten Betriebssysteme werden jedoch innerhalb des virtuellen Speichers ausgeführt, und auf x86 belegt es ein höheres Viertel (32 Bit) oder eine höhere Hälfte (64 Bit). Physischer Raum und virtueller Raum sind ohnehin nur lose miteinander verbunden. Dies ist möglicherweise bei einigen eingebetteten Plattformen nicht der Fall, die manchmal keinen virtuellen Speicher (und manchmal sogar einen standardkonformen C-Compiler) haben.

    – Maciej Piechotka

    23. August 2013 um 21:11 Uhr

  • @Hrishi Jedes moderne PC-Betriebssystem (und Hardware) verwendet Speicherschutz, um zu verhindern, dass Programme außerhalb ihres eigenen Adressraums oder in die internen Strukturen des Betriebssystems schreiben. Ich denke, die letzten bedeutenden Betriebssysteme, denen diese Funktion fehlte, waren MacOS 9 und Windows 95. Sogar die winzigen Embedded-Echtzeit-Betriebssysteme QNX und VxWorks haben Speicherschutz.

    – Absturz

    23. August 2013 um 23:42 Uhr


Benutzeravatar von Eric Postpischil
Eric Postpischil

Da das OP angibt, dass der Code von erfahrenen Compiler-Ingenieuren geschrieben wurde, ist dies möglicherweise die Absicht des Codes:

  • *(int32 *) 0 = 0; wird von dieser speziellen C-Implementierung als Code erkannt, der Verhalten verursacht, das nicht durch den C-Standard definiert ist und dieser Implementierung als illegal bekannt ist.
  • Das for (;;) gibt zusätzlich an, dass dieser Code niemals verlassen wird.
  • Die Compiler-Ingenieure wissen, dass der Optimierer diesen Code erkennt und schlussfolgert, dass er „wegoptimiert“ werden kann, da jedes Programm, das diesen Code erreicht, ein beliebiges Verhalten haben darf, sodass der Optimierer sich dafür entscheiden kann, ihm das Verhalten zu geben, als wäre es der Code wird nie erreicht.1

Diese Art der Begründung ist möglich nur wenn Sie spezifische Kenntnisse über die interne Funktionsweise einer C-Implementierung haben. Das ist so etwas wie ein Compiler-Ingenieur könnte in spezielle Header für eine C-Implementierung aufnehmen, vielleicht um diesen bestimmten Code zu markieren (z. B. Code nach einer abort Anruf) wird nie erreicht. Es sollte niemals in der normalen Programmierung verwendet werden.


1 Betrachten Sie beispielsweise diesen Code:

if (a)
    for (;;)
        *(int 32 *) 0 = 0;
else
    foo();

Der Compiler kann erkennen, dass die then-Klausel jedes Verhalten haben darf. Daher kann der Compiler frei wählen, welches Verhalten er hat. Der Einfachheit halber wählt es das gleiche Verhalten wie foo();. Dann wird der Code zu:

if (a)
    foo();
else
    foo();

und kann weiter vereinfacht werden zu:

foo();

Benutzeravatar von evilruff
Bösewicht

Tatsächlich erklärt dieser Code-Seg-Faulting nicht, warum er existiert =)

Ich denke, das stammt von der Laufzeit einer MCU … und der Grund dafür ist, dass diese Anweisung, wenn die Programmausführung an diesen Punkt gelangt, entweder einen Software-Reset für eine MCU initiiert, sodass das Programm neu gestartet wird (was in der eingebetteten Entwicklung üblich ist) ODER Wenn die MCU mit Hardware-Watchdog konfiguriert ist, erzwinge einen MCU-Neustart wegen Hardware-Watchdog und Endlosschleife.

Das Hauptziel solcher Konstruktionen besteht darin, einen Interrupt aufzurufen, der entweder vom Betriebssystem oder von der Hardware behandelt werden kann, um bestimmte Aktionen einzuleiten.

Zu wissen, dass es x86 ist, hängt von einem CPU-Modus ab … im Real-Modus passiert nichts sofort, wenn es keinen Watchdog gibt, bei Adresse 0 gibt es eine Adresse von ‘Divide by 0’ Handler, also wenn es ein alter MS- Die DOS- oder eingebettete x86-Laufzeitumgebung ändert eine Adresse des Handlers „Divide by 0“ auf 0. Sobald dies geschieht und dieser Interrupt nicht maskiert ist, springt die CPU zur Position 0: 0 und wird wahrscheinlich aufgrund einer illegalen Anweisung neu gestartet .. wenn es sich um geschützten oder VM-x86-Code handelt, ist dies eine Möglichkeit, das Betriebssystem oder einen anderen Supervisor darüber zu informieren, dass ein Problem in der Laufzeit vorliegt und die Software extern “beendet” werden sollte.

  • Danke für die Erklärung Alter! Dies ist jedoch auf x86 ausgerichteter Code und scheint nie ausgeführt zu werden. Da ist ein exit(0) vor diesem bestimmten Stück Code, und ich glaube, es ist da, um abzubrechen, falls etwas nicht stimmt exit selbst.

    – NightNFotis

    23. August 2013 um 17:04 Uhr

Sams Benutzeravatar
Sam

for(;;) ist äquivalent zu while(1),

*(int32 *) 0 = 0;schreibt 0 in einen dereferenzierten Null-Zeiger, was einen Absturz verursachen soll, aber bei bestimmten Compilern nicht immer der Fall ist: Threads mit *(int*)NULL = 1 abstürzen lassen; problematisch?

Es ist eine Endlosschleife mit undefiniertem Verhalten (Dereferenzieren eines Nullzeigers). Es ist wahrscheinlich, dass es mit einem Segfault auf * n * x oder einer Zugriffsverletzung unter Windows abstürzt.

Benutzeravatar von Klash
Klash

Mikes Kommentar ist ziemlich richtig: Er speichert den WERT Null an der ADRESSE 0.

Was auf den meisten Maschinen ein Absturz sein wird.

Benutzer-Avatar des Benutzers
Benutzer

Das Original IBM PC speicherte die Interrupt-Vektortabelle in den untersten 1 KiB des Speichers. Daher würde das tatsächliche Schreiben eines 32-Bit-Werts an die Adresse 0 auf einer solchen Architektur die Adresse für überschreiben INT 00h. INT 00h sieht im PC unbenutzt aus.

Auf im Grunde allem, was modern ist (was bedeutet, dass im x86/x86-64-Parlace alles läuft geschützt oder lang -Modus), wird es einen Segmentierungsfehler auslösen, es sei denn, Sie befinden sich in Ring 0 (Kernel-Modus), da Sie den zulässigen Adress-Dereferenzierungsbereich Ihres Prozesses verlassen.

Da die Dereferenzierung ein undefiniertes Verhalten ist (wie bereits erwähnt), ist ein Segmentierungsfehler eine durchaus akzeptable Möglichkeit, mit dieser Situation umzugehen. Wenn Sie wissen, dass auf der Zielarchitektur eine Dereferenzierung von Nulladressen einen Segmentierungsfehler verursacht, scheint dies ein ziemlich sicherer Weg zu sein, die Anwendung zum Absturz zu bringen. Wenn exit() zurückgibt, ist das wahrscheinlich das, was Sie tun möchten, da gerade etwas schrecklich schief gelaufen ist. Dass der Code aus der Laufzeit eines bestimmten Compilers stammt, bedeutet, dass jeder, der ihn geschrieben hat, das Wissen über die interne Funktionsweise des Compilers und der Laufzeit nutzen und ihn an das Verhalten der spezifischen Zielarchitektur anpassen kann.

1392090cookie-checkWas bedeutet das?: *(int32 *) 0 = 0;

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy