Fuga de memoria en la menor cantidad de bytes posible

79

Su tarea es escribir código que pierda al menos un byte de memoria en la menor cantidad de bytes posible. La memoria debe ser filtrada, no solo asignada .

La memoria filtrada es la memoria que el programa asigna pero pierde la capacidad de acceso antes de que pueda desasignar la memoria correctamente. Para la mayoría de los idiomas de alto nivel, esta memoria debe asignarse en el montón.

Un ejemplo en C ++ sería el siguiente programa:

int main(){new int;}

Esto lo convierte en un new intmontón sin un puntero. Esta memoria se pierde instantáneamente porque no tenemos forma de acceder a ella.

Así es como se vería un resumen de fugas de 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

Muchos idiomas tienen un depurador de memoria (como Valgrind ). Si puede, debe incluir la salida de dicho depurador para confirmar que ha perdido memoria.

El objetivo es minimizar el número de bytes en su fuente.

Asistente de trigo
fuente
2
Tal vez podría tener diferentes rangos de cantidad filtrados y, dependiendo de la cantidad que pierda, perderá x% de su recuento de bytes
Christopher
11
@ChristopherPeart Para uno, no soy fanático de los bonos en desafíos y para dos, como ya has demostrado, es muy fácil perder memoria ilimitada.
Wheat Wizard
1
Relacionados . Sin embargo, no es un duplicado, porque la mayoría de las respuestas a esa pregunta forman una estructura infinita accesible en la memoria en lugar de una pérdida de memoria.
2
cual es la idea ¿Que el mem no puede ser liberado? Supongo que esto requeriría una ejecución nativa para los idiomas recolectados de basura o la explotación de errores.
akostadinov
77
Veo cómo los idiomas diseñados para jugar al golf fallan miserablemente en este ...
Kh40tiK

Respuestas:

89

Perl (5.22.2), 0 bytes

Pruébalo en línea!

Sabía que habría algún lenguaje que perdiera memoria en un programa vacío. Esperaba que fuera un esolang, pero resulta que pierde perlmemoria en cualquier programa. (Supongo que esto es intencional, porque liberar memoria si sabe que va a salir de todos modos solo pierde tiempo; como tal, la recomendación común hoy en día es simplemente perder cualquier memoria restante una vez que esté en las rutinas de salida de su programa .)

Verificación

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

fuente
16
Me gustó la respuesta de Unlambda, pero esta es (en mi humilde opinión) demasiado exagerada, ya que obviamente es el intérprete en sí el que pierde la memoria, es decir, `definitivamente perdí: 7,742 bytes en 14 bloques` cuando corro perl --versionen mi máquina , a pesar de que nunca llega a ejecutar ningún programa, en absoluto.
zeppelin
11
@zeppelin: De acuerdo, pero de acuerdo con nuestras reglas, es la implementación la que define el lenguaje, por lo tanto, si la implementación pierde memoria, todos los programas en el idioma pierden memoria. No estoy necesariamente seguro de estar de acuerdo con esa regla, pero en este punto está demasiado arraigada para realmente poder cambiar.
8
Esto también funciona en el Nodo JS.
Dennis
66
Esto se siente como un nuevo vacío legal en ciernes ...
Michael Hampton
46
Finalmente un script de Perl que puedo entender.
usuario11153
66

C, 48 31 22 bytes

Advertencia: no ejecutes esto muchas veces.

¡Gracias a Dennis por mucha ayuda / ideas!

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

Esto va un paso más allá. shmgetasigna memoria compartida que no se desasigna cuando finaliza el programa. Utiliza una clave para identificar la memoria, por lo que usamos un int no inicializado. Este es un comportamiento técnicamente indefinido, pero prácticamente significa que usamos el valor que está justo encima de la parte superior de la pila cuando se llama a esto. Esto se escribirá la próxima vez que se agregue algo a la pila, por lo que perderemos la clave.


El único caso en que esto no funciona es si puedes descubrir qué había antes en la pila. Para 19 bytes adicionales, puede evitar este problema:

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

O, para 26 bytes:

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

Pero con este, la memoria se pierde después de que el programa sale. Mientras se ejecuta, el programa tiene acceso a la memoria, lo que va en contra de las reglas, pero después de que el programa termina, perdemos el acceso a la clave y la memoria aún está asignada. Esto requiere la asignación al azar del diseño del espacio de direcciones (ASLR), de lo contrario &ksiempre será lo mismo. Hoy en día, ASLR generalmente está activado por defecto.


Verificación:

Puede usar ipcs -mpara ver qué memoria compartida existe en su sistema. Eliminé entradas preexistentes para mayor claridad:

$ 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
fuente
1
@AndrewSavinykh Teóricamente, el shmid podría haberse almacenado en un archivo y un programa podría adjuntarse a él en el futuro. Así es como funciona la memoria compartida de Unix ...
tbodt
1
@AndrewSavinykh La memoria compartida se convierte básicamente en un recurso que el sistema operativo puede proporcionar a otros procesos. Es similar a un archivo que vive en la RAM y cualquier proceso que conozca el nombre (clave) tiene acceso a él hasta que se elimine. Imagine un proceso que calcula un número y lo almacena en la memoria y sale antes de que el proceso que lee los datos se conecte a la memoria compartida. En este caso, si el sistema operativo libera la memoria, el segundo proceso no puede obtenerlo.
Riley
35
Gracias por publicar esto. Acabo de proteger TIO contra fugas de memoria compartida.
Dennis
44
@ Dennis Por eso no publiqué un enlace TIO. No sabía si estaba protegido o no.
Riley
12
Me gusta cómo usa el problema de palabras para describir el escenario en el que el programa pierde menos memoria de la prevista.
kasperd
40

Unlambda ( c-refcnt/unlambda), 1 byte

i

Pruébalo en línea!

Esto es realmente un desafío para encontrar un intérprete preexistente que pierda memoria en programas muy simples. En este caso, usé Unlambda. Hay más de un intérprete oficial de Unlambda, pero c-refcntes uno de los más fáciles de construir, y tiene la propiedad útil de que pierde memoria cuando un programa se ejecuta con éxito. Así que todo lo que necesitaba dar aquí era el programa Unlambda legal más simple posible, un no-op. (Tenga en cuenta que el programa vacío no funciona aquí; la memoria todavía es accesible cuando el intérprete falla).

Verificación

$ wget ftp://ftp.madore.org/pub/madore/unlambda/unlambda-2.0.0.tar.gz
…recorte…
2017-02-18 18:11:08 (975 KB / s) - 'unlambda-2.0.0.tar.gz' guardado [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
…recorte…
== 3417 == RESUMEN DE FUGAS:
== 3417 == definitivamente perdido: 40 bytes en 1 bloques
== 3417 == perdido indirectamente: 0 bytes en 0 bloques
== 3417 == posiblemente perdido: 0 bytes en 0 bloques
== 3417 == todavía accesible: 0 bytes en 0 bloques
== 3417 == suprimido: 0 bytes en 0 bloques
== 3417 == 
== 3417 == Para recuentos de errores detectados y suprimidos, vuelva a ejecutar con: -v
== 3417 == RESUMEN DE ERRORES: 1 errores de 1 contextos (suprimido: 0 de 0)

fuente
39

TI-Basic, 12 bytes

While 1
Goto A
End
Lbl A
Pause 

"... una pérdida de memoria es cuando usa un Goto / Lbl dentro de un bucle o si es condicional (cualquier cosa que tenga un comando End) para saltar fuera de esa estructura de control antes de que se alcance el comando End ..." (más)

Timtech
fuente
77
Wow, creo que recuerdo esto. Seguí saltando de mis bucles en mis viejos programas básicos y noté cómo mi TI-84 + se volvía más y más lenta ...
Ray
2
Sí, la mayoría de nosotros conoce el sentimiento;) @RayKoopa
Timtech
13
+1 para Ti Basic. Pasé la mayor parte de mi noveno año programando esas cosas.
markasoftware
¿Necesitas Pause al final? Podrías guardar 2 bytes.
kamoroso94
@ kamoroso94 Creo que sí, porque "si un programa finaliza, la fuga se borra y no causará más problemas", por lo que es para evitar que el programa finalice.
Timtech
32

Python <3.6.5, 23 bytes

property([]).__init__()

property.__init__fugas de referencias a la propiedad de edad fget, fset, fdel, y __doc__si usted lo llama en una inicializado ya de por propertyejemplo. Este es un error, eventualmente reportado como parte del problema 31787 de CPython y corregido en Python 3.6.5 y Python 3.7.0 . (Además, sí, property([])es algo que puedes hacer).

usuario2357112
fuente
¿Se ha enviado un informe de error?
mbomb007
1
@ mbomb007: problema de CPython 31787.
user2357112
27

C #, 34 bytes

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

Esta solución no requiere el montón. Solo necesita un GC ( Garbage Collector ) realmente trabajador .

Esencialmente convierte al GC en su propio enemigo.

Explicación

Cada vez que se llama al destructor , crea nuevas instancias de esta clase malvada siempre que se agote el tiempo de espera y le dice al GC que simplemente abandone ese objeto sin esperar a que el destructor termine. Para entonces, se han creado miles de nuevas instancias.

La "maldad" de esto es que, cuanto más trabaje el GC, más te estallará en la cara.

Descargo de responsabilidad : su GC puede ser más inteligente que la mía. Otras circunstancias en el programa pueden hacer que el GC ignore el primer objeto o su destructor. En estos casos esto no explotará. Pero en muchas variaciones lo hará . Agregar algunos bytes aquí y allá podría garantizar una fuga para cada posible circunstancia. Bueno, excepto por el interruptor de encendido, tal vez.

Prueba

Aquí hay un conjunto de pruebas :

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

Salida después de 10 minutos:

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

Eso es 2 684 476 624 bytes. El total WorkingSetdel proceso fue de aproximadamente 4.8 GB

Esta respuesta se ha inspirado en el maravilloso artículo de Eric Lippert: cuando todo lo que sabes está mal .

MrPaulch
fuente
Esto es fascinante ¿El recolector de basura "olvida" que existen algunas cosas y las pierde de vista por esto? No sé mucho sobre c #. También ahora me pregunto, ¿cuál es la diferencia entre una bomba y una fuga? Me imagino que se podría crear un fiasco similar llamando a un constructor desde el interior de un constructor, o teniendo una función de recursión infinita que nunca se detiene, aunque técnicamente el sistema nunca pierde el rastro de esas referencias, simplemente se queda sin espacio ...
don brillante
1
Un constructor dentro de un constructor causaría un desbordamiento de pila. Pero el destructor de una instancia se llama en una jerarquía plana. El GC en realidad nunca pierde el rastro de los objetos. Justo cuando intenta destruirlos, sin darse cuenta, crea nuevos objetos. El código de usuario por otro lado no tiene acceso a dichos objetos. También pueden surgir las inconsistencias mencionadas ya que el GC puede decidir destruir un objeto sin llamar a su destructor.
MrPaulch
¿No se completaría el desafío simplemente usando class L{~L(){new L();}}? AFAIK the for(;;)only hace que pierda memoria más rápido, ¿verdad?
BgrWorker
1
Tristemente no. Dado que para cada objeto destruido solo se creará una nueva instancia, que de nuevo es inaccesible y marcada para su destrucción. Repetir. Solo un objeto estará pendiente de destrucción. Sin aumento de la población.
MrPaulch
2
Realmente no. Finalmente, uno finalizado será ignorado. El objeto correspondiente se comerá independientemente.
MrPaulch
26

C (gcc) , 15 bytes

f(){malloc(1);}

Verificación

$ 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
fuente
26

Javascript, 14 bytes

Golfed

setInterval(0)

Registra un controlador de intervalo vacío con un retraso predeterminado, descartando la identificación del temporizador resultante (lo que hace imposible cancelar).

ingrese la descripción de la imagen aquí

He usado un intervalo no predeterminado, para crear varios millones de temporizadores, para ilustrar la fuga, ya que el uso de un intervalo predeterminado consume CPU como loco.

zepelín
fuente
55
Jaja Me encanta que hayas escrito 'Golfed', me da curiosidad la versión sin golf
Martijn
99
podría verse asíif(window && window.setInterval && typeof window.setInterval === 'function') { window.setInterval(0); }
Tschallacka
3
En realidad, esto no es imposible de cancelar: las ID de intervalo (y tiempo de espera) se numeran secuencialmente, por lo que es bastante fácil cancelar la cosa simplemente llamando clearIntervalcon una ID incremental hasta que se acabe el intervalo. Por ejemplo:for(let i=0;i<1e5;i++){try{clearInterval(i);}catch(ex){}}
user2428118
55
@ user2428118 Como dice zeppelin, esto no es más "legítimo" que decir que las filtraciones de C / C ++ no son "reales" porque podría llamar a la fuerza brutafree()
TripeHound
1
Wow, no hay muchos desafíos donde JavaScript es un contendiente real ...
Jared Smith
19

Java, 10 bytes

¡Finalmente, una respuesta competitiva en Java!

Golfed

". "::trim

Esta es una referencia de método (contra una constante de cadena), que se puede usar así:

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

Se ". "agregará automáticamente una cadena literal al conjunto global de cadenas internas , tal como lo mantiene la java.lang.Stringclase, y a medida que la recortemos de inmediato, la referencia no se puede reutilizar más en el código (a menos que declare exactamente la misma cadena nuevamente).

...

Un conjunto de cadenas, inicialmente vacío, se mantiene de forma privada por la clase String.

Todas las cadenas literales y expresiones constantes con valores de cadena están internados. Los literales de cadena se definen en la sección 3.10.5 de la Especificación del lenguaje Java ™.

...

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

Puede convertir esto en una pérdida de memoria de "grado de producción", agregando la cadena a sí mismo e invocando el método intern () explícitamente, en un bucle.

zepelín
fuente
2
Consideré esto para C # ... pero no creo que se cuenta, porque como usted dice que puede acceder a esa memoria mediante la inclusión de otra cadena literal. También me interesaría saber qué ("." + " ").intern()haría (si fueran entradas del usuario o w / e, por lo que descontamos las optimizaciones del compilador).
VisualMelon
1
De hecho, el único consenso es delgado en el mejor de los casos, estoy firmemente del lado de "el código debe compilar". Todavía no estoy seguro de comprar esta solución dada la redacción de la pregunta (estas cadenas no se pueden liberar, y se pueden encontrar en el código de operación normal, aunque es poco probable), pero invito a otros a hacer su propio juicio
VisualMelon
3
Esa cadena ni siquiera es inaccesible , y mucho menos se filtró. Podemos recuperarlo en cualquier momento internando una cadena igual. Si esto fuera a contar, cualquier variable global no utilizada (estática privada en Java) sería una fuga. Así no se definen las pérdidas de memoria.
usuario2357112
3
@ user2357112 "... Esa cadena ni siquiera es inaccesible ..." Eso solo parece obvio porque ves el código. Ahora considere que tiene esta referencia de método X () como un argumento para su código, sabe que asigna (e interna) un literal de cadena dentro, pero no sabe cuál exactamente, podría ser "." O "123" o cualquier otra cadena de una longitud (generalmente) desconocida. ¿Podría demostrar cómo puede acceder a él o desasignar la entrada en el grupo "interno" que ocupa?
zepelín
2
@ user2357112 En una máquina con memoria finita, puede acceder a un valor almacenado en cualquier pieza de memoria simply by guessing it correctly, pero eso no significa que no existan pérdidas de memoria. there's probably some way to use reflection to determine the string's contents toopodrías demostrar esto? (sugerencia, String.intern () se implementa en el código nativo ).
zepelín
17

Óxido, 52 bytes

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

Asigna algunos bytes con el sistema malloc. Esto supone que el ABI incorrecto es aceptable.


Óxido (en teoría), 38 bytes

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

Asignamos memoria en el montón, extraemos un puntero sin formato y luego simplemente lo ignoramos, filtrándolo efectivamente. ( Box::into_rawes más corto entonces std::mem::forget).

Sin embargo, Rust por defecto usa jemalloc, que valgrind no puede detectar ninguna fuga . Podríamos cambiar al asignador del sistema, pero eso agrega 50 bytes y requiere todas las noches. Muchas gracias por la seguridad de la memoria.


Salida del primer programa:

==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
fuente
increíble. publicaciones como esta me llevaron a explorar Rust durante el año pasado, definitivamente uno de los idiomas más divertidos que he tratado de aprender.
Don brillante
16

8086 ASM, 3 bytes

Este ejemplo supone que un tiempo de ejecución C está vinculado.

jmp _malloc

esto se ensambla a e9 XX XXdonde XX XXestá la dirección relativa de_malloc

Esto invoca mallocpara asignar una cantidad impredecible de memoria y luego regresa inmediatamente, finalizando los procesos. ¡En algunos sistemas operativos como DOS, la memoria podría no ser recuperable en absoluto hasta que se reinicie el sistema!

FUZxxl
fuente
La implementación normal de malloc dará como resultado la liberación de memoria al salir del proceso.
Joshua
@ Joshua Sí, pero ese es el comportamiento definido de implementación.
FUZxxl
12

Adelante, 6 bytes

Golfed

s" " *

Asigna una cadena vacía con s" ", dejando su dirección y longitud (0) en la pila, luego las multiplica (lo que da como resultado que se pierda una dirección de 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
zepelín
fuente
10

ir 45 bytes

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

Esto crea una gorutina anónima con un bucle infinito dentro de ella. el programa puede continuar ejecutándose normalmente, ya que iniciar la rutina es algo así como generar un pequeño subproceso que se ejecuta simultáneamente, pero el programa no tiene forma de recuperar la memoria que se asignó a la rutina. el recolector de basura nunca lo recogerá tampoco, ya que todavía se está ejecutando. algunas personas llaman a esto 'goteando una gordina

don brillante
fuente
verificación de golf: esto es 2 bytes más corto que llamar C.malloc(8), ya que necesita hacerloimport"C"
Riking
9

Java 1.3, 23 bytes

void l(){new Thread();}

Crear un hilo pero no iniciarlo. El subproceso está registrado en el grupo de subprocesos interno, pero nunca se iniciará, por lo que nunca finalizó y, por lo tanto, nunca será candidato para el GC. Es un objeto irrecuperable, atrapado en los limbos de Java.

Es un error en Java hasta 1.3 incluido, ya que se corrigió después.

Pruebas

El siguiente programa se asegura de contaminar la memoria con nuevos objetos de hilo y muestra un espacio libre de memoria decreciente. En aras de las pruebas de fugas, hago que el GC funcione de manera intensiva.

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
fuente
Dado que esto solo funciona en versiones específicas de Java, debería decir "Java 3" en su encabezado.
55
No existe Java 3. Es Java 1.3. Había Java 1.0, 1.1, 2, 1.3, 1.4, 5, 6, 7, 8, 9. Numeración extraña, pero así es como es.
Olivier Grégoire
Esta fue mi idea también. Maldición.
Magic Octopus Urn
8

Befunge ( hongos ), 1 byte

$

Esto puede depender de la plataforma y de la versión (solo lo he probado con la versión 1.0.4 en Windows), pero los hongos han sido históricamente un intérprete muy permeable. El $comando (soltar) no debería hacer nada en una pila vacía, pero recorrer este código de alguna manera logra perder mucha memoria muy rápidamente. En cuestión de segundos habrá agotado un par de conciertos y se bloqueará con un error de "falta de memoria".

Tenga en cuenta que no necesariamente tiene que ser un $comando, casi cualquier cosa sería suficiente. Sin embargo, no funcionará con un archivo fuente en blanco. Tiene que haber al menos una operación.

James Holderness
fuente
8

Swift 3, 38 bytes

Nueva versión:

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

x tiene una fuerte referencia a sí mismo, por lo que no se desasignará, lo que provocará una pérdida de memoria.

Versión antigua:

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

xcontiene una fuerte referencia a y, y viceversa. Por lo tanto, ninguno será desasignado, lo que provocará una pérdida de memoria.

Daniel
fuente
Hmm, todavía puedes hacer referencia a esa memoria xy y, por lo que esto realmente no me parece una fuga (a menos que los destruyas de alguna manera).
zepelín
@zeppelin La última línea podría estar envuelta en una función para arreglar eso
NobodyNada
@NobodyNada, si tuviera que poner la última línea en un dobloque que solucionaría el problema planteado por zeppelin ¿verdad?
Daniel
@Dopapp Sí; a dofuncionaría también. ¡Buena idea!
NobodyNada
Se puede acortar, no necesita dos clases - X puede tener referencia a sí mismo:class X{var x: X!};do{let x=X();x.x=x}
Sebastian Osiński
7

Delphi (Object Pascal) - 33 bytes

Crear un objeto sin un programa de consola completo variable:

program;begin TObject.Create;end.

Habilitar FastMM4 en el proyecto mostrará la pérdida de memoria:

ingrese la descripción de la imagen aquí

RD
fuente
6

C # - 84bytes

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

Esto asigna exactamente 1 byte de memoria no administrada y luego pierde el IntPtr , que creo que es la única forma de obtenerlo o liberarlo. Puede probarlo rellenándolo en un bucle y esperando que la aplicación se bloquee (es posible que desee agregar algunos ceros para acelerar las cosas).

Consideré System.IO.File.Create("a");y tal, pero no estoy convencido de que estos son necesariamente pérdidas de memoria, como la propia aplicación va a recoger la memoria, que es el sistema operativo por debajo de eso podría filtrarse (porque Closeo Disposeno fueron llamados). El acceso a archivos también requiere permisos del sistema de archivos, y nadie quiere confiar en ellos. Y resulta que esto no se filtrará de todos modos, porque no hay nada que impida que se llame al finalizador (lo que libera los recursos subyacentes es posible), que el marco incluye para mitigar este tipo de error de juicio (hasta cierto punto), y confundir a los programadores con el bloqueo de archivos aparentemente no determinista (si eres un cínico). Gracias a Jon Hanna por aclararme esto.

Estoy un poco decepcionado de no poder encontrar un camino más corto. El .NET GC funciona, no se me ocurre ninguno IDisposablesen mscorlib que definitivamente se filtre (y de hecho todos parecen tener finalizadores, qué molesto) , no conozco ninguna otra forma de asignar memoria no administrada (salvo PInvoke ), y la reflexión asegura nada con una referencia a él (independientemente de la semántica del lenguaje (por ejemplo, miembros particulares o clases sin descriptores de acceso)) puede ser encontrado.

VisualMelon
fuente
1
System.IO.File.Create("a")no perderá nada, pero GC.SuppressFinalize(System.IO.File.Create("a"))sí, ya que se le pide explícitamente que no ejecute el finalizador del FileStreamproducido.
Jon Hanna
@ JonHanna tiene toda la razón. Mi paranoia desechable parece haber sacado lo mejor de mí.
VisualMelon
Es posible que tenga la posibilidad de que GDI + se filtre usando System.Drawing.Bitmap. No sé si cuenta porque es una biblioteca de Windows la que causa la fuga, no el programa en sí.
BgrWorker
@BgrWorker, sin duda, también tienen un finalizador, y tiendo a evitar bibliotecas externas en code-golf porque no estoy de acuerdo con el consenso sobre el costo: si puedes encontrar una forma en la que confíes, no dudes en publicar ¡en tu propia respuesta!
VisualMelon
<!-- language: lang-c# -->Gracias por esto y buena respuesta! (Es C #, así que me encanta)
Metoniem
5

Factor , 13 bytes

Factor tiene administración automática de memoria, pero también da acceso a algunas funciones de libc:

1 malloc drop

Asigna manualmente 1 byte de memoria, devuelve su dirección y la descarta.

malloc en realidad registra una copia para realizar un seguimiento de las pérdidas de memoria y las liberaciones dobles, pero identificar la que se filtró no es una tarea fácil.

Si prefiere asegurarse de que realmente pierde esa referencia:

1 (malloc) drop

Prueba de fugas con [ 1 malloc drop ] leaks.dice:

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

Prueba de fugas con [ 1 (malloc) drop ] leaks.dice:

| Disposable class | Instances | |

¡Oh no! ¡Pobre factor, ahora tiene Alzheimer! RE:

fede s.
fuente
4

AutoIt , 39 bytes

#include<Memory.au3>
_MemGlobalAlloc(1)

Asigna un byte del montón. Dado que el identificador devuelto por _MemGlobalAllocse descarta, no hay forma de liberar explícitamente esa asignación.

mınxomaτ
fuente
4

Lisp común (solo SBCL), 28 26 bytes

sb-alien::(make-alien int)

Lo ejecuta así: sbcl --eval 'sb-alien::(make-alien int)'; no se imprime ni devuelve nada, pero ocurre la asignación de memoria. Si envuelvo el formulario dentro de a (print ...), el puntero se muestra en REPL.

  1. package::(form)es una notación especial en SBCL para vincular temporalmente el paquete actual mientras lee un formulario. Esto se usa aquí para evitar prefijar ambos make-alieny intcon sb-alien. Creo que sería una trampa asumir que el paquete actual está configurado para este, porque ese no es el caso al inicio.

  2. make-alien asigna memoria para un tipo dado y un tamaño opcional (usando malloc).

  3. Al ejecutar esto en REPL, agregue 0después de la asignación para que REPL no devuelva el puntero, sino ese valor. De lo contrario, eso no sería una fuga real porque el REPL recuerda los últimos tres valores devueltos (Ver *, **,*** ) y que todavía podría tener la oportunidad de liberar la memoria asignada.

2 bytes eliminados gracias a PrzemysławP, ¡gracias!

volcado de memoria
fuente
1
¿No puedes usar 1(o 2, 3, etc.) en lugar de ()modo que devuelva el valor 1? Ahorraría 1 byte. ¿También es esta respuesta REPL solo? Tal vez si carga el código con loadusted no puede incluir ()ni nada al final, porque de todos modos no será accesible.
1
@ PrzemysławP Tienes razón en ambos puntos, probé evaly esto funciona como dijiste. ¡Muchas gracias!
coredump
3

C ++, 16 bytes

main(){new int;}

No tengo valgrind para comprobar si hay fugas, pero estoy bastante seguro de que debería. De lo contrario, intentaría:

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

Resultado valgrind

(Se filtra de hecho)

==708== LEAK SUMMARY:
==708==    definitely lost: 4 bytes in 1 blocks
matovitch
fuente
@WheatWizard que estoy usando g++ 4.3.2(no el más reciente) y se compila muy bien. Ningún tipo de retorno es intpor defecto, creo. Sin -Wallembargo, tengo una advertencia:plop.cpp:1: warning: ISO C++ forbids declaration of 'main' with no type
matovitch
2
@WheatWizard Lo siento, acabo de ver que diste el ejemplo de c ++ para comenzar el concurso. Viniendo de reddit solo miré las respuestas y (extrañamente) no vi ningún C ++. Me siento un poco tonto : /
matovitch
El consenso es que puede contar []{new int;}como una función de C ++ (el desafío no especificó un programa completo).
Toby Speight
3

Java (OpenJDK 9) , 322 220 bytes

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

Pruébalo en línea!

Esta es otra pérdida de memoria que no utiliza la Caché de cadenas. Asigna la mitad de tu RAM y no puedes hacer nada con ella.

Gracias a zeppelin por guardar todos los bytes

Rana servidor
fuente
Usted puede ahorrar un montón de bytes mediante la obtención de la Unsafeinstancia de la variable estática dentro de él, al igual que: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
Y puede ahorrar un poco más si lo reemplaza public static void mainpor un inicializador estático static{try{}catch(Exception e){}}(que podría ser un poco más difícil de iniciar pero, sin embargo, es válido y compilable).
zepelín
Sí, el uso del constructor se utilizó en una versión de Android del código que utilicé.
Cambiaré
Elimine los espacios en blanco, use en alugar de argsy elimine public. tio.run/nexus/…
Pavel
verdadero puede ser reemplazado con 1> 0
masterX244
3

c, 9 bytes

main(){}

Prueba:

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
fuente
1
En realidad no estás perdiendo memoria; gcces. Esto también debería funcionar con el programa vacío. Intente gcc src.c && valgrind ./a.out, lo que debería producir un resultado limpio.
3

C #, 109 bytes

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

Encontramos la idea detrás de esta filtración en el código de producción y la investigación lleva a este artículo. El problema principal está en esta larga cita del artículo (léalo para más información):

PurchaseOrdeAl buscar mi código para r, encuentro esta línea de código en page_loaduna de mis páginasXmlSerializer serializer = new XmlSerializer(typeof(PurchaseOrder), new XmlRootAttribute(“”));

Esto parecería una pieza de código bastante inocente. Creamos un XMLSerializerpara PurchaseOrder. ¿Pero qué pasa debajo de las sábanas?

Si echamos un vistazo al XmlSerializerconstructor con Reflector, encontramos que llama, lo this.tempAssembly = XmlSerializer.GenerateTempAssembly(this.mapping, type, defaultNamespace, location, evidence);que genera un ensamblaje temporal (dinámico). Por lo tanto, cada vez que se ejecuta este código (es decir, cada vez que se golpea la página) generará un nuevo ensamblado.

La razón por la que genera un ensamblaje es que necesita generar funciones para serializar y deserializar y estas deben residir en algún lugar.

Ok, bien ... crea un ensamblaje, ¿y qué? Cuando hayamos terminado con eso, debería desaparecer, ¿verdad?

Bueno ... un ensamblaje no es un objeto en el Montón GC, el GC realmente desconoce los ensamblajes, por lo que no se recolectará basura. La única forma de deshacerse de los ensamblados en 1.0 y 1.1 es descargar el dominio de la aplicación en el que reside.

Y ahí radica el problema, Dr. Watson.

Ejecutar desde el compilador en Visual Studio 2015 y usar la ventana Herramientas de diagnóstico muestra los siguientes resultados después de unos 38 segundos. Tenga en cuenta que la memoria del proceso aumenta constantemente y el recolector de basura (GC) sigue funcionando pero no puede recolectar nada.

Ventana de herramientas de diagnóstico

TheLethalCoder
fuente
2

C 30 bytes

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

Resultados de 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
Abel Tom
fuente
2
¿Es posible simplemente hacer main(){malloc(1);}?
kirbyfan64sos
@¡Si eso es! ¡Pero ya ha sido publicado!
Abel Tom
2

Dart, 76 bytes

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

Un poco como la respuesta de JavaScript. Cuando llama .listena un objeto de transmisión de Dart, se le devuelve una suscripción de StreamS, que le permite desconectarse de la transmisión. Sin embargo, si tira eso, nunca podrá darse de baja de la transmisión, causando una fuga. La única forma en que se puede reparar la fuga es si el Stream en sí mismo se recopila, pero todavía se hace referencia internamente por un combo StreamController + Timer.

Desafortunadamente, Dart es demasiado inteligente para las otras cosas que he probado. ()async=>await new Completer().futureno funciona porque usar waitit es lo mismo que hacer new Completer().future.then(<continuation>), lo que permite que el cierre en sí mismo sea destruido cuando el segundo Completer no está referenciado (Completer tiene una referencia al Futuro desde .future, Future tiene una referencia a la continuación como un cierre).

Además, los aislamientos (también conocidos como hilos) son limpiados por GC, por lo que generar un nuevo hilo e inmediatamente detenerlo ( import'dart:isolate';main(_)=>Isolate.spawn(main,0,paused:true);) no funciona. Incluso generar un Isolate con un bucle infinito ( import'dart:isolate';f(_){while(true){print('x');}}main()=>Isolate.spawn(f,0);) mata al Isolate y sale del programa.

Oh bien.

Dwayne Slater
fuente
Si su programa principal seguía ejecutándose y haciendo otras cosas, ¿podría el recolector de basura alguna vez detener el aislamiento? pregunto porque mi ejemplo de goroutine suena similar ... supuse que el hecho de que el programa salga y devuelva toda la memoria al sistema operativo no necesariamente significa que no se haya filtrado.
Don brillante
2

Rápido, 12 bytes

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

Explicación:

Esta es una pérdida de memoria de facto que puede ocurrir en cualquier idioma, independientemente de si el idioma utiliza la gestión manual de la memoria, el conteo automático de referencias (ARC, como Swift) o incluso la recolección de basura.

[3,5]es solo una matriz literal. Esta matriz asigna suficiente memoria para al menos estos 2 elementos. Los 3y 5son simplemente arbitrarios.

La suscripción (indexación) an Array<T>produce un ArraySlice<T>. An ArraySlice<T>es una vista de la memoria de la matriz a partir de la cual se creó.

[3,5][0...0]produce un ArraySlice<Int>, cuyo valor es [3]. Tenga 3en cuenta que en este segmento es el mismo 3elemento que 3en el original que se Arraymuestra arriba, no una copia.

El segmento resultante se puede almacenar en una variable y usar. La matriz original ya no está referenciada, por lo que podría pensar que podría ser desasignada. Sin embargo, no puede.

Dado que el segmento expone una vista en la memoria de la matriz de la que proviene, la matriz original debe mantenerse viva mientras viva la división. Por lo tanto, de la 2memoria de tamaño de elemento original que se asignó, solo se usa la memoria de primer tamaño de elemento, y se requiere que el otro exista para no asignar el primero. El segundo elemento de tamaño de la memoria se filtra por factor.

La solución a este problema es no mantener vivos pequeños trozos de grandes matrices por mucho tiempo. Si necesita persistir el contenido de la porción, entonces promueva a una matriz, lo que activará la copia de la memoria, eliminando así la dependencia de la memoria de la matriz original:

Array([3,5][0...0])
Alejandro
fuente
2

Solución 1: C (Mac OS X x86_64), 109 bytes

La fuente de golf_sol1.c

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

El programa anterior debe compilarse con acceso de ejecución en el segmento __DATA.

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

Luego, para ejecutar el programa, ejecute lo siguiente:

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

Resultados:

Desafortunadamente, Valgrind no busca la memoria asignada de las llamadas al sistema, por lo que no puedo mostrar una buena fuga detectada.

Sin embargo, podemos mirar vmmap para ver la gran parte de la memoria asignada (metadatos 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 

Explicación

Así que creo que necesito describir lo que realmente está sucediendo aquí, antes de pasar a la solución mejorada.

Esta función principal es abusar de la declaración de tipo faltante de C (por lo que se usa de manera predeterminada int sin que tengamos que desperdiciar caracteres al escribirla), así como también cómo funcionan los símbolos. El enlazador solo se preocupa por si puede o no encontrar un símbolo llamado maina llamar. Así que aquí estamos haciendo main una matriz de int que estamos inicializando con nuestro shellcode que se ejecutará. Debido a esto, main no se agregará al segmento __TEXT sino más bien al segmento __DATA, razón por la cual necesitamos compilar el programa con un segmento ejecutable __DATA.

El shellcode encontrado en main es el siguiente:

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

Lo que está haciendo es llamar a la función syscall para asignar una página de memoria (la syscall mach_vm_allocate usa internamente). RAX debe ser igual a 0x100000a (le dice a syscall qué función queremos), mientras que RDI mantiene el objetivo para la asignación (en nuestro caso queremos que sea mach_task_self ()), RSI debe mantener la dirección para escribir el puntero en la memoria recién creada (así que solo lo estamos apuntando a una sección en la pila), RDX tiene el tamaño de la asignación (solo estamos pasando RAX o 0x100000a solo para guardar en bytes), R10 tiene las banderas (estamos indicando que puede ser asignado en cualquier lugar).

Ahora no es claramente obvio de dónde obtienen sus valores RAX y RDI. Sabemos que RAX debe ser 0x100000a, y RDI debe ser el valor que devuelve mach_task_self (). Afortunadamente, mach_task_self () es en realidad una macro para una variable (mach_task_self_), que está en la misma dirección de memoria cada vez (sin embargo, debe cambiar al reiniciar). En mi caso particular, mach_task_self_ está ubicado en 0x00007fff7d578244. Entonces, para reducir las instrucciones, pasaremos estos datos de argv. Por eso ejecutamos el programa con esta expresión$(ruby -e 'puts "\xf5\xff\xff\xfe\xff\xff\x44\x82\x57\x7d\xff\x7f"')para el primer argumento La cadena son los dos valores combinados, donde el valor RAX (0x100000a) es de solo 32 bits y se le ha aplicado un complemento (por lo que no hay bytes nulos; simplemente NO somos el valor para obtener el original), el siguiente valor es el RDI (0x00007fff7d578244) que se ha desplazado hacia la izquierda con 2 bytes basura adicionales agregados al final (nuevamente para excluir los bytes nulos, simplemente lo volvemos a la derecha para volver al original).

Después de la llamada al sistema, estamos escribiendo en nuestra memoria recién asignada. La razón de esto es porque la memoria asignada usando mach_vm_allocate (o esta syscall) son en realidad páginas VM y no se paginan automáticamente en la memoria. Más bien se reservan hasta que se escriben datos en ellos, y luego esas páginas se asignan a la memoria. No estaba seguro de si cumpliría con los requisitos si solo estuviera reservado.

Para la próxima solución, aprovecharemos el hecho de que nuestro shellcode no tiene bytes nulos, por lo que podemos moverlo fuera del código de nuestro programa para reducir el tamaño.

Solución 2: C (Mac OS X x86_64), 44 bytes

La fuente para golf_sol2.c

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

El programa anterior debe compilarse con acceso de ejecución en el segmento __DATA.

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

Luego, para ejecutar el programa, ejecute lo siguiente:

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

El resultado debería ser el mismo que antes, ya que estamos haciendo una asignación del mismo tamaño.

Explicación

Sigue el mismo concepto que la solución 1, con la excepción de que hemos movido la mayor parte de nuestro código de fuga fuera del programa.

El shellcode encontrado en main ahora es el siguiente:

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

Básicamente, esto copia el código de shell que pasamos en argv para que esté después de este código (así que después de que lo haya copiado, ejecutará el código de shell insertado). Lo que funciona a nuestro favor es que el segmento __DATA tendrá al menos un tamaño de página, por lo que incluso si nuestro código no es tan grande, aún podemos escribir más "con seguridad". La desventaja es la solución ideal aquí, ni siquiera necesitaría la copia, sino que simplemente llamaría y ejecutaría el shellcode en argv directamente. Pero desafortunadamente, esta memoria no tiene derechos de ejecución. Podríamos cambiar los derechos de esta memoria, sin embargo, requeriría más código que simplemente copiarlo. Una estrategia alternativa sería cambiar los derechos de un programa externo (pero más sobre eso más adelante).

El shellcode que pasamos a argv es el siguiente:

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

Esto es muy similar a nuestro código anterior, la única diferencia es que estamos incluyendo los valores para EAX y RDI directamente.

Posible solución 1: C (Mac OS X x86_64), 11 bytes

La idea de modificar el programa externamente, nos da la posible solución de mover el filtrador a un programa externo. Donde nuestro programa real (envío) es solo un programa ficticio, y el programa de fuga asignará algo de memoria en nuestro programa objetivo. Ahora no estaba seguro de si esto estaría dentro de las reglas para este desafío, pero aun así compartirlo.

Entonces, si tuviéramos que usar mach_vm_allocate en un programa externo con el objetivo establecido para nuestro programa de desafío, eso podría significar que nuestro programa de desafío solo necesitaría ser algo similar a:

main=65259;

Donde ese shellcode es simplemente un salto corto a sí mismo (salto / bucle infinito), el programa permanece abierto y podemos hacer referencia a él desde un programa externo.

Posible solución 2: C (Mac OS X x86_64), 8 bytes

Curiosamente, cuando estaba mirando la salida de valgrind, vi que al menos según valgrind, dyld pierde memoria. Así que, efectivamente, cada programa pierde algo de memoria. Siendo este el caso, en realidad podríamos hacer un programa que no haga nada (simplemente salga), y que realmente perderá memoria.

Fuente:

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
fuente
2

Inglés simple , 71 70 58 35 bytes

Se eliminó 1 byte eliminando una línea en blanco. Se eliminaron 12 bytes eliminando la definición de tipo "bogon" y utilizando el tipo "cosa" padre en lugar del subtipo "bogon". Se eliminaron 23 bytes cambiando de un programa completo a una rutina que pierde memoria.

Versión de golf:

To x:
Allocate memory for a thing.

Versión sin golf que es un programa completo, utiliza una definición de subtipo y no pierde 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.

Si se llama a la versión golfizada de "x", perderá memoria en proporción al número de veces que se llama "x". En la versión de golf, "Desasignar la cosa". solucionaría la pérdida de memoria.

El inglés simple verifica si hay pérdidas de memoria por defecto. Cuando se ejecuta la versión que pierde memoria, aparecerá un cuadro de diálogo justo antes de que el programa se cierre. El cuadro de diálogo tiene un título de "depuración", un mensaje de "1 goteo" y un botón "Aceptar". Cuantas más veces se llame a la función de fuga, mayor será el número de "goteos" en el mensaje. Cuando se ejecuta la versión que no pierde memoria, el cuadro de diálogo no aparece.

En inglés simple, una "cosa" es un puntero a un elemento en una lista doblemente vinculada. "Cosa", "iniciar" y "cerrar" se definen en un módulo llamado "el fideo", que debe copiarse (generalmente como un archivo separado) en cada proyecto. "A", "el", "a", "para asignar memoria" y "destruir" se definen en el compilador.

Jaspe
fuente