Prüfen, ob einem Zeiger Speicher zugeordnet ist oder nicht

Lesezeit: 10 Minuten

Benutzeravatar von encodingfreak
Programmierfreak

Können wir in C überprüfen, ob ein an eine Funktion übergebener Zeiger mit Speicher belegt ist oder nicht?

Ich habe meine eigene Funktion in C geschrieben, die einen Zeichenzeiger akzeptiert – buf [pointer to a buffer] und Größe – buf_siz [buffer size]. Tatsächlich muss der Benutzer vor dem Aufruf dieser Funktion einen Puffer erstellen und ihm den Speicher von buf_siz zuweisen.

Da die Möglichkeit besteht, dass der Benutzer die Speicherzuweisung vergisst und einfach den Zeiger an meine Funktion weitergibt, möchte ich dies überprüfen. Gibt es also eine Möglichkeit, in meiner Funktion zu überprüfen, ob der übergebene Zeiger wirklich mit der buf_siz-Menge an Speicher belegt ist?

EDIT1: Es scheint, dass es keine Standardbibliothek gibt, um es zu überprüfen. Aber gibt es einen schmutzigen Hack, um es zu überprüfen?

EDIT2: Ich weiß, dass meine Funktion von einem guten C-Programmierer verwendet wird … Aber ich möchte wissen, ob wir das überprüfen können oder nicht … wenn wir können, würde ich gerne davon hören …

Fazit: Es ist also unmöglich zu überprüfen, ob ein bestimmter Zeiger innerhalb einer Funktion mit Speicher belegt ist oder nicht

  • Ich glaube nicht wirklich, aber ich fühle mich nicht sicher genug, um als Antwort zu posten.

    – Ibrahim

    16. Oktober 2009 um 5:48 Uhr

  • Es gibt keine Möglichkeit, dies zu überprüfen, es sei denn, Sie verwenden einen Speichermanager oder rollen Ihren eigenen.

    – Michael Foukarakis

    16. Oktober 2009 um 5:55 Uhr

  • Wenn es sich um einen Zeichenzeiger handelt, können wir strlen() oder sizeof() ausführen und prüfen, wie viel Speicher zugewiesen ist (natürlich, wenn der String NULL-terminiert ist). Bei anderen Typen bin ich mir nicht sicher, ob es einen Weg gibt. !!

    – Sandeep

    18. Februar 2014 um 5:33 Uhr

  • Ich weiß, dass dies eine alte Frage ist, aber es ist möglich, den zugewiesenen Speicher zu verfolgen, ohne Hacks zu verwenden. Mein Code unten enthält einige Ausschnitte, um Ihnen den Einstieg zu erleichtern.

    – c1moore

    25. April 2016 um 0:30 Uhr

  • Die Schlussfolgerung, die gezogen werden sollte, ist, dass Sie sollte nicht prüfen, auch wenn es möglich war. Dieser Artikel erklärt das Problem. Das Problem ist zwar im Windows-Begriff geschrieben, aber nicht Windows-spezifisch.

    – Ikegami

    17. Mai 2019 um 0:21 Uhr

Benutzeravatar von GManNickG
GManNickG

Sie können dies nicht überprüfen, außer einigen implementierungsspezifischen Hacks.

Pointer haben keine anderen Informationen als wohin sie zeigen. Das Beste, was Sie tun können, ist zu sagen: “Ich weiß, wie diese bestimmte Compiler-Version Speicher zuweist, also dereferenziere ich Speicher, bewege den Zeiger um 4 Bytes zurück, überprüfe die Größe, stelle sicher, dass sie übereinstimmt …” und so weiter. Sie können dies nicht auf standardmäßige Weise tun, da die Speicherzuweisung implementierungsdefiniert ist. Ganz zu schweigen davon, dass sie es möglicherweise überhaupt nicht dynamisch zugewiesen haben.

Sie müssen nur davon ausgehen, dass Ihr Client weiß, wie man in C programmiert. Die einzige Unlösung, die mir einfällt, wäre, den Speicher selbst zuzuweisen und zurückzugeben, aber das ist kaum eine kleine Änderung. (Es ist eine größere Designänderung.)

  • Ein Zeiger könnte nicht null sein, aber noch keine buf_siz-Bytes zugewiesen haben. Ich glaube nicht, dass es wirklich eine Möglichkeit gibt, zu überprüfen, was der Fragesteller will.

    – Ibrahim

    16. Oktober 2009 um 5:49 Uhr

  • Okay, wie wäre es damit? Da dies C ist, wird der Client wahrscheinlich verwendet malloc was ein zurückgibt NULL Zeiger, wenn er keinen Speicher zuweisen konnte. Also … rein malloc wir vertrauen?

    – Jacob

    16. Oktober 2009 um 5:56 Uhr

  • Es ist Sache des Clients, sicherzustellen, dass malloc funktioniert, bevor er die Funktion aufruft, wenn Sie das sagen.

    – GManNickG

    16. Oktober 2009 um 5:57 Uhr

  • @jacob – Ich weiß, dass wir bei malloc nachsehen können … aber wenn der Client vergisst, malloc auszuführen, führt dies zu einem Segmentierungsfehler … und ich möchte es vermeiden.

    – Programmierfreak

    16. Oktober 2009 um 6:01 Uhr

  • Ja. Die abschließende Schlussfolgerung ist, dass Ihre Funktion nur eine Sache und nur eine Sache tun sollte. Stellen Sie sich den Overhead vor, wenn jede Funktion sicherstellen würde, dass der Speicher, auf den sie über Parameter zugreift, gültig ist. Lassen Sie Ihre Funktion einfach das tun, was sie tun soll.

    – GManNickG

    16. Oktober 2009 um 6:04 Uhr

Benutzeravatar von Peter
Peter

Der folgende Code ist das, was ich einmal verwendet habe, um zu überprüfen, ob ein Zeiger versucht, auf illegalen Speicher zuzugreifen. Der Mechanismus besteht darin, ein SIGSEGV zu induzieren. Das SEGV-Signal wurde früher auf eine private Funktion umgeleitet, die longjmp verwendet, um zum Programm zurückzukehren. Es ist eine Art Hack, aber es funktioniert.

Der Code kann verbessert werden (verwenden Sie ‘sigaction’ anstelle von ‘signal’ usw.), aber es soll nur eine Idee geben. Es ist auch auf andere Unix-Versionen portierbar, bei Windows bin ich mir nicht sicher. Beachten Sie, dass das SIGSEGV-Signal an keiner anderen Stelle in Ihrem Programm verwendet werden sollte.

#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
#include <signal.h>

jmp_buf jump;

void segv (int sig)
{
  longjmp (jump, 1); 
}

int memcheck (void *x) 
{
  volatile char c;
  int illegal = 0;

  signal (SIGSEGV, segv);

  if (!setjmp (jump))
    c = *(char *) (x);
  else
    illegal = 1;

  signal (SIGSEGV, SIG_DFL);

  return (illegal);
}

int main (int argc, char *argv[])
{
  int *i, *j; 

  i = malloc (1);

  if (memcheck (i))
    printf ("i points to illegal memory\n");
  if (memcheck (j))
    printf ("j points to illegal memory\n");

  free (i);

  return (0);
}

  • @Saco i = malloc(1); ist gültiger C-Code und vorzuziehen i = (int*) malloc(1);. Vielleicht denken Sie an eine andere Sprache.

    – chux – Wiedereinsetzung von Monica

    19. September 2017 um 23:05 Uhr


  • Hinweis unter POSIX, setjmp() und longjmp() sollte wahrscheinlich durch ersetzt werden sigsetjmp() und siglongjmp(). Siehe stackoverflow.com/questions/20755260/…

    – Andreas Henle

    1. April 2019 um 14:24 Uhr

  • IMHO gibt es keine Garantie dafür, dass ein ungültiger Speicherzugriff ein SEGV verursacht – Ihr c = *(char *)(x); könnte gut gehen, obwohl x zeigt nicht auf einen zugeordneten Bereich. SEGV wird nur ausgelöst, wenn der Zeiger in ein Speichersegment zeigt, auf das nicht zugegriffen werden kann, aber Segmente, die mehrere kB groß sind, also wenn Sie 4 Bytes at allokieren10Änderungen sind diese Mem-Adresse 20 befindet sich trotz außerhalb eines zugewiesenen Bereichs immer noch im selben Segment wie Adresse 10daher können Sie, obwohl nicht zugewiesen, dennoch auf die Adresse zugreifen 20 ohne SEGV.

    – Michael Bier

    18. Juni 2020 um 12:39 Uhr


  • Deshalb sollten Sie unbenutzte Pointer immer auf setzen NULLverursachen diesen Wert ist garantiert, dass ein SEGV verursacht wird, wenn Sie versuchen, es zu dereferenzieren … Für andere Speicheradressen ist dies nicht garantiert.

    – Michael Bier

    18. Juni 2020 um 12:45 Uhr

  • @Michael Beer: “Es gibt keine Garantie dafür, dass ein ungültiger Speicherzugriff ein SEGV verursacht” – richtig, aber die Prüfung ist immer noch gültig. Wenn kein SEGV vorhanden ist, können Sie auf den Speicher zugreifen.

    – Petrus

    19. Januar 2021 um 19:32 Uhr


Für eine plattformspezifische Lösung könnte Sie die Win32-Funktion interessieren IsBadReadPtr (und andere mögen es). Diese Funktion kann (fast) vorhersagen, ob beim Lesen aus einem bestimmten Speicherabschnitt ein Segmentierungsfehler auftritt.

Dies gilt jedoch nicht schützen Sie im allgemeinen Fall, da das Betriebssystem nichts vom C-Laufzeit-Heap-Manager weiß und wenn ein Aufrufer einen Puffer übergibt, der nicht so groß ist, wie Sie erwarten, bleibt der Rest des Heap-Blocks weiterhin lesbar aus OS-Sicht.

  • @Greg – Tut mir leid zu sagen, dass ich mich nicht sehr für WIN32-Funktionen interessiere. Wenn möglich, ist ein gut funktionierender schmutziger Hack auch in Ordnung, da es KEINE Standard-C-Funktion gibt

    – Programmierfreak

    16. Oktober 2009 um 7:32 Uhr

  • Okay, Sie haben nicht angegeben, welche Plattform Sie verwenden sind interessieren. Wenn Sie die Plattform und den Compiler angeben, erhalten Sie möglicherweise eine spezifischere Antwort.

    – Greg Hewgill

    16. Oktober 2009 um 7:53 Uhr

  • IsBadXxxPtr sollte eigentlich CrashProgramRandomly heißen.

    – Alexey Frunze

    13. April 2012 um 11:02 Uhr

Ich initialisiere Zeiger immer auf den Nullwert. Wenn ich also Speicher zuweise, ändert sich dies. Wenn ich überprüfe, ob Speicher zugewiesen wurde, tue ich das pointer != NULL. Wenn ich Speicher freigebe, setze ich auch den Zeiger auf null. Ich kann mir nicht vorstellen, ob genügend Speicher zugewiesen wurde.

Dies löst Ihr Problem nicht, aber Sie müssen darauf vertrauen, dass jemand, der C-Programme schreibt, über ausreichende Fähigkeiten verfügt, um es richtig zu machen.

Ich habe einmal einen schmutzigen Hack auf meinem 64-Bit-Solaris verwendet. Im 64-Bit-Modus beginnt der Heap bei 0x1 0000 0000. Durch den Vergleich des Zeigers konnte ich feststellen, ob es sich um einen Zeiger im Daten- oder Codesegment handelte p < (void*)0x100000000ein Zeiger im Heap p > (void*)0x100000000 oder ein Zeiger in einem speicherabgebildeten Bereich (intptr_t)p < 0 (mmap gibt Adressen vom oberen Rand des adressierbaren Bereichs zurück). Dies ermöglichte es meinem Programm, zugewiesene und speicherabgebildete Zeiger in derselben Karte zu halten und mein Kartenmodul die richtigen Zeiger freizugeben.

Aber diese Art von Trick ist höchst unportabel, und wenn Ihr Code auf so etwas angewiesen ist, ist es an der Zeit, die Architektur Ihres Codes zu überdenken. Du machst wahrscheinlich etwas falsch.

Benutzeravatar von c1moore
c1moore

Ich weiß, dass dies eine alte Frage ist, aber in C ist fast alles möglich. Es gibt hier bereits ein paar hackige Lösungen, aber eine gültige Methode, um festzustellen, ob der Speicher richtig zugewiesen wurde, besteht darin, ein Orakel anstelle von zu verwenden malloc, calloc, reallocund free. Auf die gleiche Weise können Test-Frameworks (wie cmocka) Speicherprobleme erkennen (z. B. Fehler, nicht freigegebener Speicher usw.). Sie können eine Liste der zugewiesenen Speicheradressen führen, während sie zugewiesen werden, und diese Liste einfach überprüfen, wenn der Benutzer Ihre Funktion verwenden möchte. Ich habe etwas sehr Ähnliches für mein eigenes Testframework implementiert. Einige Beispielcode:

typedef struct memory_ref {
    void       *ptr;
    int        bytes;
    memory_ref *next;
}

memory_ref *HEAD = NULL;

void *__wrap_malloc(size_t bytes) {
    if(HEAD == NULL) {
        HEAD = __real_malloc(sizeof(memory_ref));
    }

    void *tmpPtr = __real_malloc(bytes);

    memory_ref *previousRef = HEAD;
    memory_ref *currentRef = HEAD->next;
    while(current != NULL) {
        previousRef = currentRef;
        currentRef = currentRef->next;
    }

    memory_ref *newRef = (memory_ref *)__real_malloc(sizeof(memory_ref));
    *newRef = (memory_ref){
        .ptr   = tmpPtr,
        .bytes = bytes,
        .next  = NULL
    };

    previousRef->next = newRef;

    return tmpPtr;
}

Sie hätten ähnliche Funktionen für calloc, reallocund freejeder Wrapper mit dem Präfix __wrap_. Die wirkliche malloc ist verfügbar durch die Verwendung von __real_malloc (ähnlich für die anderen Funktionen, die Sie umschließen). Wenn Sie überprüfen möchten, ob der Speicher tatsächlich zugewiesen ist, iterieren Sie einfach über die Verknüpfung memory_ref Liste und suchen Sie nach der Speicheradresse. Wenn Sie es finden und es groß genug ist, wissen Sie sicher, dass die Speicheradresse Ihr Programm nicht zum Absturz bringen wird; Andernfalls wird ein Fehler zurückgegeben. In der Header-Datei, die Ihr Programm verwendet, würden Sie diese Zeilen hinzufügen:

extern void *__real_malloc  (size_t);
extern void *__wrap_malloc  (size_t);
extern void *__real_realloc (size_t);
extern void *__wrap_realloc (size_t);
// Declare all the other functions that will be wrapped...

Meine Bedürfnisse waren ziemlich einfach, also habe ich eine sehr einfache Implementierung implementiert, aber Sie können sich vorstellen, wie dies erweitert werden könnte, um ein besseres Tracking-System zu haben (z struct der neben der Größe auch den Speicherplatz verfolgt). Dann kompilieren Sie den Code einfach mit

gcc src_files -o dest_file -Wl,-wrap,malloc -Wl,-wrap,calloc -Wl,-wrap,realloc -Wl,-wrap,free

Der Nachteil ist, dass der Benutzer seinen Quellcode mit den obigen Anweisungen kompilieren muss; Es ist jedoch alles andere als das Schlimmste, das ich je gesehen habe. Das Zuweisen und Freigeben von Speicher verursacht einen gewissen Overhead, aber beim Hinzufügen von Sicherheit entsteht immer ein gewisser Overhead.

Benutzeravatar von Greg Hewgill
Greg Hewgill

Nein, im Allgemeinen gibt es keine Möglichkeit, dies zu tun.

Wenn Ihre Schnittstelle nur “einen Zeiger auf einen Puffer übergibt, wo ich Sachen ablegen werde”, dann kann der Aufrufer wählen nicht überhaupt Speicher zuzuweisen und stattdessen einen Puffer mit fester Größe zu verwenden, der statisch zugewiesen wird, oder eine automatische Variable oder so etwas. Oder vielleicht ist es ein Zeiger auf einen Teil eines größeren Objekts auf dem Haufen.

Wenn Ihre Schnittstelle ausdrücklich sagt “Übergeben Sie einen Zeiger an zugewiesenen Speicher (weil ich ihn freigeben werde)”, dann sollten Sie damit rechnen, dass der Aufrufer dies tun wird. Ein Versäumnis ist nicht etwas, das Sie zuverlässig erkennen können.

  • Obwohl dies im Allgemeinen die beste und größtenteils richtige Antwort ist, würde ich sagen: Mit genügend Aufwand könnten Sie Ihren eigenen benutzerdefinierten Lader implementieren, um alle Speicherzuweisungen zu verfolgen – oder ein vorhandenes Tool wie verwenden valgrind 😉

    – Michael Bier

    18. Juni 2020 um 12:59 Uhr

1419690cookie-checkPrüfen, ob einem Zeiger Speicher zugeordnet ist oder nicht

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

Privacy policy