Prüfen, ob einem Zeiger Speicher zugeordnet ist oder nicht
Lesezeit: 10 Minuten
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
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
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.
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.
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:
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
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.
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
14196900cookie-checkPrüfen, ob einem Zeiger Speicher zugeordnet ist oder nichtyes
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