Perdita di memoria nel minor numero di byte possibile


79

Il tuo compito è scrivere codice che perda almeno un byte di memoria nel minor numero di byte possibile. La memoria deve essere trapelata non solo allocata .

La memoria perduta è la memoria che il programma alloca ma perde la possibilità di accedere prima che possa deallocare correttamente la memoria. Per la maggior parte delle lingue di alto livello, questa memoria deve essere allocata sull'heap.

Un esempio in C ++ sarebbe il seguente programma:

int main(){new int;}

Questo fa un new intheap senza un puntatore ad esso. Questa memoria perde immediatamente perché non abbiamo modo di accedervi.

Ecco come potrebbe apparire un riepilogo delle perdite di Valgrind :

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

Molte lingue hanno un debugger di memoria (come Valgrind ) se è possibile includere l'output di tale debugger per confermare la perdita di memoria.

L'obiettivo è ridurre al minimo il numero di byte nella sorgente.


2
Forse potresti avere diversi intervalli di quantità trapelati e, a seconda di quanto perdi, perdi il x% del conteggio dei byte
Christopher,

11
@ChristopherPeart Per uno non sono un fan dei bonus per le sfide e per due, come hai già dimostrato, è molto facile perdere la memoria illimitata.
Wheat Wizard

1
Correlato . Non è un duplicato, tuttavia, perché la maggior parte delle risposte a questa domanda formano una struttura infinita raggiungibile in memoria piuttosto che perdere effettivamente memoria.

2
qual è l'idea? Che il mem non può essere liberato? Immagino che ciò richiederebbe l'esecuzione nativa per le lingue raccolte in modo inutile o lo sfruttamento di bug.
Akostadinov,

7
Vedo come le lingue progettate per il golf falliscono miseramente in questo ...
Kh40tiK,

Risposte:


89

Perl (5.22.2), 0 byte

Provalo online!

Sapevo che ci sarebbe stata qualche lingua là fuori che trapelava memoria su un programma vuoto. Mi aspettavo che fosse un esolang, ma risulta che perlperde memoria su qualsiasi programma. (Suppongo che questo sia intenzionale, perché liberare memoria se sai che stai per uscire comunque fa solo perdere tempo; come tale, al giorno d'oggi la raccomandazione comune è quella di perdere la memoria rimanente una volta che sei nelle routine di uscita del tuo programma .)

Verifica

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

16
Mi è piaciuta la risposta di Unlambda, ma questa è (IMHO) troppo lunga, dato che è ovviamente l'interprete stesso che perde la memoria, vale a dire che `mi perdo definitivamente: 7.742 byte in 14 blocchi` quando corro perl --versionsulla mia macchina , nonostante non riesca mai a eseguire alcun programma.
Zeppelin,

11
@zeppelin: D'accordo, ma secondo le nostre regole, è l'implementazione che definisce il linguaggio, quindi se l'implementazione perde memoria, tutti i programmi nella lingua perdono memoria. Non sono necessariamente sicuro di essere d'accordo con quella regola, ma a questo punto è troppo radicato per poter davvero cambiare.

8
Questo funziona anche in Node JS.
Dennis,

6
Sembra una nuova scappatoia in preparazione ...
Michael Hampton,

46
Finalmente uno script Perl che posso capire.
user11153,

66

C, 48 31 22 byte

Avviso: non eseguirlo troppe volte.

Grazie a Dennis per un sacco di aiuto / idee!

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

Questo va oltre. shmgetalloca memoria condivisa che non è deallocata al termine del programma. Usa una chiave per identificare la memoria, quindi usiamo un int non inizializzato. Questo è un comportamento tecnicamente indefinito, ma praticamente significa che usiamo il valore che è appena sopra la parte superiore dello stack quando viene chiamato. Questo verrà scritto la prossima volta che qualcosa verrà aggiunto allo stack, quindi perderemo la chiave.


L'unico caso in cui questo non funziona è se riesci a capire cosa c'era prima nello stack. Per altri 19 byte puoi evitare questo problema:

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

Oppure, per 26 byte:

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

Ma con questo, la memoria perde quando il programma termina. Durante l'esecuzione del programma ha accesso alla memoria che è contro le regole, ma dopo che il programma termina perdiamo l'accesso alla chiave e la memoria è ancora allocata. Ciò richiede la randomizzazione del layout dello spazio degli indirizzi (ASLR), altrimenti &ksarà sempre lo stesso. Oggi ASLR è in genere attivo per impostazione predefinita.


Verifica:

Puoi usare ipcs -mper vedere quale memoria condivisa esiste sul tuo sistema. Ho rimosso voci preesistenti per chiarezza:

$ 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  

1
@AndrewSavinykh Teoricamente, lo shmid avrebbe potuto essere archiviato in un file e un programma potrebbe collegarsi ad esso in futuro. Ecco come funziona la memoria condivisa unix ...
tbodt

1
@AndrewSavinykh La memoria condivisa diventa sostanzialmente una risorsa che il sistema operativo può fornire ad altri processi. È simile a un file che vive nella RAM e qualsiasi processo che conosce il nome (chiave) ha accesso ad esso fino a quando non viene eliminato. Immagina un processo che calcola un numero e lo memorizza nella memoria ed esce prima che il processo che legge i dati si connetta alla memoria condivisa. In questo caso, se il sistema operativo libera la memoria, il secondo processo non può ottenerlo.
Riley,

35
grazie per aver postato questo. Ho appena protetto TIO da perdite di memoria condivisa.
Dennis,

4
@Dennis Ecco perché non ho pubblicato un link TIO. Non sapevo se fosse protetto o no.
Riley,

12
Mi piace come usi la parola problema per descrivere lo scenario in cui il programma perde meno memoria del previsto.
Kasperd,

40

Unlambda ( c-refcnt/unlambda), 1 byte

i

Provalo online!

Questa è davvero una sfida per trovare un interprete preesistente che perde memoria su programmi molto semplici. In questo caso, ho usato Unlambda. C'è più di un interprete Unlambda ufficiale, ma c-refcntè uno dei più facili da costruire, e qui ha la proprietà utile che perde memoria quando un programma viene eseguito correttamente. Quindi tutto quello che dovevo dare qui era il programma Unlambda legale più semplice possibile, una no-op. (Si noti che il programma vuoto non funziona qui; la memoria è ancora raggiungibile al momento dell'interruzione dell'interprete.)

Verifica

$ wget ftp://ftp.madore.org/pub/madore/unlambda/unlambda-2.0.0.tar.gz
... snip ...
18/02/2017 18:11:08 (975 KB / s) - 'unlambda-2.0.0.tar.gz' salvato [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
... snip ...
== 3417 == SINTESI DELLE PERDITE:
== 3417 == definitivamente perso: 40 byte in 1 blocco
== 3417 == indirettamente perso: 0 byte in 0 blocchi
== 3417 == eventualmente perso: 0 byte in 0 blocchi
== 3417 == ancora raggiungibile: 0 byte in 0 blocchi
== 3417 == soppresso: 0 byte in 0 blocchi
== 3417 == 
== 3417 == Per il conteggio degli errori rilevati e eliminati, rieseguire con: -v
== 3417 == SINTESI DELL'ERRORE: 1 errori da 1 contesti (soppresso: 0 da 0)

39

TI-Basic, 12 byte

While 1
Goto A
End
Lbl A
Pause 

"... una perdita di memoria è dove usi un Goto / Lbl all'interno di un loop o If condizionale (tutto ciò che ha un comando End) per saltare fuori da quella struttura di controllo prima che il comando End sia raggiunto ..." (altro)


7
Wow, penso di ricordarmelo. Continuavo a saltare fuori dai miei loop nei miei vecchi programmi di base e notavo come la mia TI-84 + diventava sempre più lenta ...
Ray

2
Sì, molti di noi conoscono la sensazione;) @RayKoopa
Timtech,

13
+1 per Ti Basic. Ho trascorso gran parte del mio anno di terza media a programmare quelle cose.
markasoftware,

Ne hai bisogno Pause alla fine? È possibile salvare 2 byte.
kamoroso94,

@ kamoroso94 Penso di sì, perché "Se un programma è terminato la perdita viene cancellata e non causerà ulteriori problemi", quindi è per fermare il programma.
Timtech,

32

Python <3.6.5, 23 byte

property([]).__init__()

property.__init__perdite riferimenti alla struttura vecchia fget, fset, fdel, e __doc__se lo si chiama in un già-inizializzato propertyesempio. Questo è un bug, eventualmente riportato come parte del numero 31787 di CPython e corretto in Python 3.6.5 e Python 3.7.0 . (Inoltre, sì, property([])è una cosa che puoi fare.)


È stata inviata una segnalazione di bug?
mbomb007,


27

C #, 34 byte

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

Questa soluzione non richiede l'heap. Ha solo bisogno di un vero GC ( Garbage Collector ) che lavora sodo .

In sostanza trasforma il GC nel suo stesso nemico.

Spiegazione

Ogni volta che viene chiamato il distruttore , crea nuove istanze di questa classe malvagia finché scade il timeout e dice al GC di abbandonare quell'oggetto senza aspettare che il distruttore finisca. Da allora sono state create migliaia di nuove istanze.

La "malvagità" di questo è, più duro è il GC, più questo ti esploderà in faccia.

Dichiarazione di non responsabilità : il tuo GC potrebbe essere più intelligente del mio. Altre circostanze nel programma possono far sì che il GC ignori il primo oggetto o il suo distruttore. In questi casi questo non esploderà. Ma in molte varianti lo farà . L'aggiunta di alcuni byte qua e là potrebbe garantire una perdita per ogni possibile circostanza. Beh tranne forse per l'interruttore di alimentazione.

Test

Ecco una suite di test :

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);
        }
    }
}

Uscita dopo 10 minuti:

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

Sono 2 684 476 624 byte. Il totale WorkingSetdel processo è stato di circa 4,8 GB

Questa risposta è stata ispirata dal meraviglioso articolo di Eric Lippert: quando tutto ciò che sai è sbagliato .


Questo è affascinante. Il garbage collector "dimentica" che alcune cose esistono e ne perde traccia a causa di ciò? Non so molto di c #. Inoltre ora mi chiedo, qual è la differenza tra una bomba e una fuga? Immagino che un simile fiasco possa essere creato chiamando un costruttore dall'interno di un costruttore, o avendo una funzione ricorsiva infinita che non si ferma mai, anche se tecnicamente il sistema non perde mai traccia di quei riferimenti, si esaurisce lo spazio ...
don luminoso

1
Un costruttore all'interno di un costruttore causerebbe un overflow dello stack. Ma il distruttore di un'istanza viene chiamato in una gerarchia piatta. Il GC in realtà non perde mai traccia degli oggetti. Proprio ogni volta che cerca di distruggerli crea inconsapevolmente nuovi oggetti. Il codice utente invece non ha accesso a detti oggetti. Anche le incoerenze menzionate possono sorgere poiché il GC può decidere di distruggere un oggetto senza chiamare il suo distruttore.
Mr Paul Paul,

La sfida non sarebbe completa semplicemente usando class L{~L(){new L();}}? AFAIK è l' for(;;)unico che fa perdere memoria più velocemente, giusto?
BgrWorker,

1
Purtroppo no. Poiché per ogni oggetto distrutto verrà creata solo una nuova istanza, che sarà nuovamente inaccessibile e contrassegnata per la distruzione. Ripetere. Solo un oggetto sarà in attesa di distruzione. Nessuna popolazione in aumento.
Mr Paul Paul,

2
Non proprio. Alla fine uno finalizzato verrà ignorato. L'oggetto corrispondente verrà mangiato a prescindere.
Mr Paul Paul,

26

C (gcc) , 15 byte

f(){malloc(1);}

Verifica

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

26

Javascript, 14 byte

golfed

setInterval(0)

Registra un gestore di intervalli vuoto con un ritardo predefinito, scartando l'ID timer risultante (rendendo impossibile l'annullamento).

inserisci qui la descrizione dell'immagine

Ho usato un intervallo non predefinito, per creare diversi milioni di timer, per illustrare la perdita, poiché l'utilizzo di un intervallo predefinito consuma la CPU come un matto.


5
Haha, adoro il fatto che tu abbia digitato "Golfed", mi incuriosisce della versione ungolfed
Martijn

9
potrebbe apparire cosìif(window && window.setInterval && typeof window.setInterval === 'function') { window.setInterval(0); }
Tschallacka,

3
In realtà, questo non è impossibile da cancellare: gli ID intervallo (e timeout) sono numerati in sequenza, quindi è abbastanza facile annullare la cosa semplicemente chiamando clearIntervalcon un ID incrementale fino a quando l'intervallo non è andato. Ad esempio:for(let i=0;i<1e5;i++){try{clearInterval(i);}catch(ex){}}
user2428118,

5
@ user2428118 Come dice zeppelin, questo non è più "legittimo" che dire che le perdite di C / C ++ non sono "reali" perché potresti chiamare free()
brutalmente

1
Wow, non molte sfide in cui JavaScript è un vero contendente ...
Jared Smith,

19

Java, 10 byte

Finalmente una risposta competitiva in Java!

golfed

". "::trim

Questo è un riferimento al metodo (rispetto a una costante di stringa), che può essere utilizzato in questo modo:

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

Una stringa letterale ". "verrà automaticamente aggiunta al pool di stringhe internato globale , come gestito dalla java.lang.Stringclasse, e man mano che la tagliamo immediatamente, il riferimento ad essa non può essere riutilizzato ulteriormente nel codice (a meno che non si dichiari di nuovo esattamente la stessa stringa).

...

Un pool di stringhe, inizialmente vuoto, viene gestito privatamente dalla classe String.

Tutte le stringhe letterali e le espressioni costanti con valore di stringa vengono internate. I valori letterali di stringa sono definiti nella sezione 3.10.5 di The Java ™ Language Specification.

...

https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#intern--

Puoi trasformarlo in una perdita di memoria di "livello di produzione", aggiungendo la stringa a se stesso e quindi invocando il metodo intern () in modo esplicito, in un ciclo.


2
L'ho considerato per C # ... ma non credo che conti, perché come dici puoi accedere a quella memoria includendo un'altra stringa letterale. Sarei anche interessato a sapere cosa ("." + " ").intern()farebbe (se fossero input dell'utente o w / e, quindi scartiamo le ottimizzazioni del compilatore).
VisualMelon,

1
In effetti, l'unico consenso è, nella migliore delle ipotesi, snello, sono solo fermamente sul lato "compilare il codice". Non sono ancora sicuro di acquistare questa soluzione, dato il testo sulla domanda (queste stringhe non possono essere liberate e possono essere trovate nel normale codice operativo anche se è improbabile), ma invito altri ad esprimere il proprio giudizio
VisualMelon,

3
Quella stringa non è nemmeno inaccessibile , figuriamoci trapelata. Possiamo recuperarlo in qualsiasi momento internando una stringa uguale. Se questo dovesse contare, qualsiasi variabile globale inutilizzata (statica privata in Java) sarebbe una perdita. Non è così che vengono definite le perdite di memoria.
user2357112

3
@ user2357112 "... Quella stringa non è nemmeno inaccessibile ..." Sembra ovvio solo perché vedi il codice. Ora considera di avere questo metodo di riferimento X () come argomento per il tuo codice, sai che alloca (e intern) una stringa letterale all'interno, ma non sai quale esattamente, potrebbe essere "." O "123" o qualsiasi altra stringa di lunghezza (generalmente) sconosciuta. Dimostrerebbe come è ancora possibile accedervi o dislocare la voce nel pool di "stagisti" che occupa?
Zeppelin,

2
@ user2357112 Su una macchina con una memoria finita, è possibile accedere a un valore memorizzato in qualsiasi pezzo di memoria simply by guessing it correctly, ma ciò non significa che non esistano cose come perdite di memoria. there's probably some way to use reflection to determine the string's contents toopotresti dimostrarlo? (suggerimento, String.intern () è implementato nel codice nativo ).
Zeppelin,

17

Ruggine, 52 byte

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

Alloca alcuni byte con il sistema malloc. Ciò presuppone che l'ABI errato sia accettabile.


Ruggine (in teoria), 38 byte

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

Allochiamo la memoria sull'heap, estraiamo un puntatore non elaborato e quindi semplicemente la ignoriamo, perdendola efficacemente. ( Box::into_rawè più breve allora std::mem::forget).

Tuttavia, per impostazione predefinita Rust utilizza jemalloc, che valgrind non è in grado di rilevare alcuna perdita . Potremmo passare all'allocatore di sistema ma questo aggiunge 50 byte e richiede di notte. Grazie mille per la sicurezza della memoria.


Uscita del primo programma:

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

eccezionale. post come questo mi hanno portato a esplorare Rust negli ultimi anni, sicuramente una delle lingue più divertenti che ho cercato di imparare.
don luminoso

16

8086 ASM, 3 byte

Questo esempio presuppone che un runtime C sia collegato.

jmp _malloc

questo si riunisce e9 XX XXdove XX XXè l'indirizzo relativo di_malloc

Questo invoca mallocper allocare una quantità imprevedibile di memoria e quindi ritorna immediatamente, terminando i processi. Su alcuni sistemi operativi come DOS, la memoria potrebbe non essere affatto recuperabile fino al riavvio del sistema!


La normale implementazione di malloc comporterà la liberazione della memoria all'uscita dal processo.
Joshua,

@Joshua Sì, ma questo è un comportamento definito dall'implementazione.
FUZxxl,

12

Avanti, 6 byte

golfed

s" " *

Alloca una stringa vuota con s" ", lasciando il relativo indirizzo e lunghezza (0) nello stack, quindi li moltiplica (con conseguente perdita di un indirizzo di memoria).

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

10

andare 45 byte

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

questo crea una goroutine anonima con un ciclo infinito al suo interno. il programma può continuare a funzionare normalmente, poiché avviare la goroutine è un po 'come generare un piccolo thread in esecuzione contemporaneamente, ma il programma non ha modo di recuperare la memoria allocata per la goroutine. il Garbage Collector non lo raccoglierà mai poiché è ancora in esecuzione. alcune persone chiamano questo "fuoriuscita una goroutine"


golf check: questo è 2 byte più corto di una chiamata C.malloc(8), poiché è necessarioimport"C"
Riking

9

Java 1.3, 23 byte

void l(){new Thread();}

Creare un thread ma non avviarlo. Il thread è registrato nel pool di thread interno, ma non verrà mai avviato, quindi non è mai terminato e quindi non è mai candidato per il GC. È un oggetto irrecuperabile, bloccato nei limbo Java.

È un bug in Java fino alla 1.3 inclusa poiché è stata riparata in seguito.

analisi

Il seguente programma si assicura di inquinare la memoria con nuovi oggetti thread e mostra uno spazio di memoria libera decrescente. Per motivi di prove di perdite, eseguo intensamente il GC.

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());
        }
    }
}

Dato che funziona solo su specifiche versioni di Java, dovresti dire "Java 3" nella tua intestazione.

5
Non esiste Java 3. È Java 1.3. C'era Java 1.0, 1.1, 2, 1.3, 1.4, 5, 6, 7, 8, 9. Strana numerazione, ma è così.
Olivier Grégoire,

Anche questa era la mia idea. Dannazione.
Magic Octopus Urn,

8

Befunge ( funghi ), 1 byte

$

Questo può dipendere dalla piattaforma e dalla versione (ho testato solo con la versione 1.0.4 su Windows), ma i funghi sono stati storicamente un interprete molto permissivo. Il $comando (drop) non dovrebbe fare nulla su uno stack vuoto, ma il looping su questo codice riesce in qualche modo a perdere molta memoria molto rapidamente. Nel giro di pochi secondi avrà esaurito un paio di concerti e si bloccherà con un errore "memoria insufficiente".

Nota che non deve essere necessariamente un $comando, praticamente qualsiasi cosa farebbe. Tuttavia, non funzionerà con un file sorgente vuoto. Deve esserci almeno un'operazione.


8

Swift 3, 38 byte

Nuova versione:

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

x ha un forte riferimento a se stesso, quindi non verrà deallocato, causando una perdita di memoria.

Vecchia versione:

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

xcontiene un forte riferimento a ye viceversa. Pertanto, nessuno dei due verrà deallocato, portando a una perdita di memoria.


Hmm, puoi ancora fare riferimento a quel ricordo xe y, quindi non mi sembra davvero una perdita (a meno che tu non li distrugga in qualche modo).
Zeppelin,

@zeppelin L'ultima riga potrebbe essere racchiusa in una funzione per risolvere il problema
NobodyNada,

@NobodyNada, se dovessi mettere l'ultima riga in un doblocco per risolvere il problema sollevato da zeppelin giusto?
Daniel,

@Dopapp Sì; un dofunzionerebbe pure. Buona idea!
NobodyNada,

Può essere abbreviato, non sono necessarie due classi - X può contenere riferimenti a se stesso:class X{var x: X!};do{let x=X();x.x=x}
Sebastian Osiński

7

Delphi (Object Pascal) - 33 byte

Creazione di un oggetto senza un programma di console completo variabile:

program;begin TObject.Create;end.

L'abilitazione di FastMM4 nel progetto mostrerà la perdita di memoria:

inserisci qui la descrizione dell'immagine


6

C # - 84 byte

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

Questo alloca esattamente 1 byte di memoria non gestita e quindi perde il IntPtr, che credo sia l'unico modo per ottenerlo o liberarlo. Puoi testarlo inserendolo in un ciclo e aspettando che l'applicazione si blocchi (potrebbe essere necessario aggiungere alcuni zeri per accelerare le cose).

Ho considerato System.IO.File.Create("a");e così, ma io non sono convinto che questi siano necessariamente le perdite di memoria, come l'applicazione stessa sarà raccogliere la memoria, è il sistema operativo sottostante che potrebbe perdere (perché Closeo Disposenon sono stati chiamati). Anche le cose di accesso ai file richiedono autorizzazioni per il file system e nessuno vuole fare affidamento su quelle. E si scopre che questo non perderà comunque, perché non c'è nulla che fermi la chiamata del finalizzatore (che è possibile liberare le risorse sottostanti), che il framework include per mitigare questo tipo di errore di giudizio (in una certa misura), e confondere i programmatori con un blocco dei file apparentemente non deterministico (se sei un cinico). Grazie a Jon Hanna per avermi messo in chiaro.

Sono un po 'deluso dal fatto che non riesco a trovare un modo più breve. Il GC .NET funziona, non riesco a pensare a nessuno IDisposablesin mscorlib che perderà sicuramente (e in effetti sembrano tutti avere finalizzatori, che fastidio) , non sono a conoscenza di nessun altro modo per allocare memoria non gestita (a parte PInvoke ), e la riflessione assicura qualsiasi cosa con un riferimento ad esso (a prescindere dalla semantica del linguaggio (ad esempio, i membri privati o classi senza di accesso)) possono essere trovati.


1
System.IO.File.Create("a")non perderà nulla, ma GC.SuppressFinalize(System.IO.File.Create("a"))sarà come viene esplicitamente chiesto di non eseguire il finalizzatore del FileStreamprodotto.
Jon Hanna,

@JonHanna abbastanza bene. La mia paranoia IDisposable sembra aver avuto la meglio su di me.
VisualMelon,

Potresti avere la possibilità di perdere GDI + usando System.Drawing.Bitmap. Non so se conta perché è una libreria di Windows che causa la perdita, non il programma stesso.
BgrWorker,

@BgrWorker hanno senza dubbio anche un finalizzatore, e tendo ad evitare le biblioteche esterne nel code-golf perché non sono d'accordo con il consenso sui costi: se riesci a trovare un modo in cui confidare, sentiti libero di pubblicare nella tua risposta!
VisualMelon,

<!-- language: lang-c# -->Grazie per questa e bella risposta! (È C # quindi lo adoro)
Metoniem,

5

Fattore , 13 byte

Factor ha una gestione automatica della memoria, ma dà anche accesso ad alcune funzionalità libc:

1 malloc drop

Alloca manualmente 1 byte di memoria, restituisce il suo indirizzo e lo rilascia.

malloc registra effettivamente una copia per tenere traccia delle perdite di memoria e delle doppie liberazioni, ma identificare quello che hai trapelato non è un compito facile.

Se preferisci assicurarti di perdere davvero quel riferimento:

1 (malloc) drop

Test di perdite con [ 1 malloc drop ] leaks.dice:

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

Test di perdite con [ 1 (malloc) drop ] leaks.dice:

| Disposable class | Instances | |

Oh no! Fattore scarso, ora ha l'Alzheimer! D:


4

AutoIt , 39 byte

#include<Memory.au3>
_MemGlobalAlloc(1)

Alloca un byte dall'heap. Poiché l'handle restituito da _MemGlobalAllocviene eliminato, non è possibile liberare esplicitamente tale allocazione.


4

Common Lisp (solo SBCL), 28 26 byte

sb-alien::(make-alien int)

Lo esegui così sbcl --eval 'sb-alien::(make-alien int)':; non viene stampato né restituito nulla, ma si verifica l'allocazione di memoria. Se avvolgo il modulo all'interno di a (print ...), il puntatore viene visualizzato nel REPL.

  1. package::(form)è una notazione speciale in SBCL per l'associazione temporanea del pacchetto corrente durante la lettura di un modulo. Questo è usato qui per evitare il prefisso sia con make-alienche intcon sb-alien. Penso che sarebbe ingannevole supporre che il pacchetto corrente sia impostato su questo, perché non è il caso all'avvio.

  2. make-alien alloca memoria per un determinato tipo e una dimensione opzionale (usando malloc).

  3. Quando si esegue questo nel REPL, aggiungere 0dopo l'allocazione in modo che il REPL non restituisca il puntatore, ma quel valore. In caso contrario, che sarebbe non essere una vera perdita, perché il REPL ricorda gli ultimi tre valori restituiti (vedi *, **,*** ) e abbiamo potuto ancora avere la possibilità di liberare la memoria allocata.

2 byte rimossi grazie a PrzemysławP, grazie!


1
Non puoi usare 1(o 2, 3ecc.) Invece di ()restituire valore 1? Si risparmierebbe 1 byte. Anche questa risposta è solo REPL? Forse se carichi il codice con loadte non puoi includerlo ()o nulla alla fine, perché non sarà comunque accessibile?

1
@ PrzemysławP Hai ragione su entrambi i punti, ci ho provato evale funziona come hai detto. Molte grazie!
coredump,

3

C ++, 16 byte

main(){new int;}

Non ho valgrind per verificare le perdite, ma abbastanza sicuro che dovrebbe. Altrimenti proverei:

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

Risultato Valgrind

(Effettivamente perde)

==708== LEAK SUMMARY:
==708==    definitely lost: 4 bytes in 1 blocks

@WheatWizard Sto usando g++ 4.3.2(non il più recente) e si compila bene. Nessun tipo di ritorno è intdi default penso. Con -Wallho un avvertimento però:plop.cpp:1: warning: ISO C++ forbids declaration of 'main' with no type
matovitch

2
@WheatWizard Spiacente, ho appena visto che hai dato l'esempio c ++ per iniziare il concorso. Provenendo da reddit ho solo guardato le risposte e (stranamente) non ho visto alcun C ++. Mi sento un po 'stupido. : /
matovitch,

Il consenso è che puoi contare proprio []{new int;}come una funzione C ++ (la sfida non ha specificato un intero programma).
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);}}

Provalo online!

Questa è un'altra perdita di memoria che non utilizza la cache delle stringhe. Alloca metà della RAM e non puoi farci nulla.

Grazie a zeppelin per aver salvato tutti i byte


Puoi salvare un sacco di byte ottenendo l' Unsafeistanza dalla variabile statica al suo interno, in questo modo: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

E puoi risparmiare ancora sostituendo public static void mainun inizializzatore statico static{try{}catch(Exception e){}}(che potrebbe essere un po 'più complicato da avviare ma è comunque valido e compilabile).
Zeppelin,

sì, l'uso del costruttore è stato utilizzato in una versione Android del codice che ho usato. Cambierò alcune cose quando im @home ma seguirò il percorso che hai seguito con una singola dichiarazione;)
Serverfrog

Rimuovi gli spazi bianchi, usa ainvece di argse rimuovi pubblico. tio.run/nexus/…
Pavel

true può essere sostituito con 1> 0
masterX244 del

3

c, 9 byte

main(){}

Prova:

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)

1
In realtà non stai perdendo memoria; gccè. Questo dovrebbe funzionare anche con il programma vuoto. Prova gcc src.c && valgrind ./a.out, che dovrebbe produrre un risultato pulito.

3

C #, 109 byte

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

Abbiamo trovato l'idea dietro questa perdita nel codice di produzione e la ricerca conduce a questo articolo. Il problema principale è in questa lunga citazione dall'articolo (leggilo per maggiori informazioni):

Cercando PurchaseOrder nel mio codice , trovo questa riga di codice in page_loaduna delle mie pagineXmlSerializer serializer = new XmlSerializer(typeof(PurchaseOrder), new XmlRootAttribute(“”));

Sembrerebbe un codice abbastanza innocente. Creiamo un XMLSerializerper PurchaseOrder. Ma cosa succede sotto le coperte?

Se diamo un'occhiata al XmlSerializercostruttore con Reflector scopriamo che chiama this.tempAssembly = XmlSerializer.GenerateTempAssembly(this.mapping, type, defaultNamespace, location, evidence);che genera un assembly temp (dinamico). Quindi ogni volta che questo codice viene eseguito (ovvero ogni volta che la pagina viene colpita) genererà un nuovo assembly.

Il motivo per cui genera un assembly è che deve generare funzioni per serializzare e deserializzare e queste devono risiedere da qualche parte.

Ok, bene ... crea un assemblaggio, e allora? Quando avremo finito, dovrebbe scomparire giusto?

Bene ... un assembly non è un oggetto sull'heap GC, il GC non è davvero a conoscenza degli assembly, quindi non verrà raccolta spazzatura. L'unico modo per sbarazzarsi degli assembly in 1.0 e 1.1 è scaricare il dominio dell'app in cui risiede.

E qui sta il problema Dr Watson.

L'esecuzione dal compilatore in Visual Studio 2015 e l'utilizzo della finestra degli strumenti di diagnostica mostra i seguenti risultati dopo circa 38 secondi. Nota che la memoria di processo è in costante aumento e Garbage Collector (GC) continua a funzionare ma non riesce a raccogliere nulla.

Finestra degli strumenti di diagnostica


2

C 30 byte

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

Risultati di Valgrind:

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

2
È possibile invece semplicemente fare main(){malloc(1);}?
Kirbyfan64sos,

@ Sì lo è! Ma è già stato pubblicato!
Abel Tom,

2

Dardo, 76 byte

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

Un po 'come la risposta JavaScript. Quando si chiama .listenun oggetto stream Dart, viene restituita una StreamSubscription, che consente di disconnettersi dallo stream. Tuttavia, se lo butti via, non puoi mai annullare l'iscrizione allo stream, causando una perdita. L'unico modo per correggere la perdita è se lo Stream stesso viene raccolto, ma viene comunque referenziato internamente da una combinazione StreamController + Timer.

Sfortunatamente, Dart è troppo intelligente per le altre cose che ho provato. ()async=>await new Completer().futurenon funziona perché l'utilizzo di waitit equivale a fare new Completer().future.then(<continuation>), il che consente di distruggere la chiusura stessa al secondo non viene fatto riferimento al Completatore (il Completatore contiene un riferimento al Futuro di .future, il Futuro contiene un riferimento alla continuazione come chiusura).

Inoltre, gli isolati (ovvero i thread) vengono ripuliti da GC, quindi generarsi in un nuovo thread e metterlo immediatamente in pausa ( import'dart:isolate';main(_)=>Isolate.spawn(main,0,paused:true);) non funziona. Anche la generazione di un isolato con un ciclo infinito ( import'dart:isolate';f(_){while(true){print('x');}}main()=>Isolate.spawn(f,0);) uccide l'isolato ed esce dal programma.

Oh bene.


se il tuo programma principale continuasse a funzionare e fare altre cose, il garbage collector sarebbe mai in grado di fermare l'isolato? chiedo perché il mio esempio goroutine sembra simile ... ho ipotizzato che il fatto che il programma esca e restituisca tutta la memoria al sistema operativo non significa necessariamente che non sia trapelato.
don luminoso

2

Rapido, 12 byte

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

Spiegazione:

Questa è una perdita di memoria di fatto che può verificarsi in qualsiasi lingua, indipendentemente dal fatto che la lingua utilizzi la gestione manuale della memoria, il conteggio dei riferimenti automatici (ARC, come Swift) o persino la spazzatura.

[3,5]è solo un array letterale. Questo array alloca memoria sufficiente per almeno questi 2 elementi. I 3e 5sono solo arbitrari.

La sottoscrizione (indicizzazione) an Array<T>produce un ArraySlice<T>. An ArraySlice<T>è una vista nella memoria dell'array da cui è stata creata.

[3,5][0...0]produce un ArraySlice<Int>, il cui valore è [3]. Si noti che 3in questa sezione è lo stesso 3elemento 3dell'originale Arraymostrato sopra, non una copia.

La sezione risultante può quindi essere memorizzata in una variabile e utilizzata. All'array originale non viene più fatto riferimento, quindi si potrebbe pensare che potrebbe essere deallocato. Tuttavia, non può.

Poiché la sezione espone una vista sulla memoria della matrice da cui proviene, la matrice originale deve essere mantenuta in vita finché la sezione rimane. Pertanto, per quanto riguarda la 2memoria di dimensioni degli elementi originali allocata, viene utilizzata solo la memoria di dimensioni delle dimensioni degli elementi di base, mentre l'altra deve esistere per non allocare la prima. La seconda dimensione dell'elemento della memoria viene sottratta.

La soluzione a questo problema è di non mantenere in vita piccole fette di array di grandi dimensioni a lungo. Se è necessario mantenere il contenuto della sezione, promuoverlo su un array, che attiverà la copia della memoria, rimuovendo così la dipendenza dalla memoria dell'array originale:

Array([3,5][0...0])

2

Soluzione 1: C (Mac OS X x86_64), 109 byte

La fonte per golf_sol1.c

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

Il programma sopra deve essere compilato con accesso all'esecuzione sul segmento __DATA.

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

Quindi per eseguire il programma eseguire quanto segue:

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

risultati:

Sfortunatamente Valgrind non controlla la memoria allocata dalle chiamate di sistema, quindi non posso mostrare una bella perdita rilevata.

Tuttavia, possiamo guardare vmmap per vedere il grosso pezzo di memoria allocata (metadati MALLOC).

                                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 

Spiegazione

Quindi penso di dover descrivere cosa sta realmente succedendo qui, prima di passare alla soluzione migliorata.

Questa funzione principale sta abusando della dichiarazione di tipo mancante di C (quindi per impostazione predefinita è int senza che dobbiamo sprecare i caratteri che la scrivono), nonché il modo in cui i simboli funzionano. Il linker si preoccupa solo di trovare un simbolo chiamato mainper chiamare. Quindi qui stiamo creando un array di int che stiamo inizializzando con il nostro shellcode che verrà eseguito. Per questo motivo, main non verrà aggiunto al segmento __TEXT ma piuttosto al segmento __DATA, motivo per cui è necessario compilare il programma con un segmento eseguibile __DATA.

Lo shellcode trovato in main è il seguente:

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

Ciò che sta facendo è chiamare la funzione syscall per allocare una pagina di memoria (la syscall mach_vm_allocate utilizza internamente). RAX dovrebbe essere uguale a 0x100000a (indica alla funzione syscall quale funzione vogliamo), mentre RDI mantiene l'obiettivo per l'allocazione (nel nostro caso vogliamo che questo sia mach_task_self ()), RSI dovrebbe contenere l'indirizzo per scrivere il puntatore nella memoria appena creata (quindi lo stiamo semplicemente puntando a una sezione dello stack), RDX contiene le dimensioni dell'allocazione (stiamo solo passando in RAX o 0x100000a solo per risparmiare sui byte), R10 contiene i flag (stiamo indicando che può essere assegnato ovunque).

Ora non è chiaramente ovvio da dove RAX e RDI ottengano i loro valori. Sappiamo che RAX deve essere 0x100000a e RDI deve essere il valore restituito da mach_task_self (). Fortunatamente mach_task_self () è in realtà una macro per una variabile (mach_task_self_), che è sempre allo stesso indirizzo di memoria (dovrebbe cambiare comunque al riavvio). Nel mio caso particolare mach_task_self_ sembra trovarsi in 0x00007fff7d578244. Quindi, per ridurre le istruzioni, passeremo invece a trasferire questi dati da argv. Questo è il motivo per cui eseguiamo il programma con questa espressione$(ruby -e 'puts "\xf5\xff\xff\xfe\xff\xff\x44\x82\x57\x7d\xff\x7f"')per il primo argomento. La stringa sono i due valori combinati, in cui il valore RAX (0x100000a) è solo 32 bit e ha un proprio complemento applicato su di esso (quindi non ci sono byte nulli; non abbiamo semplicemente il valore per ottenere l'originale), il valore successivo è l'RDI (0x00007fff7d578244) che è stato spostato a sinistra con 2 byte spazzatura aggiuntivi aggiunti alla fine (di nuovo per escludere i byte null, lo spostiamo di nuovo a destra per riportarlo all'originale).

Dopo il syscall stiamo scrivendo nella nostra memoria appena allocata. Il motivo è dovuto al fatto che la memoria allocata utilizzando mach_vm_allocate (o questo syscall) sono in realtà pagine di macchine virtuali e non vengono automaticamente paginate nella memoria. Piuttosto, sono riservati fino a quando i dati non vengono scritti su di loro, quindi quelle pagine vengono mappate in memoria. Non ero sicuro che avrebbe soddisfatto i requisiti se fosse stato riservato.

Per la prossima soluzione trarremo vantaggio dal fatto che il nostro shellcode non ha byte nulli, quindi possiamo spostarlo al di fuori del codice del nostro programma per ridurne le dimensioni.

Soluzione 2: C (Mac OS X x86_64), 44 byte

La fonte per golf_sol2.c

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

Il programma sopra deve essere compilato con accesso all'esecuzione sul segmento __DATA.

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

Quindi per eseguire il programma eseguire quanto segue:

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

Il risultato dovrebbe essere lo stesso di prima, poiché stiamo effettuando un'allocazione delle stesse dimensioni.

Spiegazione

Segue più o meno lo stesso concetto della soluzione 1, con l'eccezione che abbiamo spostato la parte del nostro codice che perde al di fuori del programma.

Lo shellcode trovato in main ora è il seguente:

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

Questo sostanzialmente copia il codice shell che passiamo in argv per essere dopo questo codice (quindi dopo averlo copiato, eseguirà il codice shell inserito). Ciò che funziona a nostro favore è che il segmento __DATA avrà almeno una dimensione di pagina, quindi anche se il nostro codice non è così grande possiamo ancora "tranquillamente" scrivere di più. Il rovescio della medaglia è la soluzione ideale qui, non avrebbe nemmeno bisogno della copia, invece chiamerebbe ed eseguirà direttamente il shellcode in argv. Ma sfortunatamente, questa memoria non ha diritti di esecuzione. Potremmo cambiare i diritti di questa memoria, tuttavia richiederebbe più codice che semplicemente copiarlo. Una strategia alternativa sarebbe quella di cambiare i diritti da un programma esterno (ma ne parleremo più avanti).

Il codice shell che passiamo ad argv è il seguente:

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

Questo è molto simile al nostro codice precedente, con l'unica differenza che stiamo includendo direttamente i valori di EAX e RDI.

Possibile soluzione 1: C (Mac OS X x86_64), 11 byte

L'idea di modificare il programma esternamente, ci offre la possibile soluzione di spostare il leaker in un programma esterno. Laddove il nostro programma attuale (invio) è solo un programma fittizio, e il programma leaker assegnerà parte della memoria nel nostro programma target. Ora non ero sicuro che ciò rientrasse nelle regole di questa sfida, ma condividendola comunque.

Quindi se dovessimo usare mach_vm_allocate in un programma esterno con l'obiettivo impostato sul nostro programma di sfida, ciò significherebbe che il nostro programma di sfida dovrebbe solo essere qualcosa sulla falsariga di:

main=65259;

Dove quel codice shell è semplicemente un salto corto verso se stesso (salto / loop infinito), quindi il programma rimane aperto e possiamo fare riferimento a esso da un programma esterno.

Possibile soluzione 2: C (Mac OS X x86_64), 8 byte

Stranamente quando stavo guardando l'output di valgrind, ho visto che almeno secondo valgrind, il diavolo perde memoria. Così efficacemente ogni programma perde memoria. Stando così le cose, potremmo effettivamente creare un programma che non fa nulla (esce semplicemente) e che in realtà perderà memoria.

Fonte:

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

2

Inglese semplice , 71 70 58 35 byte

Rimosso 1 byte eliminando una riga vuota. Rimossi 12 byte eliminando la definizione del tipo "bogon" e usando il tipo "cosa" padre anziché il sottotipo "bogon". Rimossi 23 byte passando da un programma completo a una routine che perde memoria.

Versione golfizzata:

To x:
Allocate memory for a thing.

Versione non controllata che è un programma completo, utilizza una definizione di sottotipo e non perde memoria:

A bogon is a thing.

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

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

Se viene chiamata la versione giocata a golf di "x", la memoria perderà in proporzione al numero di volte che viene chiamato "x". Nella versione golfata, "Deallocate la cosa". riparerebbe la perdita di memoria.

L'inglese semplice verifica la presenza di perdite di memoria per impostazione predefinita. Quando viene eseguita la versione che perde memoria, verrà visualizzata una finestra di dialogo prima che il programma si spenga. La finestra di dialogo ha un titolo di "debug", un messaggio di "1 gocciolamento" e un pulsante "OK". Più volte viene chiamata la funzione di perdita, maggiore è il numero di "gocciolamenti" nel messaggio. Quando viene eseguita la versione che non perde memoria, la finestra di dialogo non viene visualizzata.

In inglese semplice, una "cosa" è un puntatore a un elemento in un elenco doppiamente collegato. "Cosa", "avviare" e "chiudere" sono definiti in un modulo chiamato "tagliatella", che deve essere copiato (di solito come file separato) in ciascun progetto. "A", "the", "to", "allocare memoria" e "distruggere" sono definiti nel compilatore.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.