4 Stimmen

zwei aufeinanderfolgende Seiten einfügen

Ich schreibe einen Unit-Test für meine UTF8-Manipulationsbibliothek, und ich möchte, dass mein Test zu segfault, wenn eine Funktion in einen Pufferüberlauf geht. So kam ich auf die Idee, zwei Seiten nebeneinander im Speicher abzubilden, die erste mit PROT_READ | PROT_WRITE, und die zweite mit PROT_NONE. Auf diese Weise ist im Falle eines Überlaufs ein Segfault garantiert. Hier ist ein Beispiel:

void *addr1, *addr2; /* these are the pages; mmap call left out for simplicity */
char *p = (char *) (addr1 + getpagesize() - 8);

utf8_encode(aUtf8String, p, 8); // this shouldn't segfault

Das Problem ist, wenn ich die zweite Seite zuordne, stürzt mein Programm ab. Hier ist ein Beispielprogramm, das das Problem reproduziert (GNU/Linux):

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/mman.h>

void checkMap(void *p) 
{ 
    if(p == MAP_FAILED) {
        printf("error running mmap: %s\n", strerror(errno));
        exit(1);
    }   
}

int main(void)
{
    void *addr1, *addr2;
    size_t pagesize;

    pagesize = getpagesize();
    checkMap(addr1 = mmap(NULL, pagesize, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0));
    checkMap(addr2 = mmap(addr1 + pagesize, pagesize, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0));  /* segfaults */
    munmap(addr1, pagesize);
    munmap(addr2, pagesize);

    return 0;
}

Interessanterweise führt eine printf()-Anweisung vor der ersten mmap() dazu, dass das Programm erfolgreich läuft. Weiß jemand, warum mmap segfaulting ist? Wenn mein Ziel mit mmap() nicht zu erreichen ist, hat jemand einen anderen Rat, wie ich meinen Code auf Pufferüberlauf testen könnte?

0 Stimmen

Ihr Code funktioniert bei mir perfekt. Das einzige Problem ist, dass Sie die Datei unistd.h für getpagesize() nicht einbinden. Der Compiler wird den Typ erraten. Das könnte auf 64bit-Systemen zu Problemen führen, wenn er einen 32bit int vermutet.

0 Stimmen

Ich habe es auch getestet, und ich bekomme das Segfaulting, aber nicht innerhalb von main(). Vielmehr stürzt es im Glibc-Code ab, beim Beenden ... Wirklich seltsam, das hier. Ich habe auf einer sehr einfachen 32-Bit-Maschine getestet.

0 Stimmen

Nur aus Neugierde, mit welcher Kernel-Version/Distro läuft es bei Ihnen? Ich habe es gerade auf einer 64-Bit-Version ausprobiert (nach #including unistd.h), und jetzt schlägt es auf munmap fehl.

3voto

Commodore Jaeger Punkte 30016

Sie können anrufen mprotect() um die Schutzkennzeichen für den Speicher zu ändern, der durch mmap() . Dies könnte eine bessere Lösung sein, als zu versuchen mmap() zwei nebeneinander liegende Seiten mit unterschiedlichen Schutzmechanismen, da dies die Ursache Ihrer Probleme zu sein scheint.

(Linux erlaubt Ihnen den Aufruf von mprotect() en tout Seite, aber POSIX erlaubt nur Seiten, die bereits von mmap() .)

Dies ist einer der Tricks Elektrischer Zaun zum Abfangen von Pufferüberläufen verwendet.

1 Stimmen

Beachten Sie, dass es auch möglich wäre mmap zunächst 2 Seiten, dann MAP_FIXED die neue PROT_NONE Seite über der zweiten Seite. Nicht zulässig ist es, einfach eine Adresse neben einer bestehenden Seite zu wählen und sie mit MAP_FIXED ; Sie können nicht wissen, ob es nicht bereits mit einem anderen Mapping belegt ist (z.B. ein Teil von libc).

0voto

falstro Punkte 32879

Dies ist nicht ganz im Zusammenhang mit Ihrer Frage (es sei denn, Ihr ultimatives Ziel ist in der Tat, um Ihren Test arbeiten), aber, IMHO, verlassen auf diese Art von Fehlerbehandlung könnte irreführend sein (vor allem, wenn Sie mehrere Plattformen zielen, wo der Test sollte fehlgeschlagen, nur dass der Seg-Fehler nicht ausgelöst wurde, aufgrund einiger nicht spezifizierten Verhalten).

Vielleicht wäre ein anderer Ansatz für Sie sinnvoller, z. B. einfach einen größeren Puffer als nötig zuzuweisen, eine Markierung am Ende des Puffers zu setzen und zu prüfen, ob er überschrieben wurde?

Wie bereits von anderen erwähnt, sind Electric Fence, Valgrind oder andere Tools in ihrer Analyse wahrscheinlich ausgefeilter, wenn Sie bereit sind, eine kompliziertere Testumgebung einzurichten.

0voto

ivaigult Punkte 5188

Als diese Antwort schlägt vor:

  • Zunächst müssen Sie die gesamte Region abbilden, ohne dabei MAP_FIXED und ist daher reserviert.
  • Und dann die zweite Seite noch einmal neu zuordnen (vielleicht mit anderen Flaggen).

Deshalb ist die Verdoppelung der Zuordnung len im ersten mmap Aufruf behebt das Problem:

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>

void checkMap(void *p) 
{ 
    if(p == MAP_FAILED) {
        printf("error running mmap: %s\n", strerror(errno));
        exit(1);
    }   
}

int main(void)
{
    void *addr1, *addr2;
    size_t pagesize;

    pagesize = getpagesize();
    checkMap(addr1 = mmap(NULL, pagesize * 2, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0));
    checkMap(addr2 = mmap(addr1 + pagesize, pagesize, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0));  /* works okay */
    munmap(addr1, pagesize);
    munmap(addr2, pagesize);

    return 0;
}

demo%3B%0A++++++++exit(1)%3B%0A++++%7D+++%0A%7D%0A%0Aint+main(void)%0A%7B%0A++++void+addr1,+addr2%3B%0A++++size_t+pagesize%3B%0A%0A++++pagesize+%3D+getpagesize()%3B%0A++++checkMap(addr1+%3D+mmap(NULL,+pagesize++2,+PROT_READ+%7C+PROT_WRITE,+MAP_ANONYMOUS+%7C+MAP_PRIVATE,+-1,+0))%3B%0A++++checkMap(addr2+%3D+mmap(addr1+%2B+pagesize,+pagesize,+PROT_READ+%7C+PROT_WRITE,+MAP_ANONYMOUS+%7C+MAP_PRIVATE+%7C+MAP_FIXED,+-1,+0))%3B++/+works+okay+*/%0A++++munmap(addr1,+pagesize)%3B%0A++++munmap(addr2,+pagesize)%3B%0A%0A++++return+0%3B%0A%7D%27),l:%275%27,n:%270%27,o:%27C+source+%231%27,t:%270%27)),header:(),k:54.7780678851175,l:%274%27,n:%270%27,o:%27%27,s:0,t:%270%27),(g:!((h:executor,i:(argsPanelShown:%271%27,compilationPanelShown:%270%27,compiler:cclang1500,compilerOutShown:%270%27,execArgs:%27%27,execStdin:%27%27,fontScale:14,fontUsePx:%270%27,j:1,lang:___c,libs:!(),options:%27%27,source:1,stdinPanelShown:%271%27,tree:%271%27,wrap:%271%27),l:%275%27,n:%270%27,o:%27Executor+x86-64+clang+15.0.0+(C,+Editor+%231)%27,t:%270%27)),header:(),k:45.2219321148825,l:%274%27,n:%270%27,o:%27%27,s:0,t:%270%27)),l:%272%27,n:%270%27,o:%27%27,t:%270%27)),version:4)

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X