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 int
montó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.
Respuestas:
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
perl
memoria 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
fuente
perl --version
en mi máquina , a pesar de que nunca llega a ejecutar ningún programa, en absoluto.C,
48 3122 bytesAdvertencia: no ejecutes esto muchas veces.
¡Gracias a Dennis por mucha ayuda / ideas!
Esto va un paso más allá.
shmget
asigna 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:
O, para 26 bytes:
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
&k
siempre será lo mismo. Hoy en día, ASLR generalmente está activado por defecto.Verificación:
Puede usar
ipcs -m
para ver qué memoria compartida existe en su sistema. Eliminé entradas preexistentes para mayor claridad:fuente
Unlambda (
c-refcnt/unlambda
), 1 bytePrué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-refcnt
es 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
fuente
TI-Basic, 12 bytes
"... 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)
fuente
Pause
al final? Podrías guardar 2 bytes.Python <3.6.5, 23 bytes
property.__init__
fugas de referencias a la propiedad de edadfget
,fset
,fdel
, y__doc__
si usted lo llama en una inicializado ya de porproperty
ejemplo. 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).fuente
C #, 34 bytes
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 :
Salida después de 10 minutos:
Eso es 2 684 476 624 bytes. El total
WorkingSet
del proceso fue de aproximadamente 4.8 GBEsta respuesta se ha inspirado en el maravilloso artículo de Eric Lippert: cuando todo lo que sabes está mal .
fuente
class L{~L(){new L();}}
? AFAIK thefor(;;)
only hace que pierda memoria más rápido, ¿verdad?C (gcc) , 15 bytes
Verificación
fuente
Javascript, 14 bytes
Golfed
Registra un controlador de intervalo vacío con un retraso predeterminado, descartando la identificación del temporizador resultante (lo que hace imposible cancelar).
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.
fuente
if(window && window.setInterval && typeof window.setInterval === 'function') { window.setInterval(0); }
clearInterval
con una ID incremental hasta que se acabe el intervalo. Por ejemplo:for(let i=0;i<1e5;i++){try{clearInterval(i);}catch(ex){}}
free()
Java, 10 bytes
¡Finalmente, una respuesta competitiva en Java!
Golfed
Esta es una referencia de método (contra una constante de cadena), que se puede usar así:
Se
". "
agregará automáticamente una cadena literal al conjunto global de cadenas internas , tal como lo mantiene lajava.lang.String
clase, 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).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.
fuente
("." + " ").intern()
haría (si fueran entradas del usuario o w / e, por lo que descontamos las optimizaciones del compilador).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 too
podrías demostrar esto? (sugerencia, String.intern () se implementa en el código nativo ).Óxido, 52 bytes
Asigna algunos bytes con el sistema malloc. Esto supone que el ABI incorrecto es aceptable.
Óxido (en teoría), 38 bytesAsignamos memoria en el montón, extraemos un puntero sin formato y luego simplemente lo ignoramos, filtrándolo efectivamente. (
Box::into_raw
es más corto entoncesstd::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:
fuente
8086 ASM, 3 bytes
Este ejemplo supone que un tiempo de ejecución C está vinculado.
esto se ensambla a
e9 XX XX
dondeXX XX
está la dirección relativa de_malloc
Esto invoca
malloc
para 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!fuente
Adelante, 6 bytes
Golfed
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
fuente
ir 45 bytes
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
fuente
C.malloc(8)
, ya que necesita hacerloimport"C"
Java 1.3, 23 bytes
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.
fuente
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.fuente
Swift 3, 38 bytes
Nueva versión:
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:
x
contiene una fuerte referencia ay
, y viceversa. Por lo tanto, ninguno será desasignado, lo que provocará una pérdida de memoria.fuente
x
yy
, por lo que esto realmente no me parece una fuga (a menos que los destruyas de alguna manera).do
bloque que solucionaría el problema planteado por zeppelin ¿verdad?do
funcionaría también. ¡Buena idea!class X{var x: X!};do{let x=X();x.x=x}
Delphi (Object Pascal) - 33 bytes
Crear un objeto sin un programa de consola completo variable:
Habilitar FastMM4 en el proyecto mostrará la pérdida de memoria:
fuente
C # - 84bytes
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 (porqueClose
oDispose
no 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
IDisposables
en 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.fuente
System.IO.File.Create("a")
no perderá nada, peroGC.SuppressFinalize(System.IO.File.Create("a"))
sí, ya que se le pide explícitamente que no ejecute el finalizador delFileStream
producido.<!-- language: lang-c# -->
Gracias por esto y buena respuesta! (Es C #, así que me encanta)Factor , 13 bytes
Factor tiene administración automática de memoria, pero también da acceso a algunas funciones de libc:
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:
Prueba de fugas con
[ 1 malloc drop ] leaks.
dice:Prueba de fugas con
[ 1 (malloc) drop ] leaks.
dice:¡Oh no! ¡Pobre factor, ahora tiene Alzheimer! RE:
fuente
AutoIt , 39 bytes
Asigna un byte del montón. Dado que el identificador devuelto por
_MemGlobalAlloc
se descarta, no hay forma de liberar explícitamente esa asignación.fuente
Lisp común (solo SBCL),
2826 bytesLo 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.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 ambosmake-alien
yint
consb-alien
. Creo que sería una trampa asumir que el paquete actual está configurado para este, porque ese no es el caso al inicio.make-alien
asigna memoria para un tipo dado y un tamaño opcional (usando malloc).Al ejecutar esto en REPL, agregue
0
despué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!
fuente
1
(o2
,3
, etc.) en lugar de()
modo que devuelva el valor1
? Ahorraría 1 byte. ¿También es esta respuesta REPL solo? Tal vez si carga el código conload
usted no puede incluir()
ni nada al final, porque de todos modos no será accesible.eval
y esto funciona como dijiste. ¡Muchas gracias!C ++, 16 bytes
No tengo valgrind para comprobar si hay fugas, pero estoy bastante seguro de que debería.De lo contrario, intentaría:Resultado valgrind
(Se filtra de hecho)
fuente
g++ 4.3.2
(no el más reciente) y se compila muy bien. Ningún tipo de retorno esint
por defecto, creo. Sin-Wall
embargo, tengo una advertencia:plop.cpp:1: warning: ISO C++ forbids declaration of 'main' with no type
[]{new int;}
como una función de C ++ (el desafío no especificó un programa completo).Java (OpenJDK 9) ,
322220 bytesPrué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
fuente
Unsafe
instancia 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);}}
public static void main
por un inicializador estáticostatic{try{}catch(Exception e){}}
(que podría ser un poco más difícil de iniciar pero, sin embargo, es válido y compilable).a
lugar deargs
y elimine public. tio.run/nexus/…c, 9 bytes
Prueba:
fuente
gcc
es. Esto también debería funcionar con el programa vacío. Intentegcc src.c && valgrind ./a.out
, lo que debería producir un resultado limpio.C #, 109 bytes
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):
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.
fuente
C 30 bytes
Resultados de Valgrind:
fuente
main(){malloc(1);}
?Dart, 76 bytes
Un poco como la respuesta de JavaScript. Cuando llama
.listen
a 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().future
no funciona porque usar waitit es lo mismo que hacernew 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.
fuente
Rápido, 12 bytes
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. Los3
y5
son simplemente arbitrarios.La suscripción (indexación) an
Array<T>
produce unArraySlice<T>
. AnArraySlice<T>
es una vista de la memoria de la matriz a partir de la cual se creó.[3,5][0...0]
produce unArraySlice<Int>
, cuyo valor es[3]
. Tenga3
en cuenta que en este segmento es el mismo3
elemento que3
en el original que seArray
muestra 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
2
memoria 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:
fuente
Solución 1: C (Mac OS X x86_64), 109 bytes
La fuente de golf_sol1.c
El programa anterior debe compilarse con acceso de ejecución en el segmento __DATA.
Luego, para ejecutar el programa, ejecute lo siguiente:
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).
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
main
a 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:
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
El programa anterior debe compilarse con acceso de ejecución en el segmento __DATA.
Luego, para ejecutar el programa, ejecute lo siguiente:
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:
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:
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:
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:
fuente
Inglés simple ,
71705835 bytesSe 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:
Versión sin golf que es un programa completo, utiliza una definición de subtipo y no pierde memoria:
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.
fuente