Ihre Aufgabe ist es, Code zu schreiben, der mindestens ein Byte Speicher in so wenigen Bytes wie möglich verliert. Der Speicher muss durchgesickert sein, nicht nur zugeordnet .
Speicherverlust ist der Speicher, den das Programm zuweist, auf den es jedoch nicht mehr zugreifen kann, bevor die ordnungsgemäße Zuordnung des Speichers aufgehoben werden kann. Für die meisten höheren Sprachen muss dieser Speicher auf dem Heap reserviert werden.
Ein Beispiel in C ++ wäre das folgende Programm:
int main(){new int;}
Dies macht ein new int
auf dem Heap ohne einen Zeiger darauf. Dieser Speicher wird sofort gelöscht, da wir keine Möglichkeit haben, darauf zuzugreifen.
So könnte eine Leckübersicht von Valgrind aussehen:
LEAK SUMMARY:
definitely lost: 4 bytes in 1 blocks
indirectly lost: 0 bytes in 0 blocks
possibly lost: 0 bytes in 0 blocks
still reachable: 0 bytes in 0 blocks
suppressed: 0 bytes in 0 blocks
In vielen Sprachen gibt es einen Speicher-Debugger (wie z. B. Valgrind ). Wenn Sie können, sollten Sie die Ausgabe eines solchen Debuggers einbeziehen, um zu bestätigen, dass Speicherlecks aufgetreten sind.
Ziel ist es, die Anzahl der Bytes in Ihrer Quelle zu minimieren.
Antworten:
Perl (5.22.2), 0 Bytes
Probieren Sie es online!
Ich wusste, dass es da draußen eine Sprache geben würde, die Speicherplatz in einem leeren Programm hat. Ich hatte erwartet, dass es ein Esolang sein würde, aber es stellte sich heraus, dass
perl
bei jedem Programm Speicherplatz verloren geht. (Ich gehe davon aus, dass dies beabsichtigt ist, da das Freigeben von Speicher, wenn Sie wissen, dass Sie sowieso beenden werden, nur Zeitverschwendung ist. Daher wird heutzutage häufig empfohlen, nur den verbleibenden Speicher zu verlieren, sobald Sie sich in den Exitroutinen Ihres Programms befinden .)Nachprüfung
quelle
perl --version
auf meinem Computer laufe , obwohl es überhaupt nie dazu kommt, ein Programm auszuführen.C
48 3122 BytesWarnung: Führen Sie dies nicht zu oft aus.
Vielen Dank an Dennis für jede Menge Hilfe / Ideen!
Das geht noch einen Schritt weiter.
shmget
reserviert gemeinsam genutzten Speicher, der nicht freigegeben wird, wenn das Programm endet. Es wird ein Schlüssel zum Identifizieren des Speichers verwendet, daher wird ein nicht initialisiertes int verwendet. Dies ist ein technisch undefiniertes Verhalten. In der Praxis bedeutet dies jedoch, dass wir den Wert verwenden, der sich beim Aufruf knapp über dem Stapel befindet. Dies wird über das nächste Mal geschrieben, wenn etwas zum Stapel hinzugefügt wird, sodass wir den Schlüssel verlieren.Der einzige Fall, in dem dies nicht funktioniert, ist, dass Sie herausfinden können, was sich zuvor auf dem Stapel befunden hat. Für zusätzliche 19 Bytes können Sie dieses Problem vermeiden:
Oder für 26 Bytes:
Bei dieser Version geht der Speicher jedoch verloren, nachdem das Programm beendet wurde. Während der Ausführung hat das Programm Zugriff auf den Speicher, der gegen die Regeln verstößt. Nach dem Beenden des Programms verlieren wir jedoch den Zugriff auf den Schlüssel und der Speicher wird weiterhin zugewiesen. Dies erfordert eine Adressraum-Layout-Randomisierung (ASLR), ansonsten
&k
bleibt sie immer gleich. Heutzutage ist ASLR normalerweise standardmäßig aktiviert.Nachprüfung:
Mit können Sie
ipcs -m
sehen, welcher gemeinsam genutzte Speicher auf Ihrem System vorhanden ist. Ich habe bereits vorhandene Einträge aus Gründen der Übersichtlichkeit entfernt:quelle
Unlambda (
c-refcnt/unlambda
), 1 ByteProbieren Sie es online!
Es ist wirklich eine Herausforderung, einen bereits vorhandenen Interpreter zu finden, der bei sehr einfachen Programmen Speicherplatz verliert. In diesem Fall habe ich Unlambda verwendet. Es gibt mehr als einen offiziellen Unlambda-Interpreter, dieser
c-refcnt
ist jedoch einer der am einfachsten zu erstellenden und hat die nützliche Eigenschaft, dass bei erfolgreicher Ausführung eines Programms Speicherplatz verloren geht. Alles, was ich hier geben musste, war das einfachste legale Unlambda-Programm, ein No-Op. (Beachten Sie, dass das leere Programm hier nicht funktioniert; der Speicher ist zum Zeitpunkt des Absturzes des Interpreters noch verfügbar.)Nachprüfung
quelle
TI-Basic, 12 Bytes
"... ein Speicherverlust ist, wenn Sie ein Goto / Lbl innerhalb einer Schleife oder eine If-Bedingung (alles, was einen End-Befehl hat) verwenden, um aus dieser Kontrollstruktur zu springen, bevor der End-Befehl erreicht ist ..." (mehr)
quelle
Pause
am ende Sie könnten 2 Bytes sparen.Python <3.6.5, 23 Bytes
property.__init__
undichte Stellen Verweise auf die Eigenschaft der altenfget
,fset
,fdel
, und__doc__
wenn Sie es auf einer bereits initialisiert nennenproperty
Instanz. Dies ist ein Fehler, der im Rahmen des CPython- Problems 31787 gemeldet und in Python 3.6.5 und Python 3.7.0 behoben wurde . (Ja,property([])
das können Sie auch.)quelle
C #, 34 Bytes
Für diese Lösung ist der Heap nicht erforderlich. Es braucht nur einen wirklich hart arbeitenden GC ( Garbage Collector ).
Im Wesentlichen macht es den GC zu seinem eigenen Feind.
Erläuterung
Wann immer der Destruktor aufgerufen wird, werden neue Instanzen dieser bösen Klasse erstellt, solange das Zeitlimit abgelaufen ist, und der GC wird angewiesen, dieses Objekt einfach zu verwerfen, ohne auf den Abschluss des Destruktors zu warten. Bis dahin wurden Tausende neuer Instanzen erstellt.
Je "böser" dies ist, desto härter arbeitet der GC, desto mehr wird dies in Ihrem Gesicht explodieren.
Haftungsausschluss : Ihr GC ist möglicherweise intelligenter als meiner. Andere Umstände im Programm können dazu führen, dass der GC das erste Objekt oder seinen Destruktor ignoriert. In diesen Fällen wird dies nicht explodieren. Aber in vielen Variationen es wird . Das Hinzufügen einiger Bytes hier und da kann ein Leck für alle möglichen Umstände sicherstellen. Naja, bis auf den Netzschalter vielleicht.
Prüfung
Hier ist eine Testsuite :
Ausgabe nach 10 Minuten:
Das sind 2 684 476 624 Bytes. Das Gesamtvolumen
WorkingSet
des Prozesses betrug ca. 4,8 GBDiese Antwort wurde von Eric Lipperts wunderbarem Artikel inspiriert: Wenn alles, was Sie wissen, falsch ist .
quelle
class L{~L(){new L();}}
? AFAIK ist dasfor(;;)
einzige, was den Speicher schneller leckt , oder?C (gcc) , 15 Bytes
Nachprüfung
quelle
Javascript, 14 Bytes
Golf gespielt
Registriert einen leeren Intervall-Handler mit einer Standardverzögerung und verwirft die resultierende Timer-ID (was ein Abbrechen unmöglich macht).
Ich habe ein nicht standardmäßiges Intervall verwendet, um mehrere Millionen Timer zu erstellen, um das Leck zu veranschaulichen, da die Verwendung eines standardmäßigen Intervalls die CPU wie verrückt frisst.
quelle
if(window && window.setInterval && typeof window.setInterval === 'function') { window.setInterval(0); }
clearInterval
mit einer inkrementierenden ID anrufen, bis Ihr Intervall abgelaufen ist. Zum Beispiel:for(let i=0;i<1e5;i++){try{clearInterval(i);}catch(ex){}}
free()
Java, 10 Bytes
Endlich eine kompetitive Antwort in Java!
Golf gespielt
Dies ist eine Methodenreferenz (gegen eine String-Konstante), die folgendermaßen verwendet werden kann:
Eine Literalzeichenfolge
". "
wird automatisch zum globalen Pool internierter Zeichenfolgen hinzugefügt , wie von derjava.lang.String
Klasse verwaltet, und da wir sie sofort abschneiden, kann der Verweis darauf im Code nicht weiter verwendet werden (es sei denn, Sie deklarieren dieselbe Zeichenfolge erneut).https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#intern--
Sie können dies zu einem Speicherverlust in Produktionsqualität machen, indem Sie die Zeichenfolge zu sich selbst hinzufügen und dann die intern () -Methode explizit in einer Schleife aufrufen .
quelle
("." + " ").intern()
würde (wenn es sich um Benutzereingaben oder um W / W handelt, so dass wir Compiler-Optimierungen rabattieren).simply by guessing it correctly
bedeutet jedoch nicht, dass so etwas wie Speicherlecks nicht vorhanden sind.there's probably some way to use reflection to determine the string's contents too
Könntest du das demonstrieren? (Hinweis, String.intern () ist im nativen Code implementiert ).Rust, 52 Bytes
Ordnet dem System-Malloc einige Bytes zu. Dies setzt voraus, dass der falsche ABI akzeptabel ist.
Rust (theoretisch), 38 BytesWir ordnen Speicher auf dem Heap zu, extrahieren einen rohen Zeiger und ignorieren ihn dann einfach, wodurch er effektiv verloren geht. (
Box::into_raw
ist dann kürzerstd::mem::forget
).Rust verwendet jedoch standardmäßig jemalloc, wodurch valgrind keine Undichtigkeiten feststellen kann . Wir könnten zum System-Allokator wechseln, aber das fügt 50 Bytes hinzu und erfordert jede Nacht. Vielen Dank für die Speichersicherheit.
Ausgabe des ersten Programms:
quelle
8086 ASM, 3 Bytes
In diesem Beispiel wird davon ausgegangen, dass eine C-Laufzeit eingebunden ist.
Hier wird zusammengestellt,
e9 XX XX
woXX XX
die relative Adresse von ist_malloc
Dies ruft
malloc
auf, eine unvorhersehbare Menge an Speicher zuzuweisen, und kehrt dann sofort zurück und beendet die Prozesse. Auf einigen Betriebssystemen wie DOS ist der Speicher möglicherweise erst nach einem Neustart des Systems wieder verfügbar!quelle
Viertens 6 Bytes
Golf gespielt
Ordnet eine leere Zeichenfolge mit zu
s" "
, wobei die Adresse und die Länge (0) auf dem Stapel verbleiben, und multipliziert sie dann (was dazu führt, dass eine Speicheradresse verloren geht).Valgrind
quelle
gehe zu 45 Bytes
Dadurch entsteht eine anonyme Goroutine mit einer Endlosschleife. Das Programm kann normal weiter ausgeführt werden, da das Starten der Goroutine wie das Starten eines gleichzeitig ausgeführten kleinen Threads aussieht, das Programm jedoch nicht die Möglichkeit hat, den für die Goroutine zugewiesenen Speicher zurückzugewinnen. Der Garbage Collector wird es auch nie einsammeln, da es noch läuft. manche Leute nennen dies "eine Goroutine lecken"
quelle
C.malloc(8)
, da Sie müssenimport"C"
Java 1.3, 23 Bytes
Einen Thread erstellen, aber nicht starten. Der Thread ist im internen Thread-Pool registriert, wird jedoch niemals gestartet, also niemals beendet und ist daher niemals ein Kandidat für den GC. Es ist ein unwiederbringliches Objekt, das in Java-Limbos steckt.
Es ist ein Fehler in Java, bis 1.3 enthalten, da es danach behoben wurde.
Testen
Das folgende Programm stellt sicher, dass der Speicher mit neuen Thread-Objekten verschmutzt wird und der freie Speicherplatz abnimmt. Aus Gründen der Dichtheitsprüfung lasse ich den GC intensiv laufen.
quelle
Befunge ( Pilze ), 1 Byte
Dies kann plattform- und versionsabhängig sein (ich habe es nur mit Version 1.0.4 unter Windows getestet), aber Pilze waren in der Vergangenheit ein sehr undichter Interpreter. Der
$
(drop) -Befehl sollte auf einem leeren Stack nichts tun, aber wenn man diesen Code durchläuft, verliert man sehr schnell eine Menge Speicher. Innerhalb von Sekunden wird es ein paar Gigs aufgebraucht haben und mit einem "Out of Memory" -Fehler abstürzen.Beachten Sie, dass es sich nicht unbedingt um einen
$
Befehl handeln muss - fast alles würde funktionieren. Es funktioniert jedoch nicht mit einer leeren Quelldatei. Es muss mindestens eine Operation geben.quelle
Schnelle 3, 38 Bytes
Neue Version:
x
hat einen starken Bezug zu sich selbst, daher wird die Zuordnung nicht aufgehoben, was zu einem Speicherverlust führt.Alte Version:
x
enthält einen starken Verweis aufy
und umgekehrt. Somit wird keine Zuordnung aufgehoben, was zu einem Speicherverlust führt.quelle
x
und aufy
diese Erinnerung verweisen , so dass dies für mich nicht wirklich wie ein Leck aussieht (es sei denn, du zerstörst sie irgendwie).do
Block setzen würde, der das Problem beheben würde, das Zeppelin aufgeworfen hat, oder?do
würde auch funktionieren. Gute Idee!class X{var x: X!};do{let x=X();x.x=x}
Delphi (Object Pascal) - 33 Byte
Erstellen eines Objekts ohne ein variables Vollkonsolenprogramm:
Wenn Sie FastMM4 im Projekt aktivieren, wird der Speicherverlust angezeigt:
quelle
84bytes
Dadurch wird genau 1 Byte nicht verwalteter Speicher zugewiesen, und dann geht der Speicher verloren, von
IntPtr
dem ich glaube, dass er der einzige Weg ist, ihn abzurufen oder freizugeben. Sie können es testen, indem Sie es in eine Schleife stopfen und darauf warten, dass die Anwendung abstürzt (möglicherweise möchten Sie ein paar Nullen hinzufügen, um die Dinge zu beschleunigen).Ich überlegte
System.IO.File.Create("a");
und so, aber ich bin nicht davon überzeugt , dass diese notwendigerweise Speicherlecks, wie die Anwendung selbst wird die Erinnerung sammeln, es ist das Betriebssystem unter dem könnte auslaufen (daClose
oderDispose
nicht genannt wurden). Für den Dateizugriff sind auch Dateisystemberechtigungen erforderlich, auf die sich niemand verlassen möchte. Und es stellt sich heraus, dass dies sowieso nicht lecken wird, da nichts den Aufruf des Finalisierers aufhält (wodurch die zugrunde liegenden Ressourcen freigesetzt werden können), was das Framework einschließt, um diese Art von Fehleinschätzungen (bis zu einem gewissen Grad) zu mindern, und Programmierer mit scheinbar nicht deterministischer Dateisperrung zu verwechseln (wenn Sie ein Zyniker sind). Vielen Dank an Jon Hanna, der mich auf den Punkt gebracht hat.Ich bin ein bisschen enttäuscht, dass ich keinen kürzeren Weg finden kann. Der .NET GC funktioniert, ich kann mir keinen
IDisposables
in mscorlib vorstellen, der definitiv leckt (und in der Tat scheinen alle Finalisierer zu haben, wie ärgerlich) . Mir ist keine andere Möglichkeit bekannt, nicht verwalteten Speicher zuzuweisen (außer PInvoke) ) und Reflexion sorgt für etwas , um es mit einer Referenz (unabhängig von der Sprache Semantik (zB privaten Mitgliedern oder Klassen ohne Accessoren)) kann gefunden werden.quelle
System.IO.File.Create("a")
GC.SuppressFinalize(System.IO.File.Create("a"))
leckt nichts, wird aber da ausdrücklich gebeten, den Finalizer desFileStream
Produzierten nicht laufen zu lassen .<!-- language: lang-c# -->
Danke für diese & nette Antwort! (Es ist C #, also ich liebe es)Faktor 13 Bytes
Factor verfügt über eine automatische Speicherverwaltung, bietet jedoch auch Zugriff auf einige libc-Funktionen:
Ordnet manuell 1 Byte Speicher zu, gibt die Adresse zurück und löscht sie.
malloc
Es wird tatsächlich eine Kopie registriert, um Speicherlecks zu verfolgen und doppelt freizugeben. Es ist jedoch keine leichte Aufgabe, die von Ihnen geleakte zu identifizieren.Wenn Sie sicherstellen möchten, dass Sie diese Referenz wirklich verlieren, gehen Sie wie folgt vor:
Testen von Lecks mit
[ 1 malloc drop ] leaks.
sagt:Testen von Lecks mit
[ 1 (malloc) drop ] leaks.
sagt:Ach nein! Armer Faktor, jetzt hat es Alzheimer! D:
quelle
Common Lisp (nur SBCL),
2826 BytesSie führen es so aus
sbcl --eval 'sb-alien::(make-alien int)'
:; es wird nichts gedruckt oder zurückgegeben, aber die Speicherzuordnung erfolgt. Wenn ich das Formular in a(print ...)
einbinde, wird der Zeiger in der REPL angezeigt.package::(form)
ist eine spezielle Notation in SBCL, um das aktuelle Paket vorübergehend zu binden, während ein Formular gelesen wird. Dies wird hier verwendet, um Präfixe sowohlmake-alien
als auchint
mit zu vermeidensb-alien
. Ich denke, es wäre ein Betrug anzunehmen, dass das aktuelle Paket auf dieses eingestellt ist, da dies beim Start nicht der Fall ist.make-alien
reserviert Speicher für einen bestimmten Typ und eine optionale Größe (unter Verwendung von malloc).Wenn Sie dies in der REPL ausführen, fügen Sie dies
0
nach der Zuordnung hinzu, damit die REPL nicht den Zeiger, sondern diesen Wert zurückgibt. Ansonsten, das wäre nicht ein wirkliches Leck sein , weil die REPL der letzten drei zurückgegebenen Werte erinnert (siehe*
,**
,***
) und wir konnten noch eine Chance haben, die zugewiesenen Speicher freizugeben .2 Bytes entfernt dank PrzemysławP, danke!
quelle
1
(oder2
,3
usw.) anstelle von verwenden,()
damit Sie einen Wert zurückgeben1
? Es würde 1 Byte sparen. Auch ist diese Antwort nur REPL? Vielleicht, wenn Sie Code mit ladenload
, können Sie nichts()
oder nichts am Ende einfügen , weil es sowieso nicht zugänglich sein wird?eval
und das funktioniert wie du gesagt hast. Danke vielmals!AutoIt , 39 Bytes
Ordnet ein Byte vom Heap zu. Da das von zurückgegebene Handle
_MemGlobalAlloc
verworfen wird, gibt es keine Möglichkeit, diese Zuordnung explizit freizugeben.quelle
C ++, 16 Bytes
Ich habe kein Valgrind, um zu überprüfen, ob es undicht ist, aber ich bin mir ziemlich sicher, dass es das sollte.Ansonsten würde ich versuchen:Valgrind Ergebnis
(Es leckt tatsächlich)
quelle
g++ 4.3.2
(nicht die neueste) und es kompiliert ganz gut.int
Ich denke, dass kein Rückgabetyp standardmäßig ist. Mit-Wall
ich habe eine Warnung:plop.cpp:1: warning: ISO C++ forbids declaration of 'main' with no type
[]{new int;}
als C ++ - Funktion zählen können (die Herausforderung hat kein gesamtes Programm angegeben).Java (OpenJDK 9) ,
322.220ByteProbieren Sie es online!
Dies ist ein weiterer Speicherverlust, der den String-Cache nicht verwendet. Es belegt die Hälfte Ihres Arbeitsspeichers und Sie können damit nichts anfangen.
Dank an Zeppelin für das Speichern aller Bytes
quelle
Unsafe
Instanz aus der darin enthaltenen statischen Variablen abrufen:import sun.misc.*;class M{static void main(String[]a)throws Exception{java.lang.reflect.Field f=Unsafe.class.getDeclaredField("theUnsafe");f.setAccessible(1<2);((Unsafe)f.get(1)).allocateMemory(1);}}
public static void main
durch einen statischen Initialisierer ersetzenstatic{try{}catch(Exception e){}}
(der etwas schwieriger zu starten sein könnte, aber dennoch gültig und kompilierbar ist).a
statt verwendenargs
und public entfernen. tio.run/nexus/…c, 9 Bytes
Beweis:
quelle
gcc
ist. Dies sollte auch mit dem leeren Programm funktionieren. Versuchen Siegcc src.c && valgrind ./a.out
, was ein sauberes Ergebnis erzeugen sollte.C #, 109 Bytes
Wir haben die Idee hinter dieser Lücke im Produktionscode gefunden und die Untersuchung führt zu diesem Artikel. Das Hauptproblem ist in diesem langen Zitat aus dem Artikel (lesen Sie es für weitere Informationen):
Wenn Sie den Compiler in Visual Studio 2015 ausführen und das Fenster "Diagnosetools" verwenden, werden nach ca. 38 Sekunden die folgenden Ergebnisse angezeigt. Beachten Sie, dass der Prozessspeicher stetig ansteigt und der Garbage Collector (GC) weiterhin ausgeführt wird, aber nichts sammeln kann.
quelle
C 30 Bytes
Valgrind Ergebnisse:
quelle
main(){malloc(1);}
?Dart, 76 Bytes
Ein bisschen wie die JavaScript-Antwort. Wenn Sie
.listen
ein Dart-Stream-Objekt aufrufen, erhalten Sie ein StreamSubscription zurück, mit dem Sie die Verbindung zum Stream trennen können. Wenn Sie das jedoch wegwerfen, können Sie sich niemals vom Stream abmelden, was zu einem Leck führt. Die einzige Möglichkeit, das Leck zu beheben, besteht darin, dass der Stream selbst erfasst wird, aber intern von einer StreamController + Timer-Kombination referenziert wird.Leider ist Dart zu schlau für die anderen Sachen, die ich ausprobiert habe.
()async=>await new Completer().future
funktioniert nicht, weil das Verwenden von wait das Gleiche ist wie doingnew Completer().future.then(<continuation>)
, wodurch der Abschluss selbst zerstört werden kann, wenn auf den zweiten Completer nicht verwiesen wird (Completer enthält einen Verweis auf die Zukunft von.future
, Future enthält einen Verweis auf die Fortsetzung als Abschluss).Isolate (auch als Threads bezeichnet) werden von GC bereinigt, sodass es
import'dart:isolate';main(_)=>Isolate.spawn(main,0,paused:true);
nicht funktioniert, sich selbst in einem neuen Thread zu erzeugen und ihn sofort anzuhalten ( ). Selbst wenn Sie ein Isolat mit einer Endlosschleife (import'dart:isolate';f(_){while(true){print('x');}}main()=>Isolate.spawn(f,0);
) erzeugen, wird das Isolat beendet und das Programm beendet.Naja.
quelle
Schnell, 12 Bytes
Erläuterung:
Dies ist ein De-facto-Speicherverlust, der in jeder Sprache auftreten kann, unabhängig davon, ob die Sprache die manuelle Speicherverwaltung, die automatische Referenzzählung (ARC, wie Swift) oder sogar die umfassende Speicherbereinigung verwendet.
[3,5]
ist nur ein Array-Literal. Dieses Array reserviert genügend Speicher für mindestens diese beiden Elemente. Die3
und5
sind einfach beliebig.Indizieren (Indizieren) von und
Array<T>
erzeugt eineArraySlice<T>
. EinArraySlice<T>
ist ein Blick in den Speicher des Arrays, aus dem es erstellt wurde.[3,5][0...0]
erzeugt eineArraySlice<Int>
, deren Wert ist[3]
. Beachten Sie, dass das3
in diesem Slice das gleiche3
Element wie3
imArray
oben gezeigten Original ist und keine Kopie.Das resultierende Slice kann dann in einer Variablen gespeichert und verwendet werden. Das ursprüngliche Array wird nicht mehr referenziert, sodass Sie denken, es könnte freigegeben werden. Kann es aber nicht.
Da das Slice einen Blick auf den Speicher des Arrays freigibt, von dem es stammt, muss das ursprüngliche Array so lange am Leben gehalten werden, wie das Slice lebt. Von den ursprünglichen
2
Speichergrößen, die zugewiesen wurden, wird nur die erste Speichergröße verwendet, die vorhanden sein muss, damit die erste nicht zugewiesen wird. Die zweite Elementgröße des Speichers ist durchgesickert.Die Lösung für dieses Problem besteht darin, kleine Scheiben großer Arrays nicht lange am Leben zu erhalten. Wenn Sie den Slice-Inhalt beibehalten möchten, stufen Sie ihn in ein Array um, wodurch der zu kopierende Speicher ausgelöst und die Abhängigkeit vom Speicher des ursprünglichen Arrays aufgehoben wird:
quelle
Lösung 1: C (Mac OS X x86_64), 109 Byte
Die Quelle für golf_sol1.c
Das obige Programm muss mit Ausführungszugriff auf das __DATA-Segment kompiliert werden.
Führen Sie dann zur Ausführung des Programms Folgendes aus:
Ergebnisse:
Leider sucht Valgrind nicht nach Speicher, der durch Systemaufrufe zugewiesen wurde, sodass ich kein schönes erkanntes Leck anzeigen kann.
In vmmap sehen wir jedoch den großen Teil des zugewiesenen Speichers (MALLOC-Metadaten).
Erläuterung
Ich denke, ich muss beschreiben, was hier tatsächlich vor sich geht, bevor ich mich der verbesserten Lösung zuwende.
Diese Hauptfunktion missbraucht die fehlende Typdeklaration von C (daher wird standardmäßig int verwendet, ohne dass wir das Schreiben von Zeichen verschwenden müssen) sowie die Funktionsweise von Symbolen. Der Linker kümmert sich nur darum, ob er ein
main
zum Aufrufen aufgerufenes Symbol finden kann . Also machen wir hier main zu einem Array von ints, die wir mit unserem Shellcode initialisieren, der ausgeführt wird. Aus diesem Grund wird main nicht zum __TEXT-Segment, sondern zum __DATA-Segment hinzugefügt, weshalb das Programm mit einem ausführbaren __DATA-Segment kompiliert werden muss.Der in main gefundene Shellcode ist der folgende:
Dazu wird die Funktion syscall aufgerufen, um eine Speicherseite zuzuweisen (die Funktion syscall mach_vm_allocate verwendet sie intern). RAX sollte 0x100000a entsprechen (teilt dem Syscall mit, welche Funktion wir wollen), während RDI das Ziel für die Zuordnung enthält (in unserem Fall soll dies mach_task_self () sein), RSI sollte die Adresse enthalten, um den Zeiger in den neu erstellten Speicher zu schreiben (also zeigen wir nur auf einen Abschnitt im Stapel), RDX enthält die Größe der Zuordnung (wir übergeben nur RAX oder 0x100000a, um nur Bytes zu sparen), R10 enthält die Flags (wir geben an, dass dies möglich ist) überall zugeteilt werden).
Jetzt ist nicht klar ersichtlich, woher RAX und RDI ihre Werte beziehen. Wir wissen, dass RAX 0x100000a und RDI der Wert sein muss, den mach_task_self () zurückgibt. Zum Glück ist mach_task_self () eigentlich ein Makro für eine Variable (mach_task_self_), die sich jedes Mal auf derselben Speicheradresse befindet (sollte sich jedoch beim Neustart ändern). In meinem speziellen Fall befindet sich mach_task_self_ zufällig unter 0x00007fff7d578244. Um Anweisungen einzuschränken, geben wir stattdessen diese Daten von argv weiter. Aus diesem Grund führen wir das Programm mit diesem Ausdruck aus
$(ruby -e 'puts "\xf5\xff\xff\xfe\xff\xff\x44\x82\x57\x7d\xff\x7f"')
für das erste Argument. Die Zeichenfolge sind die beiden kombinierten Werte, wobei der RAX-Wert (0x100000a) nur 32 Bit beträgt und auf den ein Einsen-Komplement angewendet wurde (es gibt also keine Null-Bytes; wir haben nur NICHT den Wert, um das Original zu erhalten). Der nächste Wert lautet Die RDI (0x00007fff7d578244), die mit 2 zusätzlichen Junk-Bytes am Ende nach links verschoben wurde (um die Null-Bytes wieder auszuschließen, verschieben wir sie einfach zurück nach rechts, um sie wieder auf das Original zurückzusetzen).Nach dem Systemaufruf schreiben wir in unseren neu zugewiesenen Speicher. Der Grund dafür ist, dass der mit mach_vm_allocate (oder diesem Syscall) zugewiesene Speicher tatsächlich VM-Seiten sind und nicht automatisch in den Speicher ausgelagert werden. Vielmehr werden sie reserviert, bis Daten in sie geschrieben werden, und dann werden diese Seiten in den Speicher abgebildet. War nicht sicher, ob es den Anforderungen entsprechen würde, wenn es nur reserviert wäre.
Für die nächste Lösung werden wir die Tatsache ausnutzen, dass unser Shell-Code keine Null-Bytes enthält, und können ihn daher aus dem Code unseres Programms verschieben, um die Größe zu verringern.
Lösung 2: C (Mac OS X x86_64), 44 Byte
Die Quelle für golf_sol2.c
Das obige Programm muss mit Ausführungszugriff auf das __DATA-Segment kompiliert werden.
Führen Sie dann zur Ausführung des Programms Folgendes aus:
Das Ergebnis sollte dasselbe sein wie zuvor, da wir eine Zuweisung derselben Größe vornehmen.
Erläuterung
Es folgt ungefähr dem Konzept von Lösung 1, mit der Ausnahme, dass wir den Teil unseres undichten Codes außerhalb des Programms verschoben haben.
Der in main gefundene Shellcode ist jetzt der folgende:
Dies kopiert im Grunde den Shellcode, den wir in argv übergeben haben, um nach diesem Code zu sein (also wird der eingefügte Shellcode ausgeführt, nachdem er kopiert wurde). Was zu unseren Gunsten funktioniert, ist, dass das __DATA-Segment mindestens eine Seitengröße hat. Selbst wenn unser Code nicht so groß ist, können wir dennoch mehr "sicher" schreiben. Der Nachteil ist hier die ideale Lösung, würde nicht einmal die Kopie benötigen, sondern nur den Shellcode in argv direkt aufrufen und ausführen. Leider hat dieser Speicher keine Ausführungsrechte. Wir könnten die Rechte dieses Speichers ändern, aber es würde mehr Code erfordern als nur das Kopieren. Eine alternative Strategie wäre, die Rechte eines externen Programms zu ändern (dazu später mehr).
Der Shellcode, den wir an argv übergeben, ist der folgende:
Dies entspricht in etwa unserem vorherigen Code. Der einzige Unterschied besteht darin, dass wir die Werte für EAX und RDI direkt einbeziehen.
Mögliche Lösung 1: C (Mac OS X x86_64), 11 Byte
Die Idee, das Programm extern zu modifizieren, gibt uns die Möglichkeit, den Leaker in ein externes Programm zu verschieben. Wenn unser aktuelles Programm (Einreichung) nur ein Dummy-Programm ist und das Leaker-Programm Speicher in unserem Zielprogramm reserviert. Jetzt war ich mir nicht sicher, ob dies unter die Regeln für diese Herausforderung fallen würde, teilte sie aber trotzdem.
Wenn wir also mach_vm_allocate in einem externen Programm mit dem Ziel verwenden würden, das auf unser Herausforderungsprogramm festgelegt ist, könnte dies bedeuten, dass unser Herausforderungsprogramm nur etwas im Sinne von Folgendem sein müsste:
Wo dieser Shellcode nur ein kurzer Sprung zu sich selbst ist (unendlicher Sprung / Endlosschleife), bleibt das Programm geöffnet und wir können von einem externen Programm darauf verweisen.
Mögliche Lösung 2: C (Mac OS X x86_64), 8 Byte
Lustigerweise habe ich bei der Betrachtung der Valgrind-Ausgabe gesehen, dass Valgrind zufolge Dyld den Speicher verliert. So effektiv verliert jedes Programm etwas Speicher. Wenn dies der Fall ist, könnten wir tatsächlich nur ein Programm erstellen, das nichts tut (einfach beendet), und das tatsächlich Speicher verliert.
Quelle:
quelle
Nur Englisch ,
71705835 Bytes1 Byte durch Löschen einer Leerzeile entfernt. 12 Bytes wurden entfernt, indem die Definition des Typs "bogon" entfernt und der übergeordnete Typ "thing" anstelle des Subtyps "bogon" verwendet wurde. 23 Bytes wurden entfernt, indem von einem vollständigen Programm zu einer Routine gewechselt wurde, die Speicherplatz verliert.
Golf Version:
Ungolfed-Version, die ein vollständiges Programm ist, eine Untertypdefinition verwendet und keinen Speicher verliert:
Wenn die Golf-Version von "x" aufgerufen wird, leckt der Speicher proportional zu der Häufigkeit, mit der "x" aufgerufen wird. In der Golfversion "Deallocate the thing". würde das Speicherleck beheben.
Normales Englisch sucht standardmäßig nach Speicherlecks. Wenn die Version ausgeführt wird, die Speicher verliert, wird ein Dialogfeld angezeigt, bevor das Programm beendet wird. Das Dialogfeld hat den Titel "Debug", die Meldung "1 Tropf" und die Schaltfläche "OK". Je öfter die Leaking-Funktion aufgerufen wird, desto größer ist die Anzahl der "Tropfen" in der Nachricht. Wenn die Version ausgeführt wird, bei der kein Speicherverlust auftritt, wird das Dialogfeld nicht angezeigt.
Im einfachen Englisch ist ein "Ding" ein Zeiger auf ein Element in einer doppelt verknüpften Liste. "Thing", "to start up" und "to shut down" werden in einem Modul namens "the noodle" definiert, das in jedes Projekt (normalerweise als separate Datei) kopiert werden muss. "A", "the", "to", "to allocate memory" und "to destroy" sind im Compiler definiert.
quelle