Beobachten des Abrufens veralteter Anweisungen auf x86 mit selbstmodifizierendem Code

Lesezeit: 6 Minuten

Benutzer-Avatar
Chris

Mir wurde gesagt und ich habe aus den Handbüchern von Intel gelesen, dass es möglich ist, Anweisungen in den Speicher zu schreiben, aber die Befehls-Prefetch-Warteschlange hat bereits die veralteten Anweisungen abgerufen und wird diese alten Anweisungen ausführen. Es ist mir nicht gelungen, dieses Verhalten zu beobachten. Meine Methodik ist wie folgt.

Das Intel Software-Entwicklungshandbuch besagt ab Abschnitt 11.6, dass

Ein Schreibvorgang in eine Speicherstelle in einem Codesegment, das derzeit im Prozessor zwischengespeichert ist, bewirkt, dass die zugehörige Cache-Zeile (oder -Zeilen) ungültig gemacht werden. Diese Prüfung basiert auf der physikalischen Adresse des Befehls. Darüber hinaus prüfen die P6-Familie und Pentium-Prozessoren, ob ein Schreibvorgang in ein Codesegment einen Befehl modifizieren kann, der zur Ausführung vorab abgerufen wurde. Wenn der Schreibvorgang einen vorab abgerufenen Befehl betrifft, wird die Vorabruf-Warteschlange ungültig gemacht. Diese letztere Prüfung basiert auf der linearen Adresse des Befehls.

Es sieht also so aus, als ob ich, wenn ich veraltete Anweisungen ausführen möchte, zwei verschiedene lineare Adressen haben muss, die auf dieselbe physische Seite verweisen. Also speichere ich eine Datei auf zwei verschiedene Adressen.

int fd = open("code_area", O_RDWR | O_CREAT, S_IRWXU | S_IRWXG | S_IRWXO);
assert(fd>=0);
write(fd, zeros, 0x1000);
uint8_t *a1 = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC,
        MAP_FILE | MAP_SHARED, fd, 0);
uint8_t *a2 = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC,
        MAP_FILE | MAP_SHARED, fd, 0);
assert(a1 != a2);

Ich habe eine Assembly-Funktion, die ein einziges Argument akzeptiert, einen Zeiger auf die Anweisung, die ich ändern möchte.

fun:
    push %rbp
    mov %rsp, %rbp

    xorq %rax, %rax # Return value 0

# A far jump simulated with a far return
# Push the current code segment %cs, then the address we want to far jump to

    xorq %rsi, %rsi
    mov %cs, %rsi
    pushq %rsi
    leaq copy(%rip), %r15
    pushq %r15
    lretq

copy:
# Overwrite the two nops below with `inc %eax'. We will notice the change if the
# return value is 1, not zero. The passed in pointer at %rdi points to the same physical
# memory location of fun_ins, but the linear addresses will be different.
    movw $0xc0ff, (%rdi)

fun_ins:
    nop   # Two NOPs gives enough space for the inc %eax (opcode FF C0)
    nop
    pop %rbp
    ret
fun_end:
    nop

In C kopiere ich den Code in die speicherabgebildete Datei. Ich rufe die Funktion von der linearen Adresse auf a1aber ich übergebe einen Zeiger auf a2 als Ziel der Codeänderung.

#define DIFF(a, b) ((long)(b) - (long)(a))
long sz = DIFF(fun, fun_end);
memcpy(a1, fun, sz);
void *tochange = DIFF(fun, fun_ins);
int val = ((int (*)(void*))a1)(tochange);

Wenn die CPU den modifizierten Code aufgenommen hat, gilt val==1. Andernfalls, wenn die veralteten Anweisungen ausgeführt wurden (zwei nops), val==0.

Ich habe dies auf einem 1,7 GHz Intel Core i5 (2011 MacBook Air) und einer Intel(R) Xeon(R) CPU X3460 @ 2,80 GHz ausgeführt. Jedes Mal sehe ich jedoch val==1, was darauf hinweist, dass die CPU die neue Anweisung immer bemerkt.

Hat jemand Erfahrung mit dem Verhalten, das ich beobachten möchte? Ist meine Überlegung richtig? Ich bin ein wenig verwirrt darüber, dass das Handbuch P6- und Pentium-Prozessoren erwähnt und dass mein Core i5-Prozessor nicht erwähnt wird. Vielleicht passiert etwas anderes, was dazu führt, dass die CPU ihre Anweisungs-Prefetch-Warteschlange löscht? Jeder Einblick wäre sehr hilfreich!

  • Welches Handbuch haben Sie verwendet (überprüfen Sie die “Bestellnummer” auf der ersten Seite und tragen Sie es hier ein)?

    – osgx

    30. Juni 2013 um 23:26 Uhr

  • Lesen Sie auch den Abschnitt „8.1.3 Handling Self- and Cross-Modifying Code“ der Bedienungsanleitung – download.intel.com/products/processor/manual/325462.pdf

    – osgx

    30. Juni 2013 um 23:57 Uhr

  • Hmm, versuchen Sie, PROT_EXEC von a2 zu deaktivieren … Dies kann einige Intel Atoms betreffen

    – osgx

    1. Juli 2013 um 1:25 Uhr

  • +1 für das Werfen von “Air Powerbook” in eine so präzise Diskussion über Intel-Minutien 🙂

    – Kartoffelklatsche

    30. Juni 2013 um 23:22 Uhr

  • Wenn Sie weiter schauen, werden Sie Patente im Zusammenhang mit SMC sehen, bei denen ich ein Erfinder bin. AFAIK Ich habe die I$- und ITLB-Inklusionsmechanismen von P6 erfunden, um “Anweisungen im Flug” zu erschnüffeln. // Ich halte das für Fehler. Ich denke, dass es einfacher gewesen wäre, ein vollständig assoziatives CAM mit den Anweisungsblöcken aller Anweisungen in der Pipeline zu erstellen, physisch. Mit einem Bloomfilter, wenn Sie Strom sparen möchten. // Ich denke, es waren Fehler (a), weil sie kompliziert und schwer zu korrigieren waren, obwohl sie viele Tore gespart haben, und (b) glasige Backen in der Leistung.

    – Krazy Glew

    22. August 2013 um 19:00 Uhr


  • Was wäre, wenn der von Ihnen geänderte Code immer, sagen wir, 1 kByte vor der aktuellen Ausführungsadresse wäre? Würde das das Problem vermeiden?

    – zehn Mal

    15. Dezember 2013 um 9:44 Uhr

  • Ein weiteres interessantes Beispiel für CPUs, die über die im x86-ISA-Handbuch angegebenen Anforderungen hinausgehen: Kohärente Pagewalks für TLB-Einträge, die nur spekulativ geladen werden konnten, um Win9x nicht zu beschädigen. AMD hat die Kohärenz mit Bulldozer aufgegeben, da sie wohl entschieden haben, dass Win9x und andere davon abhängige Software nicht mehr relevant sind.

    – Peter Cordes

    22. Oktober 2016 um 0:57 Uhr


  • Ich wollte den Kommentar von @PeterCordes über das “kohärente” Durchlaufen der Seitentabelle für TLB-Fehler erläutern, aber ich werde schnell sein: (1) Der Hauptgrund, warum Intel mit der Ausführung der Seitentabelle begonnen hat, durchläuft den Cache, anstatt den Cache zu umgehen , war Leistung. Vor P6 waren Page Table Walks langsam, profitierten nicht vom Cache und waren nicht spekulativ. Langsam genug, dass das TLB-Miss-Handling der Software ein Leistungsgewinn war. P6 beschleunigt TLB-Fehler, indem sie spekulativ ausgeführt werden, den Cache verwenden und auch Zwischenknoten wie Seitenverzeichniseinträge zwischenspeichern.

    – Krazy Glew

    7. November 2016 um 19:39 Uhr

  • (2a’) Einer der peinlichsten Fehler betraf Add-with-Carry in den Speicher. In frühem Mikrocode. Das Laden würde gehen, das Übertrags-Flag würde aktualisiert werden und der Speicher könnte fehlerhaft sein – aber das Übertrags-Flag war bereits aktualisiert worden, so dass der Befehl nicht neu gestartet werden konnte. // Es war ein einfacher Mikrocode-Fix, der den Speicher vor dem Schreiben des Carry-Flags durchführte – aber ein zusätzliches uop reichte aus, damit diese Anweisung nicht in das Ucode-System mit “mittlerer Geschwindigkeit” passte.

    – Krazy Glew

    7. November 2016 um 20:05 Uhr


  • Danke, Andi. Das ist eine großartige Geschichte! Ich habe das Gefühl, dass es irgendwo in eine Antwort gehört, entweder diese als Riese beiseite oder vielleicht eine selbst beantwortete Frage und Antwort, wenn uns eine gute “Frage” einfällt, die dies als Antwort hat: P Heilige Scheiße, also das ist Woher kommt diese zusätzliche ALU-uop im Speicherziel-ADC, sogar auf der Core2- und SnB-Familie? Hätte nie gedacht, war aber darüber verwirrt.

    – Peter Cordes

    7. November 2016 um 20:46 Uhr


  • Ähnliches gilt für selbstmodifizierenden Code: Wir wollten nicht so sehr, dass selbstmodifizierender Code schnell läuft, als dass wir versuchten, die Legacy-Mechanismen für selbstmodifizierenden Code – Leeren der Pipe für Serialisierungsbefehle wie CPUID – langsamer als nur zu machen Schnüffeln des Icaches und der Pipeline. Aber auch dies gilt für eine High-End-Maschine: Auf einer Low-End-Maschine sind die Legacy-Mechanismen schnell genug und billig.

    – Krazy Glew

    8. November 2016 um 5:11 Uhr

1371330cookie-checkBeobachten des Abrufens veralteter Anweisungen auf x86 mit selbstmodifizierendem Code

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

Privacy policy