Speicherverlust in so wenigen Bytes wie möglich

79

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 intauf 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.

Weizen-Assistent
quelle
2
Vielleicht könnten Sie verschiedene Bereiche der Menge durchgesickert haben und je nachdem, wie viel Sie durchgesickert sind, verlieren Sie x% Ihrer Byteanzahl
Christopher
11
@ChristopherPeart Zum einen bin ich kein Fan von Boni auf Herausforderungen und zum anderen ist es, wie Sie bereits gezeigt haben, sehr einfach, unbegrenzten Speicher zu verlieren.
Weizen-Assistent
1
Verwandte . Kein Duplikat, denn die meisten Antworten auf diese Frage bilden eine unendlich erreichbare Struktur im Speicher, anstatt tatsächlich Speicher zu verlieren.
2
was ist die idee Dass das mem nicht befreit werden kann? Ich schätze, dies würde eine native Ausführung für Sprachen erfordern, die mit Datenmüll gesammelt wurden oder Fehler ausnutzen.
Akostadinov
7
Ich sehe, wie Sprachen, die für das Golfen entworfen wurden, in diesem
Fall

Antworten:

89

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 perlbei 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

$ echo -n | valgrind perl
snip
==18517== 
==18517== LEAK SUMMARY:
==18517==    definitely lost: 8,134 bytes in 15 blocks
==18517==    indirectly lost: 154,523 bytes in 713 blocks
==18517==      possibly lost: 0 bytes in 0 blocks
==18517==    still reachable: 0 bytes in 0 blocks
==18517==         suppressed: 0 bytes in 0 blocks
==18517== 
==18517== For counts of detected and suppressed errors, rerun with: -v
==18517== ERROR SUMMARY: 15 errors from 15 contexts (suppressed: 0 from 0)

quelle
16
Ich mochte die Unlambda-Antwort, aber diese ist (meiner Meinung nach) zu lang, da es offensichtlich der Interpreter selbst ist, der den Speicher verliert, dh ich verliere definitiv: 7.742 Bytes in 14 Blöcken, wenn ich perl --versionauf meinem Computer laufe , obwohl es überhaupt nie dazu kommt, ein Programm auszuführen.
Zeppelin
11
@zeppelin: Einverstanden, aber nach unseren Regeln ist es die Implementierung, die die Sprache definiert. Wenn die Implementierung also Speicher verliert, verlieren alle Programme im Sprachspeicher. Ich bin mir nicht sicher, ob ich mit dieser Regel einverstanden bin, aber zu diesem Zeitpunkt ist sie zu tief verwurzelt, um sich wirklich ändern zu können.
8
Dies funktioniert auch in Node JS.
Dennis
6
Das fühlt sich wie eine neue Standardlücke an ...
Michael Hampton
46
Endlich ein Perl-Skript, das ich verstehen kann.
user11153
66

C 48 31 22 Bytes

Warnung: Führen Sie dies nicht zu oft aus.

Vielen Dank an Dennis für jede Menge Hilfe / Ideen!

f(k){shmget(k,1,512);}

Das geht noch einen Schritt weiter. shmgetreserviert 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:

f(){srand(time(0));shmget(rand(),1,512);}

Oder für 26 Bytes:

main(k){shmget(&k,1,512);}

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 &kbleibt sie immer gleich. Heutzutage ist ASLR normalerweise standardmäßig aktiviert.


Nachprüfung:

Mit können Sie ipcs -msehen, welcher gemeinsam genutzte Speicher auf Ihrem System vorhanden ist. Ich habe bereits vorhandene Einträge aus Gründen der Übersichtlichkeit entfernt:

$ cat leakMem.c 
f(k){shmget(k,1,512);}
int main(){f();}     
$ gcc leakMem.c -o leakMem
leakMem.c:1:1: warning: return type defaults to ‘int’ [-Wimplicit-int]
 f(k){shmget(k,1,512);}
 ^
leakMem.c: In function ‘f’:
leakMem.c:1:1: warning: type of ‘k’ defaults to ‘int’ [-Wimplicit-int]
leakMem.c:1:6: warning: implicit declaration of function ‘shmget’ [-Wimplicit-function-declaration]
 f(k){shmget(k,1,512);}
ppcg:ipcs -m

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      


$ ./leakMem 

$ ipcs -m

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      

0x0000007b 3375157    Riley      0          1          0  
Riley
quelle
1
@AndrewSavinykh Theoretisch könnte die shmid in einer Datei gespeichert worden sein und ein Programm könnte sie in Zukunft anhängen. So funktioniert Unix Shared Memory ...
tbodt
1
@AndrewSavinykh Shared Memory wird im Grunde genommen zu einer Ressource, die das Betriebssystem anderen Prozessen zur Verfügung stellen kann. Es ähnelt einer Datei, die sich im RAM befindet, und jeder Prozess, der den Namen (Schlüssel) kennt, hat Zugriff darauf, bis er gelöscht wird. Stellen Sie sich einen Prozess vor, der eine Zahl berechnet und im Speicher speichert und beendet, bevor der Prozess, der die Daten liest, eine Verbindung zum gemeinsam genutzten Speicher herstellt. In diesem Fall kann der zweite Prozess den Speicher nicht abrufen, wenn das Betriebssystem ihn freigibt.
Riley
35
Vielen Dank, dass Sie dies gepostet haben. Ich habe TIO gerade vor Shared Memory-Lecks geschützt.
Dennis
4
@Dennis Deshalb habe ich keinen TIO-Link gepostet. Ich wusste nicht, ob es geschützt war oder nicht.
Riley
12
Ich mag, wie Sie das Wort Problem verwenden , um das Szenario zu beschreiben, in dem das Programm weniger Speicher als beabsichtigt verliert.
Kasperd
40

Unlambda ( c-refcnt/unlambda), 1 Byte

i

Probieren 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-refcntist 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

$ wget ftp://ftp.madore.org/pub/madore/unlambda/unlambda-2.0.0.tar.gz
… Schnippel…
2017-02-18 18:11:08 (975 KB / s) - 'unlambda-2.0.0.tar.gz' gespeichert [492894]
$ tar xf unlambda-2.0.0.tar.gz 
$ cd unlambda-2.0.0 / c-refcnt /
$ gcc unlambda.c
$ echo -ni | valgrind ./a.out / dev / stdin
… Schnippel…
== 3417 == LEAK SUMMARY:
== 3417 == definitiv verloren: 40 Bytes in 1 Blöcken
== 3417 == indirekt verloren: 0 Bytes in 0 Blöcken
== 3417 == Möglicherweise verloren: 0 Bytes in 0 Blöcken
== 3417 == noch erreichbar: 0 Bytes in 0 Blöcken
== 3417 == unterdrückt: 0 Bytes in 0 Blöcken
== 3417 == 
== 3417 == Für die Anzahl der erkannten und unterdrückten Fehler erneut ausführen mit: -v
== 3417 == FEHLERZUSAMMENFASSUNG: 1 Fehler aus 1 Kontexten (unterdrückt: 0 aus 0)

quelle
39

TI-Basic, 12 Bytes

While 1
Goto A
End
Lbl A
Pause 

"... 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)

Timtech
quelle
7
Wow, ich denke ich erinnere mich daran. Ich sprang immer wieder aus meinen Schleifen in meinen alten Basisprogrammen und bemerkte, wie mein TI-84 + immer langsamer wurde ...
Ray
2
Ja, die meisten von uns kennen das Gefühl;) @RayKoopa
Timtech
13
+1 für Ti Basic. Ich habe den größten Teil meines neunten Schuljahres damit verbracht, diese Dinge zu programmieren.
Markasoftware
Brauchst du Pause am ende Sie könnten 2 Bytes sparen.
Kamoroso94
@ kamoroso94 Ich denke schon, denn "Wenn ein Programm beendet wird, wird das Leck gelöscht und es werden keine weiteren Probleme verursacht", also soll es das Programm am Beenden hindern.
Timtech
32

Python <3.6.5, 23 Bytes

property([]).__init__()

property.__init__undichte Stellen Verweise auf die Eigenschaft der alten fget, fset, fdel, und __doc__wenn Sie es auf einer bereits initialisiert nennen propertyInstanz. 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.)

user2357112
quelle
Wurde ein Fehlerbericht gesendet?
mbomb007
1
@ mbomb007: CPython-Problem 31787.
user2357112
27

C #, 34 Bytes

class L{~L(){for(;;)new L();}}

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 :

using System;
using System.Threading;
using System.Diagnostics;
class LeakTest {
    public static void Main() {
        SpawnLeakage();
        Console.WriteLine("{0}-: Objects may be freed now", DateTime.Now);
        // any managed object created in SpawbLeakage 
        //  is no longer accessible
        // The GC should take care of them

        // Now let's see
        MonitorGC();
    }
    public static void SpawnLeakage() {
        Console.WriteLine("{0}-: Creating 'leakage' object", DateTime.Now);
        L l = new L();
    }
    public static void MonitorGC() {
        while(true) {
            int top = Console.CursorTop;
            int left = Console.CursorLeft;
            Console.WriteLine(
                "{0}-: Total managed memory: {1} bytes",
                DateTime.Now,
                GC.GetTotalMemory(false)
            );
            Console.SetCursorPosition(left, top);
        }
    }
}

Ausgabe nach 10 Minuten:

2/19/2017 2:12:18 PM-: Creating 'leakage' object
2/19/2017 2:12:18 PM-: Objects may be freed now
2/19/2017 2:22:36 PM-: Total managed memory: 2684476624 bytes

Das sind 2 684 476 624 Bytes. Das Gesamtvolumen WorkingSetdes Prozesses betrug ca. 4,8 GB

Diese Antwort wurde von Eric Lipperts wunderbarem Artikel inspiriert: Wenn alles, was Sie wissen, falsch ist .

MrPaulch
quelle
Das ist faszinierend. Vergisst der Müllmann, dass es Dinge gibt, und verliert er sie aus diesem Grund aus den Augen? Ich weiß nicht viel über c #. Auch jetzt frage ich mich, was ist der Unterschied zwischen einer Bombe und einem Leck? Ich stelle mir ein ähnliches Fiasko durch den Aufruf einen Konstruktor erstellt werden kann aus dem Innern eines Konstruktor, oder eine unendliche recursing Funktion, der nie aufhört, obwohl technisch das System nie aus diesen Referenzen verliert, es läuft gerade aus dem Raum ...
don hell
1
Ein Konstruktor innerhalb eines Konstruktors würde einen Stapelüberlauf verursachen. Der Destruktor einer Instanz wird jedoch in einer flachen Hierarchie aufgerufen. Der GC verliert nie den Überblick über die Objekte. Immer wenn es versucht, sie zu zerstören, werden unbeabsichtigt neue Objekte erzeugt. Der Benutzercode hingegen hat keinen Zugriff auf diese Objekte. Außerdem können die genannten Inkonsistenzen auftreten, da der GC möglicherweise beschließt, ein Objekt zu zerstören, ohne seinen Destruktor aufzurufen.
MrPaulch
Wäre die Herausforderung nicht vollständig, wenn Sie nur verwenden class L{~L(){new L();}}? AFAIK ist das for(;;)einzige, was den Speicher schneller leckt , oder?
BgrWorker
1
Traurigerweise Nein. Da für jedes zerstörte Objekt nur eine neue Instanz erstellt wird, ist diese wiederum unzugänglich und für die Zerstörung markiert. Wiederholen. Es steht immer nur ein Objekt zur Zerstörung an. Keine wachsende Bevölkerung.
MrPaulch
2
Nicht wirklich. Irgendwann wird eine abgeschlossene ignoriert. Das entsprechende Objekt wird trotzdem gegessen.
MrPaulch
26

C (gcc) , 15 Bytes

f(){malloc(1);}

Nachprüfung

$ cat leak.c
f(){malloc(1);}
main(){f();}
$ gcc -g -o leak leak.c
leak.c: In function ‘f’:
leak.c:1:5: warning: incompatible implicit declaration of built-in function ‘malloc’ [enabled by default]
 f(){malloc(1);}
     ^
$ valgrind --leak-check=full ./leak
==32091== Memcheck, a memory error detector
==32091== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==32091== Using Valgrind-3.10.0 and LibVEX; rerun with -h for copyright info
==32091== Command: ./leak
==32091==
==32091==
==32091== HEAP SUMMARY:
==32091==     in use at exit: 1 bytes in 1 blocks
==32091==   total heap usage: 1 allocs, 0 frees, 1 bytes allocated
==32091==
==32091== 1 bytes in 1 blocks are definitely lost in loss record 1 of 1
==32091==    at 0x4C29110: malloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==32091==    by 0x40056A: f (leak.c:1)
==32091==    by 0x40057A: main (leak.c:2)
==32091==
==32091== LEAK SUMMARY:
==32091==    definitely lost: 1 bytes in 1 blocks
==32091==    indirectly lost: 0 bytes in 0 blocks
==32091==      possibly lost: 0 bytes in 0 blocks
==32091==    still reachable: 0 bytes in 0 blocks
==32091==         suppressed: 0 bytes in 0 blocks
==32091==
==32091== For counts of detected and suppressed errors, rerun with: -v
==32091== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
Dennis
quelle
26

Javascript, 14 Bytes

Golf gespielt

setInterval(0)

Registriert einen leeren Intervall-Handler mit einer Standardverzögerung und verwirft die resultierende Timer-ID (was ein Abbrechen unmöglich macht).

Bildbeschreibung hier eingeben

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.

Zeppelin
quelle
5
Haha, ich liebe es, dass du 'Golfed' eingegeben hast, und bin gespannt auf die ungolfed version
Martijn
9
es könnte so aussehenif(window && window.setInterval && typeof window.setInterval === 'function') { window.setInterval(0); }
Tschallacka
3
Tatsächlich ist es nicht unmöglich, dies zu stornieren: Intervall- (und Timeout-) IDs werden fortlaufend nummeriert, daher ist es ziemlich einfach, das Ding zu stornieren, indem Sie einfach clearIntervalmit einer inkrementierenden ID anrufen, bis Ihr Intervall abgelaufen ist. Zum Beispiel:for(let i=0;i<1e5;i++){try{clearInterval(i);}catch(ex){}}
user2428118
5
@ user2428118 Wie Zeppelin sagt, ist dies nicht "legitimer", als zu sagen, dass die C / C ++ - Lecks nicht "echt" sind, da Sie Anrufe anfree()
TripeHound
1
Wow, nicht viele Herausforderungen, bei denen JavaScript ein tatsächlicher Anwärter ist ...
Jared Smith
19

Java, 10 Bytes

Endlich eine kompetitive Antwort in Java!

Golf gespielt

". "::trim

Dies ist eine Methodenreferenz (gegen eine String-Konstante), die folgendermaßen verwendet werden kann:

Supplier<String> r = ". "::trim

Eine Literalzeichenfolge ". "wird automatisch zum globalen Pool internierter Zeichenfolgen hinzugefügt , wie von der java.lang.StringKlasse 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).

...

Ein ursprünglich leerer String-Pool wird privat von der Klasse String verwaltet.

Alle Literalzeichenfolgen und Zeichenfolgenwert-Konstantenausdrücke werden interniert. String-Literale werden in Abschnitt 3.10.5 der Java ™ -Sprachenspezifikation definiert.

...

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 .

Zeppelin
quelle
2
Ich habe dies für C # in Betracht gezogen, aber ich denke, es zählt nicht, denn wie Sie sagen, können Sie auf diesen Speicher zugreifen, indem Sie ein anderes Zeichenfolgenliteral einfügen. Ich wäre auch interessiert zu wissen, was passieren ("." + " ").intern()würde (wenn es sich um Benutzereingaben oder um W / W handelt, so dass wir Compiler-Optimierungen rabattieren).
VisualMelon
1
In der Tat ist der einzige Konsens bestenfalls schlank, ich bin nur fest auf der Seite "der Code sollte kompiliert werden". Ich bin immer noch nicht sicher , ob ich diese Lösung den Wortlaut der Frage gegeben kaufen (diese Strings nicht befreit werden, und sie können auch in normalen Betriebs Code gefunden werden , obwohl es unwahrscheinlich ist), aber ich lade andere zu ihrem eigenen Urteil zu fällen
VisualMelon
3
Diese Saite ist nicht einmal unzugänglich , geschweige denn durchgesickert. Wir können es jederzeit durch Internieren einer gleichen Zeichenfolge abrufen. Wenn dies zählen würde, wäre jede nicht verwendete globale Variable (private statische in Java) ein Leck. So werden Speicherverluste nicht definiert.
user2357112
3
@ user2357112 "... diese Zeichenfolge ist nicht einmal unzugänglich ..." Das sieht nur deshalb so aus, weil Sie den Code sehen. Angenommen, Sie haben diese Methodenreferenz X () als Argument für Ihren Code erhalten. Sie wissen, dass sie ein String-Literal innerhalb der Referenz zuordnet (und interniert), aber Sie wissen nicht genau, welches, es könnte "." Oder "123" sein. oder eine beliebige andere Zeichenfolge mit (im Allgemeinen) unbekannter Länge. Würden Sie bitte demonstrieren, wie Sie weiterhin darauf zugreifen können, oder den Eintrag im "internen" Pool freigeben, den es belegt?
Zeppelin
2
@ user2357112 Auf einem Computer mit einem begrenzten Speicher können Sie auf einen Wert zugreifen, der in einem beliebigen Teil des Speichers gespeichert ist. Dies simply by guessing it correctlybedeutet jedoch nicht, dass so etwas wie Speicherlecks nicht vorhanden sind. there's probably some way to use reflection to determine the string's contents tooKönntest du das demonstrieren? (Hinweis, String.intern () ist im nativen Code implementiert ).
Zeppelin
17

Rust, 52 Bytes

extern{fn malloc(_:u8);}fn main(){unsafe{malloc(9)}}

Ordnet dem System-Malloc einige Bytes zu. Dies setzt voraus, dass der falsche ABI akzeptabel ist.


Rust (theoretisch), 38 Bytes

fn main(){Box::into_raw(Box::new(1));}

Wir ordnen Speicher auf dem Heap zu, extrahieren einen rohen Zeiger und ignorieren ihn dann einfach, wodurch er effektiv verloren geht. ( Box::into_rawist dann kürzer std::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:

==10228== Memcheck, a memory error detector
==10228== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==10228== Using Valgrind-3.12.0.SVN and LibVEX; rerun with -h for copyright info
==10228== Command: ./1
==10228== 
==10228== 
==10228== HEAP SUMMARY:
==10228==     in use at exit: 9 bytes in 1 blocks
==10228==   total heap usage: 7 allocs, 6 frees, 2,009 bytes allocated
==10228== 
==10228== LEAK SUMMARY:
==10228==    definitely lost: 9 bytes in 1 blocks
==10228==    indirectly lost: 0 bytes in 0 blocks
==10228==      possibly lost: 0 bytes in 0 blocks
==10228==    still reachable: 0 bytes in 0 blocks
==10228==         suppressed: 0 bytes in 0 blocks
==10228== Rerun with --leak-check=full to see details of leaked memory
==10228== 
==10228== For counts of detected and suppressed errors, rerun with: -v
==10228== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
kennytm
quelle
genial. Beiträge wie diese haben mich dazu gebracht, Rust im letzten Jahr zu erkunden, definitiv eine der spaßigsten Sprachen, die ich je zu lernen versucht habe.
Don Bright
16

8086 ASM, 3 Bytes

In diesem Beispiel wird davon ausgegangen, dass eine C-Laufzeit eingebunden ist.

jmp _malloc

Hier wird zusammengestellt, e9 XX XXwo XX XXdie relative Adresse von ist_malloc

Dies ruft mallocauf, 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!

FUZxxl
quelle
Die normale Implementierung von malloc führt dazu, dass beim Beenden des Prozesses Speicher freigegeben wird.
Joshua,
@Joshua Ja, aber das ist implementierungsdefiniertes Verhalten.
FUZxxl
12

Viertens 6 Bytes

Golf gespielt

s" " *

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

%valgrind --leak-check=full gforth -e 's" " * bye'
...
==12788== HEAP SUMMARY:
==12788==     in use at exit: 223,855 bytes in 3,129 blocks
==12788==   total heap usage: 7,289 allocs, 4,160 frees, 552,500 bytes allocated
==12788== 
==12788== 1 bytes in 1 blocks are definitely lost in loss record 1 of 22
==12788==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==12788==    by 0x406E39: gforth_engine (in /usr/bin/gforth-0.7.0)
==12788==    by 0x41156A: gforth_go (in /usr/bin/gforth-0.7.0)
==12788==    by 0x403F9A: main (in /usr/bin/gforth-0.7.0)
==12788== 
...
==12818== LEAK SUMMARY:
==12818==    definitely lost: 1 bytes in 1 blocks
==12818==    indirectly lost: 0 bytes in 0 blocks
Zeppelin
quelle
10

gehe zu 45 Bytes

package main
func main(){go func(){for{}}()}

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"

Don Bright
quelle
Golf-Check: Dies ist 2 Bytes kürzer als der Aufruf C.malloc(8), da Sie müssenimport"C"
Riking
9

Java 1.3, 23 Bytes

void l(){new Thread();}

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.

public class Pcg110485 {

    static
    void l(){new Thread();}

    public static void main(String[] args) {

        while(true){
            l();
            System.gc();
            System.out.println(Runtime.getRuntime().freeMemory());
        }
    }
}
Olivier Grégoire
quelle
Da dies nur für bestimmte Java-Versionen funktioniert, sollten Sie stattdessen "Java 3" in Ihrem Header angeben.
5
Es gibt kein Java 3. Es ist Java 1.3. Es gab Java 1.0, 1.1, 2, 1.3, 1.4, 5, 6, 7, 8, 9. Seltsame Nummerierung, aber so ist es.
Olivier Grégoire
Das war auch meine Idee. Verdammt.
Magic Octopus Urn
8

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.

James Holderness
quelle
8

Schnelle 3, 38 Bytes

Neue Version:

class X{var x: X!};do{let x=X();x.x=x}

x hat einen starken Bezug zu sich selbst, daher wird die Zuordnung nicht aufgehoben, was zu einem Speicherverlust führt.

Alte Version:

class X{var y:Y!}
class Y{var x:X!}
do{let x=X();let y=Y();x.y=y;y.x=x}

xenthält einen starken Verweis auf yund umgekehrt. Somit wird keine Zuordnung aufgehoben, was zu einem Speicherverlust führt.

Daniel
quelle
Hmm, du kannst immer noch durch xund auf ydiese Erinnerung verweisen , so dass dies für mich nicht wirklich wie ein Leck aussieht (es sei denn, du zerstörst sie irgendwie).
Zeppelin
@zeppelin Die letzte Zeile könnte in eine Funktion eingeschlossen werden, um das zu beheben
NobodyNada
@NobodyNada, wenn ich die letzte Zeile in einen doBlock setzen würde, der das Problem beheben würde, das Zeppelin aufgeworfen hat, oder?
Daniel
@ Dopapp Ja; a dowürde auch funktionieren. Gute Idee!
NobodyNada
Es kann gekürzt werden, Sie brauchen keine zwei Klassen - X kann sich beziehen:class X{var x: X!};do{let x=X();x.x=x}
Sebastian Osiński
7

Delphi (Object Pascal) - 33 Byte

Erstellen eines Objekts ohne ein variables Vollkonsolenprogramm:

program;begin TObject.Create;end.

Wenn Sie FastMM4 im Projekt aktivieren, wird der Speicherverlust angezeigt:

Bildbeschreibung hier eingeben

RD
quelle
6

84bytes

class P{static void Main(){System.Runtime.InteropServices.Marshal.AllocHGlobal(1);}}

Dadurch wird genau 1 Byte nicht verwalteter Speicher zugewiesen, und dann geht der Speicher verloren, von IntPtrdem 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 (da Closeoder Disposenicht 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 IDisposablesin 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.

VisualMelon
quelle
1
System.IO.File.Create("a")GC.SuppressFinalize(System.IO.File.Create("a"))leckt nichts, wird aber da ausdrücklich gebeten, den Finalizer des FileStreamProduzierten nicht laufen zu lassen .
Jon Hanna
@ JonHanna ganz richtig. Meine IDisposable-Paranoia scheint mich überwunden zu haben.
VisualMelon
Möglicherweise haben Sie die Möglichkeit, mit System.Drawing.Bitmap ein GDI + -Leak zu verursachen. Weiß nicht, ob es zählt, weil es sich um eine Windows-Bibliothek handelt, die das Leck verursacht, nicht um das Programm selbst.
BgrWorker
@BgrWorker sie haben zweifellos auch einen Finalisten, und ich vermeide externe Bibliotheken in Code-Golf, weil ich nicht mit dem Konsens über deren Kosten einverstanden bin: Wenn Sie einen Weg finden, in den Sie sich sicher fühlen, können Sie gerne posten es in deiner eigenen Antwort!
VisualMelon
<!-- language: lang-c# -->Danke für diese & nette Antwort! (Es ist C #, also ich liebe es)
Metoniem
5

Faktor 13 Bytes

Factor verfügt über eine automatische Speicherverwaltung, bietet jedoch auch Zugriff auf einige libc-Funktionen:

1 malloc drop

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:

1 (malloc) drop

Testen von Lecks mit [ 1 malloc drop ] leaks.sagt:

| Disposable class | Instances |                    |
| malloc-ptr       | 1         | [ List instances ] |

Testen von Lecks mit [ 1 (malloc) drop ] leaks.sagt:

| Disposable class | Instances | |

Ach nein! Armer Faktor, jetzt hat es Alzheimer! D:

fede s.
quelle
5

Common Lisp (nur SBCL), 28 26 Bytes

sb-alien::(make-alien int)

Sie 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.

  1. 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 sowohl make-alienals auch intmit zu vermeiden sb-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.

  2. make-alien reserviert Speicher für einen bestimmten Typ und eine optionale Größe (unter Verwendung von malloc).

  3. Wenn Sie dies in der REPL ausführen, fügen Sie dies 0nach 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!

Core-Dump
quelle
1
Können Sie nicht 1(oder 2, 3usw.) anstelle von verwenden, ()damit Sie einen Wert zurückgeben 1? Es würde 1 Byte sparen. Auch ist diese Antwort nur REPL? Vielleicht, wenn Sie Code mit laden load, können Sie nichts ()oder nichts am Ende einfügen , weil es sowieso nicht zugänglich sein wird?
PrzemysławP
1
@ PrzemysławP Du hast in beiden Punkten Recht, ich habe es versucht evalund das funktioniert wie du gesagt hast. Danke vielmals!
Coredump
4

AutoIt , 39 Bytes

#include<Memory.au3>
_MemGlobalAlloc(1)

Ordnet ein Byte vom Heap zu. Da das von zurückgegebene Handle _MemGlobalAllocverworfen wird, gibt es keine Möglichkeit, diese Zuordnung explizit freizugeben.

mınxomaτ
quelle
3

C ++, 16 Bytes

main(){new int;}

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:

main(){[]{new int;}();}

Valgrind Ergebnis

(Es leckt tatsächlich)

==708== LEAK SUMMARY:
==708==    definitely lost: 4 bytes in 1 blocks
matovitch
quelle
@ WheatWizard verwende ich g++ 4.3.2(nicht die neueste) und es kompiliert ganz gut. intIch denke, dass kein Rückgabetyp standardmäßig ist. Mit -Wallich habe eine Warnung:plop.cpp:1: warning: ISO C++ forbids declaration of 'main' with no type
Matovitch
2
@ WheatWizard Entschuldigung, ich habe gerade gesehen, dass Sie das c ++ - Beispiel angegeben haben, um den Wettbewerb zu starten. Von reddit kommend habe ich nur die Antworten angeschaut und (seltsamerweise) kein C ++ gesehen. Ich fühle mich ein bisschen dumm. : /
Matovitch
Einigkeit besteht darin, dass Sie nur []{new int;}als C ++ - Funktion zählen können (die Herausforderung hat kein gesamtes Programm angegeben).
Toby Speight
3

Java (OpenJDK 9) , 322.220 Byte

import sun.misc.*;class Main{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);}}

Probieren 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

Serverfrog
quelle
Sie können eine Reihe von Bytes speichern, indem Sie die UnsafeInstanz 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);}}
Zeppelin
Und Sie können etwas mehr sparen, indem Sie den public static void maindurch einen statischen Initialisierer ersetzen static{try{}catch(Exception e){}}(der etwas schwieriger zu starten sein könnte, aber dennoch gültig und kompilierbar ist).
Zeppelin
yhea die Verwendung des Konstruktors wurde in einer Android-Version des von mir verwendeten Codes verwendet. Ich werde einige Dinge ändern, wenn ich @home bin, aber ich werde den Weg gehen, den Sie mit einer einzigen Anweisung
gegangen sind
Leerzeichen entfernen, astatt verwenden argsund public entfernen. tio.run/nexus/…
Pavel
true kann durch 1> 0 ersetzt werden
masterX244
3

c, 9 Bytes

main(){}

Beweis:

localhost/home/elronnd-10061: cat t.c
main(){}
localhost/home/elronnd-10062: valgrind gcc t.c
==10092== Memcheck, a memory error detector
==10092== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==10092== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==10092== Command: gcc t.c
==10092==
t.c:1:1: warning: return type defaults to ‘int’ [-Wimplicit-int]
 main(){}
 ^~~~
==10092==
==10092== HEAP SUMMARY:
==10092==     in use at exit: 178,518 bytes in 73 blocks
==10092==   total heap usage: 362 allocs, 289 frees, 230,415 bytes allocated
==10092==
==10092== LEAK SUMMARY:
==10092==    definitely lost: 4,659 bytes in 8 blocks
==10092==    indirectly lost: 82 bytes in 5 blocks
==10092==      possibly lost: 0 bytes in 0 blocks
==10092==    still reachable: 173,777 bytes in 60 blocks
==10092==         suppressed: 0 bytes in 0 blocks
==10092== Rerun with --leak-check=full to see details of leaked memory
==10092==
==10092== For counts of detected and suppressed errors, rerun with: -v
==10092== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Elronnd
quelle
1
Du verlierst nicht wirklich Gedächtnis; gccist. Dies sollte auch mit dem leeren Programm funktionieren. Versuchen Sie gcc src.c && valgrind ./a.out, was ein sauberes Ergebnis erzeugen sollte.
3

C #, 109 Bytes

public class P{static void Main({for(;;)System.Xml.Serialization.XmlSerializer.FromTypes(new[]{typeof(P)});}}

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 ich meinen Code nach PurchaseOrder durchsuche, finde ich diese Codezeile auf page_loadeiner meiner SeitenXmlSerializer serializer = new XmlSerializer(typeof(PurchaseOrder), new XmlRootAttribute(“”));

Dies scheint ein ziemlich unschuldiger Code zu sein. Wir schaffen eine XMLSerializerfür PurchaseOrder. Aber was passiert unter der Decke?

Wenn wir uns den XmlSerializerKonstruktor mit Reflector ansehen, stellen wir fest, dass er this.tempAssembly = XmlSerializer.GenerateTempAssembly(this.mapping, type, defaultNamespace, location, evidence);eine temporäre (dynamische) Assembly generiert. Jedes Mal, wenn dieser Code ausgeführt wird (dh wenn die Seite aufgerufen wird), wird eine neue Assembly generiert.

Der Grund, warum eine Assembly generiert wird, besteht darin, dass Funktionen zum Serialisieren und Deserialisieren generiert werden müssen, die sich irgendwo befinden müssen.

Ok, gut ... es entsteht eine Baugruppe, na und? Wenn wir damit fertig sind, sollte es einfach verschwinden, oder?

Nun, eine Assembly ist kein Objekt auf dem GC-Heap, der GC kennt keine Assemblys, sodass kein Müll gesammelt wird. Die einzige Möglichkeit, Assemblys in 1.0 und 1.1 zu entfernen, besteht darin, die App-Domäne zu entladen, in der sie sich befindet.

Und darin liegt das Problem, Dr. Watson.

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.

Fenster "Diagnosetools"

TheLethalCoder
quelle
2

C 30 Bytes

f(){int *i=malloc(sizeof(4));}

Valgrind Ergebnisse:

         ==26311== HEAP SUMMARY:
         ==26311==     in use at exit: 4 bytes in 1 blocks
         ==26311==   total heap usage: 1 allocs, 0 frees, 4 bytes allocated
         ==26311== 
         ==26311== LEAK SUMMARY:
         ==26311==    definitely lost: 4 bytes in 1 blocks
         ==26311==    indirectly lost: 0 bytes in 0 blocks
         ==26311==      possibly lost: 0 bytes in 0 blocks
         ==26311==    still reachable: 0 bytes in 0 blocks
         ==26311==         suppressed: 0 bytes in 0 blocks
         ==26311== Rerun with --leak-check=full to see details of leaked memory
Abel Tom
quelle
2
Ist es möglich, stattdessen einfach zu tun main(){malloc(1);}?
kirbyfan64sos
@ Ja es ist! Aber es wurde bereits gepostet!
Abel Tom
2

Dart, 76 Bytes

import'dart:async';main()=>new Stream.periodic(Duration.ZERO).listen((_){});

Ein bisschen wie die JavaScript-Antwort. Wenn Sie .listenein 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().futurefunktioniert nicht, weil das Verwenden von wait das Gleiche ist wie doing new 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.

Dwayne Slater
quelle
Wenn Ihr Hauptprogramm weiterläuft und andere Dinge erledigt, könnte der Müllsammler dann jemals das Isolat stoppen? Ich frage, weil mein Goroutine-Beispiel ähnlich klingt. Ich bin davon ausgegangen, dass die Tatsache, dass das Programm beendet wird und den gesamten Speicher an das Betriebssystem zurückgibt, nicht unbedingt bedeutet, dass es nicht durchgesickert ist.
Don Bright
2

Schnell, 12 Bytes

[3,5][0...0]

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. Die 3und 5sind einfach beliebig.

Indizieren (Indizieren) von und Array<T>erzeugt eine ArraySlice<T>. Ein ArraySlice<T>ist ein Blick in den Speicher des Arrays, aus dem es erstellt wurde.

[3,5][0...0]erzeugt eine ArraySlice<Int>, deren Wert ist [3]. Beachten Sie, dass das 3in diesem Slice das gleiche 3Element wie 3im Arrayoben 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 2Speichergröß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:

Array([3,5][0...0])
Alexander
quelle
2

Lösung 1: C (Mac OS X x86_64), 109 Byte

Die Quelle für golf_sol1.c

main[]={142510920,2336753547,3505849471,284148040,2370322315,2314740852,1351437506,1208291319,914962059,195};

Das obige Programm muss mit Ausführungszugriff auf das __DATA-Segment kompiliert werden.

clang golf_sol1.c -o golf_sol1 -Xlinker -segprot -Xlinker __DATA -Xlinker rwx -Xlinker rwx

Führen Sie dann zur Ausführung des Programms Folgendes aus:

./golf_sol1 $(ruby -e 'puts "\xf5\xff\xff\xfe\xff\xff\x44\x82\x57\x7d\xff\x7f"')

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).

                                VIRTUAL   REGION 
REGION TYPE                        SIZE    COUNT (non-coalesced) 
===========                     =======  ======= 
Kernel Alloc Once                    4K        2 
MALLOC guard page                   16K        4 
MALLOC metadata                   16.2M        7 
MALLOC_SMALL                      8192K        2         see MALLOC ZONE table below
MALLOC_TINY                       1024K        2         see MALLOC ZONE table below
STACK GUARD                       56.0M        2 
Stack                             8192K        3 
VM_ALLOCATE (reserved)             520K        3         reserved VM address space (unallocated)
__DATA                             684K       42 
__LINKEDIT                        70.8M        4 
__TEXT                            5960K       44 
shared memory                        8K        3 
===========                     =======  ======= 
TOTAL                            167.0M      106 
TOTAL, minus reserved VM space   166.5M      106 

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 mainzum 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:

movq 8(%rsi), %rdi
movl (%rdi), %eax
movq 4(%rdi), %rdi
notl %eax
shrq $16, %rdi
movl (%rdi), %edi
leaq -0x8(%rsp), %rsi
movl %eax, %edx
leaq -9(%rax), %r10
syscall
movq (%rsi), %rsi
movl %esi, (%rsi)
ret

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

main[]={141986632,10937,1032669184,2,42227};

Das obige Programm muss mit Ausführungszugriff auf das __DATA-Segment kompiliert werden.

clang golf_sol2.c -o golf_sol2 -Xlinker -segprot -Xlinker __DATA -Xlinker rwx -Xlinker rwx

Führen Sie dann zur Ausführung des Programms Folgendes aus:

./golf_sol2 $(ruby -e 'puts "\xb8\xf5\xff\xff\xfe\xf7\xd0\x48\xbf\xff\xff\x44\x82\x57\x7d\xff\x7f\x48\xc1\xef\x10\x8b\x3f\x48\x8d\x74\x24\xf8\x89\xc2\x4c\x8d\x50\xf7\x0f\x05\x48\x8b\x36\x89\x36\xc3"')

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:

movq 8(%rsi), %rsi
movl $42, %ecx
leaq 2(%rip), %rdi
rep movsb (%rsi), (%rdi)

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:

movl $0xfefffff5, %eax
notl %eax
movq $0x7fff7d578244ffff, %rdi
shrq $16, %rdi
movl (%rdi), %edi
leaq -0x8(%rsp), %rsi
movl %eax, %edx
leaq -9(%rax), %r10
syscall
movq (%rsi), %rsi
movl %esi, (%rsi)
ret

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:

main=65259;

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:

main(){}


==55263== LEAK SUMMARY:
==55263==    definitely lost: 696 bytes in 17 blocks
==55263==    indirectly lost: 17,722 bytes in 128 blocks
==55263==      possibly lost: 0 bytes in 0 blocks
==55263==    still reachable: 0 bytes in 0 blocks
==55263==         suppressed: 16,316 bytes in 272 blocks
ScrimpyCat
quelle
2

Nur Englisch , 71 70 58 35 Bytes

1 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:

To x:
Allocate memory for a thing.

Ungolfed-Version, die ein vollständiges Programm ist, eine Untertypdefinition verwendet und keinen Speicher verliert:

A bogon is a thing.

To do something:
  Allocate memory for a bogon.
  Destroy the bogon.

To run:
  Start up.
  Do something.
  Shut down.

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.

Jaspis
quelle