memcpy Nullbytes in konstante Variable – undefiniertes Verhalten?

Lesezeit: 8 Minuten

Benutzeravatar von Jackson Allan
Jackson Allan

In C und C++ handelt es sich um undefiniertes Verhalten memcpy in ein const Variable, wenn die Anzahl der zu kopierenden Bytes Null ist?

int x = 0;
const int foo = 0;
memcpy( (void *)&foo, &x, 0 );

Diese Frage ist nicht rein theoretisch. Ich habe ein Szenario, in dem memcpy aufgerufen wird und wenn der Zielzeiger auf zeigt const Arbeitsspeicher, dann ist das Größenargument garantiert null. Also frage ich mich, ob ich es als Sonderfall behandeln muss.

  • Warum überhaupt Memcpy in C++ verwenden? Dafür ist std::copy da. Die gesamte (void*)-Umwandlung ignoriert jegliche Konstanz und Typsicherheit (das ist so wichtig in C++). Stellen Sie außerdem sicher, dass Sie Ihre Frage speziell für “C” und “C++” stellen, da es sich um unterschiedliche Sprachen mit unterschiedlichen Regeln handelt

    – Pepijn Kramer

    9. Oktober um 14:38 Uhr


  • Vermutlich, wenn das Ziel ein Zeiger auf ist const Speicher, dann handelt es sich um einen ungültigen Zeiger und das Verhalten ist entsprechend undefiniert cpReferenz.

    – Adrian Maulwurf

    9. Oktober um 14:41 Uhr

  • Warum sollte das undefiniert sein? Die wackeligen Zeigerumwandlungen sind normalerweise legal, es ist die Deferenzierung (oder das Schreiben auf das Ergebnis von einem), die illegal ist.

    – HolyBlackCat

    9. Oktober um 14:45 Uhr


  • @HolyBlackCat Der Standard erlegt einige Einschränkungen auf memcpy die einige Dinge zu einem überraschend undefinierten Verhalten machen. Zum Beispiel memcpy( NULL, NULL, 0 ) ist ein technisch undefiniertes Verhalten, da die übergebenen Zeiger gültig sein müssen, obwohl tatsächlich keine Kopie stattfindet. Was meine ursprüngliche Frage betrifft, so konnte ich im Standard nichts finden, das genau dieses Szenario abdeckt, obwohl dort möglicherweise etwas enthalten ist.

    – Jackson Allan

    9. Oktober um 15:07 Uhr

  • @PepijnKramer “Warum überhaupt memcpy in C++ verwenden?” – Es gibt mehrere Situationen/Ecken in C++, wo die einzige Möglichkeit ist, auf Wortspiele zu verzichten UB geht über memcpydaher ist es nicht unvernünftig, es in C++-Code zu sehen.

    – Jesper Juhl

    9. Oktober um 15:09 Uhr


Benutzeravatar von Nate Eldredge
Nate Eldredge

cc17

Die ältere Frage Ist es garantiert sicher, memcpy(0,0,0) auszuführen? weist auf 7.1.4p1 hin:

Jede der folgenden Aussagen trifft zu, sofern in den folgenden detaillierten Beschreibungen nicht ausdrücklich anders angegeben: Wenn ein Argument einer Funktion einen ungültigen Wert hat (z. B. einen Wert außerhalb der Domäne der Funktion oder einen Zeiger außerhalb des Adressraums des Programms, oder ein Nullzeiger, oder ein Zeiger auf nicht modifizierbaren Speicher, wenn der entsprechende Parameter nicht konstant qualifiziert ist) oder ein Typ (nach Beförderung), der von einer Funktion mit variabler Anzahl von Argumenten nicht erwartet wird, Das Verhalten ist undefiniert.

Der Prototyp für memcpy ist

void *memcpy(void * restrict s1, const void * restrict s2, size_t n);

wo der erste Parameter nicht ist const-qualifiziert und &foo weist auf nicht modifizierbaren Speicher hin. Dieser Code ist also UB, es sei denn, die Beschreibung von memcpy ausdrücklich etwas anderes angibt, was nicht der Fall ist. Es heißt lediglich:

Die Funktion memcpy kopiert n Zeichen aus dem Objekt, auf das s2 zeigt, in das Objekt, auf das s1 zeigt.

Dies impliziert das memcpy mit einer Zählung von 0 kopiert keine Zeichen (was auch durch 7.24.1p2 “kopiert null Zeichen” bestätigt wird, danke Lundin), aber es befreit Sie nicht von der Anforderung, gültige Argumente zu übergeben.

  • Da C++ die Anforderungen der C-Bibliothek nicht lockert, gilt dies auch für C++.

    – Passant

    10. Oktober um 5:51 Uhr

  • Ich vermute, dass dies ein Shroedinger-Fehler sein wird – es gibt absolut keinen Grund, warum das Aufrufen auf die von Ihnen beschriebene Weise irgendwelche Probleme verursachen würde; aber sobald Sie es tatsächlich implementieren, werden Sie feststellen, dass es genau eine Bibliothek auf dem Planeten gibt, die abstürzt, wenn Sie dies tun, und das ist die, die Sie verwenden.

    – Betty Crokker

    10. Oktober um 12:23 Uhr

  • @BettyCrokker Oder schlimmer noch, die Bibliothek wäre vollkommen in Ordnung, wenn Sie sie tatsächlich aufrufen würden. Der Compiler sieht den Aufruf jedoch als Memcpy mit Sonderfall und schließt daraus, dass dies nicht passieren kann, und entfernt den aufrufenden Codepfad.

    – Benutzer1937198

    10. Oktober um 13:40 Uhr

  • “Dies impliziert, dass memcpy mit einer Zählung von 0 nichts tut” C17 7.24.1/2 gibt ausdrücklich an, dass memcpy (eine Kopierfunktion) null Bytes kopiert, falls der Größenparameter null ist.

    – Ludin

    10. Oktober um 14:12 Uhr

  • @Joshua: Ein Hauptziel des Standards ist es, Compiler so nützlich wie möglich zu machen. Wenn auf einer Plattform, eine Implementierung, die sich seltsam verhielt, wenn sie angegeben wurde memcpy(0,0,0) für eine Aufgabe wirklich nützlicher wäre als eine, die sie als No-Op behandeln würde, denke ich, dass der Standard eine solche Behandlung zulassen würde. Wenn es keinen Fall gibt, in dem eine solche Behandlung nützlich wäre, dann sollte sich niemand darum kümmern, ob der Standard eine solche Behandlung erlaubt, und somit gäbe es keinen Grund für den Standard, Tinte auszugeben, die dies verbietet.

    – Superkatze

    10. Oktober um 18:14 Uhr


Benutzeravatar von Supercat
Superkatze

Es ist klar, dass auf der überwiegenden Mehrheit der Plattformen eine Implementierung, die memcpy (alles, alles, 0) als No-Op verarbeiten würde, unabhängig von der Gültigkeit der Quell- und Zielzeiger, in jeder Hinsicht, im Wesentlichen in jedem Non wäre -ausgedachtes Szenario, genauso gut oder besser als eines, das alles andere tut.

Die Art und Weise, wie der Standard geschrieben ist, könnte jedoch dahingehend interpretiert werden, dass Compiler jede Situation als UB behandeln dürfen, in der die Zieladresse nicht mit beschreibbarem Speicher verbunden ist.

Wenn eine Implementierung verwendet wird, die versucht, Sonderfälle in der von den Autoren des Standards beabsichtigten nützlichen Weise zu verarbeiten, ohne Rücksicht darauf, ob der Standard ein solches Verhalten eindeutig vorschreibt, alle memcpy und memmove Operationen, bei denen die Größe Null ist, werden zuverlässig als No-Ops verarbeitet. Wenn die Größe häufig null ist, kann das Überspringen von a Leistungsvorteile bringen memcpy oder memmove Aufruf im Fall der Größe Null, aber eine solche Überprüfung wäre niemals auf Korrektheit erforderlich.

Wenn man jedoch eine zuverlässige Kompatibilität mit Compilerkonfigurationen sicherstellen möchte, die aggressiv davon ausgehen, dass Code niemals Eingaben erhält, die Eckfälle auslösen, die nicht zu 100% eindeutig vom Standard vorgeschrieben sind, und darauf ausgelegt ist, unsinnigen Code zu generieren, wenn solche Eingaben empfangen werden , dann ist es in jedem Fall erforderlich, eine Überprüfung für size==0 hinzuzufügen, wenn eine Nullgröße von etwas anderem als einem Zeiger auf beschreibbaren Speicher begleitet werden kann, da eine solche Überprüfung die Leistung in Situationen mit sehr großer Größe negativ beeinflussen kann selten null.

  • Kommentare sind nicht für längere Diskussionen gedacht; diese Konversation wurde in den Chat verschoben.

    – Samuel Liew

    11. Oktober um 1:49 Uhr

  • @supercat Jedes Mal, wenn jemand auf dieser Seite eine Frage zu undefiniertem Verhalten in C stellt, posten Sie eine Version dieser Tirade, und ich muss fragen, warum Sie sie weiterhin posten hier, wo keiner der Menschen, deren Meinung Sie ändern müssen, es lesen wird? Sie könnten ein Positionspapier für den C-Ausschuss schreiben. Sie könnten es auf den GCC- oder LLVM-Entwicklungs-Mailinglisten zur Sprache bringen. Sie könnten einige tatsächliche Nachforschungen anstellen, um Ihre Behauptungen zu untermauern, und sie dann in PLDI oder ASPLOS veröffentlichen. Du hast bessere Möglichkeiten als hier ignoriert zu werden.

    – zol

    12. Oktober um 13:41 Uhr

  • @zwol: Meine Antwort knüpft direkt an die gestellte Frage an und erwähnt, warum die Gewährleistung eines definierten Verhaltens möglicherweise die Kosten einer einfachen Implementierung auf einigen Plattformen erhöht. Es gibt keinen Grund, warum jemand, der nicht auf solche Plattformen abzielt sollte muss man sich über solche Dinge Gedanken machen, und ich denke, vor 20 Jahren hätte so ziemlich jeder zugestimmt, dass man sich darüber keine Sorgen machen muss, aber eine Menge Code wird mit Compilern so unentgeltlich verarbeitet machen Es ist notwendig, sich über solche Dinge Gedanken zu machen, damit Compiler-“Optimierungen” keine willkürliche Remote-Code-Ausführung erleichtern.

    – Superkatze

    12. Oktober um 14:29 Uhr


  • @zwol: Kann jemand heute ein guter C-Programmierer sein, ohne beides zu verstehen (1) der Standard wurde nicht mit der Absicht geschrieben, Programmierer durch unnötige Reifen springen zu lassen, und (2) einige Compiler sind so konzipiert, dass sie sich auf gefährlich unsinnige Weise verhalten, es sei denn, Programmierer springen durch unentgeltliche Reifen, die nicht vom Standard vorgesehen sind? Während Nr. 2 wie eine unverschämte Behauptung erscheinen mag, von der ich nicht erwarten würde, dass die Leute sie ohne Beweise glauben, da es unverschämt ist zu glauben, dass angeblich universelle Compiler auf diese Weise entworfen würden, könnte man nur sagen, dass dies nicht der Fall ist …

    – Superkatze

    12. Oktober um 14:54 Uhr

  • Nö. Hier interessiert es niemanden. Die Poster auf dieser Site interessieren sich nur dafür, was Sie tatsächlich tun müssen, damit Ihr Code heute funktioniert, was bedeutet, dass Sie die Interpretationen der Compiler der aktuellen Generation des Standards als das akzeptieren, was C ist. Auch hier wären Sie viel besser bedient, wenn Sie ein N-Dokument für das Komitee oder eine wissenschaftliche Arbeit darüber verfassen, wie viel „Legacy“-Code tatsächlich von aktuellen Compilern gebrochen wurde oder wirklich sonst irgendwas immer wieder mehr oder weniger dieselben 500 Wörter auf einer Seite zu schreiben, die niemanden interessiert.

    – zol

    12. Oktober um 16:05 Uhr

1433590cookie-checkmemcpy Nullbytes in konstante Variable – undefiniertes Verhalten?

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

Privacy policy