Votre tâche consiste à écrire du code qui perdra au moins un octet de mémoire dans le moins d'octets possible. La mémoire doit être libérée et pas seulement allouée .
La mémoire perdue est la mémoire allouée par le programme, mais perd sa capacité d'accès avant de pouvoir désallouer correctement la mémoire. Pour la plupart des langages de haut niveau, cette mémoire doit être allouée sur le tas.
Un exemple en C ++ serait le programme suivant:
int main(){new int;}
Cela fait un new int
sur le tas sans un pointeur. Cette mémoire est instantanément perdue car nous n'avons aucun moyen d'y accéder.
Voici à quoi pourrait ressembler un résumé de fuite 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
De nombreuses langues ont un débogueur de mémoire (tel que Valgrind ). Si vous le pouvez, vous devez inclure la sortie d'un tel débogueur pour confirmer que vous avez perdu de la mémoire.
L'objectif est de minimiser le nombre d'octets dans votre source.
Réponses:
Perl (5.22.2), 0 octet
Essayez-le en ligne!
Je savais qu'il y aurait une langue qui aurait perdu de la mémoire dans un programme vide. Je m'attendais à ce que ce soit un esolang, mais il s'avère que cela
perl
laisse une trace de la mémoire de tout programme. (Je suppose que c'est intentionnel, car libérer de la mémoire si vous savez que vous allez quitter va tout de même faire perdre du temps; en tant que telle, il est généralement recommandé de laisser fuir toute mémoire restante une fois que vous êtes dans les routines de sortie de votre programme. .)Vérification
la source
perl --version
à mon humble avis) trop distante, car c'est évidemment l'interpréteur lui-même qui fuit la mémoire, c'est-à-dire que je suis «définitivement perdu: 7 742 octets sur 14 blocs» lorsque je cours sur ma machine , même s’il n’arrive jamais à exécuter aucun programme.C,
48 3122 octetsAttention: ne le lancez pas trop souvent.
Merci à Dennis pour son aide et ses idées!
Cela va un peu plus loin.
shmget
alloue de la mémoire partagée qui n'est pas désallouée à la fin du programme. Il utilise une clé pour identifier la mémoire, nous utilisons donc un int non initialisé. Ce comportement est techniquement indéfini, mais cela signifie pratiquement que nous utilisons la valeur située juste au-dessus du sommet de la pile lorsque cela est appelé. Cela sera écrit la prochaine fois que quelque chose sera ajouté à la pile, nous perdrons donc la clé.Le seul cas où cela ne fonctionne pas est si vous pouvez déterminer ce qui était sur la pile auparavant. Pour 19 octets supplémentaires, vous pouvez éviter ce problème:
Ou, pour 26 octets:
Mais avec celui-ci, la mémoire est perdue après la fermeture du programme. Pendant l'exécution du programme, l'accès à la mémoire est contraire aux règles, mais après la fin du programme, nous perdons l'accès à la clé et la mémoire est toujours allouée. Cela nécessite la randomisation de la disposition de l'espace adresse (ASLR), sinon ce
&k
sera toujours la même chose. De nos jours, ASLR est généralement activé par défaut.Vérification:
Vous pouvez utiliser
ipcs -m
pour voir quelle mémoire partagée existe sur votre système. J'ai supprimé les entrées préexistantes pour plus de clarté:la source
Unlambda (
c-refcnt/unlambda
), 1 octetEssayez-le en ligne!
C'est vraiment un défi de trouver un interpréteur préexistant qui perd de la mémoire sur des programmes très simples. Dans ce cas, j'ai utilisé Unlambda. Il existe plus d'un interprète officiel Unlambda, mais il
c-refcnt
est l'un des plus faciles à créer. Il a ici la propriété utile de perdre de la mémoire lorsqu'un programme est exécuté avec succès. Donc, tout ce que je devais donner ici était le programme juridique le plus simple possible Unlambda, un no-op. (Notez que le programme vide ne fonctionne pas ici; la mémoire est toujours accessible lorsque l'interpréteur plante.)Vérification
la source
TI-Basic, 12 octets
"... une fuite de mémoire se produit lorsque vous utilisez un Saut / Lbl dans une boucle ou si conditionnel (tout élément comportant une commande End) pour sortir de cette structure de contrôle avant que la commande End ne soit atteinte ..." (plus)
la source
Pause
à la fin? Vous pourriez économiser 2 octets.Python <3.6.5, 23 octets
property.__init__
fuites références à la propriété de vieuxfget
,fset
,fdel
et__doc__
si vous l' appelez sur un déjà initialisé parproperty
exemple. Il s'agit d'un bogue, éventuellement signalé dans le numéro 31787 de CPython et corrigé dans Python 3.6.5 et Python 3.7.0 . (En outre, oui,property([])
vous pouvez le faire.)la source
C #, 34 octets
Cette solution ne nécessite pas le tas. Il faut juste un GC ( Garbage Collector ) vraiment dur .
Essentiellement, il transforme le GC en son propre ennemi.
Explication
Chaque fois que le destructeur est appelé, il crée de nouvelles instances de cette classe diabolique tant que le délai d'attente est écoulé et demande au GC de simplement abandonner cet objet sans attendre la fin du destructeur. D'ici là, des milliers de nouvelles instances ont été créées.
La "perversité" de ceci est, plus le GC travaille dur, plus cela va exploser dans votre visage.
Avertissement : Votre GC peut être plus intelligent que le mien. D'autres circonstances dans le programme peuvent amener le GC à ignorer le premier objet ou son destructeur. Dans ces cas, cela ne va pas exploser. Mais dans de nombreuses variantes, ce sera le cas . L'ajout de quelques octets ici et là peut garantir une fuite dans toutes les circonstances. Eh bien, sauf pour l'interrupteur d'alimentation peut-être.
Tester
Voici une suite de tests :
Sortie après 10 minutes:
C'est 2 684 476 624 octets. Le total
WorkingSet
du processus était d'environ 4,8 GoCet article d'Eric Lippert a inspiré cette réponse: Quand tout ce que tu sais est faux .
la source
class L{~L(){new L();}}
? Autantfor(;;)
que je sache, il fuit la mémoire plus rapidement, non?C (gcc) , 15 octets
Vérification
la source
Javascript, 14 octets
Golfé
Enregistre un gestionnaire d'intervalle vide avec un délai par défaut, en éliminant l'identifiant de minuterie résultant (rendant impossible l'annulation).
J'ai utilisé un intervalle autre que celui par défaut pour créer plusieurs millions de minuteries, afin d'illustrer la fuite, car l'utilisation d'un intervalle par défaut consomme le processeur comme un fou.
la source
if(window && window.setInterval && typeof window.setInterval === 'function') { window.setInterval(0); }
clearInterval
avec un ID incrémenté jusqu'à la disparition de votre intervalle. Par exemple:for(let i=0;i<1e5;i++){try{clearInterval(i);}catch(ex){}}
free()
Java, 10 octets
Enfin, une réponse compétitive en Java!
Golfé
C'est une référence de méthode (par rapport à une constante de chaîne), qui peut être utilisée comme ça:
Une chaîne littérale
". "
sera automatiquement ajoutée au pool global de chaînes internées , telle que gérée par lajava.lang.String
classe, et comme nous la coupons immédiatement, sa référence ne peut plus être réutilisée dans le code (à moins que vous ne déclariez à nouveau exactement la même chaîne).https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#intern--
Vous pouvez transformer ceci en une fuite de mémoire "de classe production", en ajoutant la chaîne à elle-même, puis en appelant explicitement la méthode intern () , dans une boucle.
la source
("." + " ").intern()
qu’il ferait (s’il s’agissait d’une saisie utilisateur ou non, nous optimisons donc les optimisations du compilateur).simply by guessing it correctly
, mais cela ne signifie pas pour autant qu'une fuite de mémoire n'existe pas.there's probably some way to use reflection to determine the string's contents too
Pourriez-vous le démontrer? (indice, String.intern () est implémenté dans le code natif ).Rouille, 52 octets
Alloue quelques octets avec le système malloc. Cela suppose que le mauvais ABI est acceptable.
Rouille (en théorie), 38 octetsNous allouons de la mémoire sur le tas, extrayons un pointeur brut, puis l’ignorons et le perdons. (
Box::into_raw
est plus court alorsstd::mem::forget
).Cependant, Rust utilise par défaut jemalloc, ce que valgrind ne peut détecter aucune fuite . Nous pourrions passer à l’allocateur système, mais cela ajoute 50 octets et requiert tous les soirs. Merci beaucoup pour la sécurité de la mémoire.
Résultat du premier programme:
la source
8086 ASM, 3 octets
Cet exemple suppose qu'un environnement d'exécution C est lié.
cela assemble à
e9 XX XX
oùXX XX
est l'adresse relative de_malloc
Cela appelle
malloc
à allouer une quantité de mémoire imprévisible, puis revient immédiatement, mettant fin aux processus. Sur certains systèmes d'exploitation tels que DOS, il est possible que la mémoire ne soit plus récupérable avant le redémarrage du système!la source
Forth, 6 octets
Golfé
Alloue une chaîne vide avec
s" "
, laissant son adresse et sa longueur (0) sur la pile, puis les multiplie (entraînant la perte d'une adresse mémoire).Valgrind
la source
aller 45 octets
cela crée un goroutine anonyme avec une boucle infinie à l'intérieur. le programme peut continuer à fonctionner normalement, car démarrer goroutine revient à créer un petit thread exécutant simultanément, mais le programme ne dispose d'aucun moyen pour récupérer la mémoire allouée à goroutine. le ramasse-miettes ne le collectera jamais non plus puisqu'il est toujours en cours d'exécution. certaines personnes appellent cela 'une fuite de goroutine'
la source
C.malloc(8)
, puisque vous en avez besoinimport"C"
Java 1.3, 23 octets
Créer un fil mais ne pas le démarrer. Le thread est inscrit dans le pool de threads interne, mais ne sera jamais démarré, donc jamais terminé et par conséquent, ne jamais être candidat au GC. C'est un objet irrécupérable, coincé dans les limbes Java.
C'est un bogue en Java jusqu'à la 1.3 inclus car il a été corrigé par la suite.
Essai
Le programme suivant veille à polluer la mémoire avec de nouveaux objets thread et à afficher un espace mémoire libre décroissant. Afin de tester les fuites, je fais fonctionner le CPG de manière intensive.
la source
Befunge ( champignons ), 1 octet
Cela peut dépendre de la plate-forme et de la version (je n’ai testé que la version 1.0.4 sous Windows), mais les champignons ont toujours été un interpréteur très fuyant. La
$
commande (drop) ne devrait rien faire sur une pile vide, mais le bouclage sur ce code parvient en quelque sorte à perdre beaucoup de mémoire très rapidement. En quelques secondes, il aura épuisé quelques concerts et tombera en panne avec une erreur "mémoire insuffisante".Notez que cela ne doit pas nécessairement être une
$
commande - n'importe quoi ferait l'affaire. Cela ne fonctionnera pas avec un fichier source vide cependant. Il doit y avoir au moins une opération.la source
Rapide 3, 38 octets
Nouvelle version:
x
a une forte référence à lui-même, il ne sera donc pas désalloué, ce qui entraînerait une fuite de mémoire.Ancienne version:
x
contient une référence forte ày
, et vice versa. Ainsi, ni l'un ni l'autre ne seront désalloués, entraînant une fuite de mémoire.la source
x
ety
, donc cela ne ressemble pas vraiment à une fuite pour moi (à moins que vous ne les détruisiez d'une manière ou d'une autre).do
bloc qui résoudrait le problème que Zeppelin a soulevé, n'est-ce pas?do
fonctionnerait aussi bien. Bonne idée!class X{var x: X!};do{let x=X();x.x=x}
Delphi (Object Pascal) - 33 octets
Création d'un objet sans variable, programme de console complet:
L'activation de FastMM4 dans le projet affichera la fuite de mémoire:
la source
C # - 84bytes
Cela alloue exactement 1 octet de mémoire non gérée, puis perd le
IntPtr
, ce qui, à mon avis, est le seul moyen de l'obtenir ou de le libérer. Vous pouvez le tester en le plaçant dans une boucle et en attendant que l'application se bloque (vous voudrez peut-être ajouter quelques zéros pour accélérer les choses).J'ai envisagé
System.IO.File.Create("a");
, etc., mais je ne suis pas convaincu qu'il s'agisse nécessairement de fuites de mémoire, car l'application elle - même collectera la mémoire. C'est le système d'exploitation situé en dessous qui risque de fuir (parce queClose
ouDispose
non appelé). L'accès aux fichiers nécessite également des autorisations du système de fichiers, et personne ne veut les utiliser. Et il s'avère que cela ne coulera pas de toute façon, car rien n'empêche d'appeler le finaliseur (ce qui libère les ressources sous-jacentes est possible), ce que le cadre inclut pour atténuer ce type d'erreur de jugement (dans une certaine mesure), et confondre les programmeurs avec un verrouillage de fichier apparemment non déterministe (si vous êtes cynique). Merci à Jon Hanna de m'avoir mis au clair.Je suis un peu déçu de ne pouvoir trouver un moyen plus court. Le .NET GC fonctionne, je ne vois aucun mal
IDisposables
dans mscorlib qui va définitivement fuir (et en fait, ils semblent tous avoir des finaliseurs, quel ennui) , je ne connais pas d'autre moyen d'allouer de la mémoire non gérée (à l'exception de PInvoke ), et la réflexion garantit que tout ce qui y est référé (quelle que soit la sémantique de la langue (par exemple, les membres privés ou les classes sans accesseurs)) peut être trouvé.la source
System.IO.File.Create("a")
ne fuira rien, maisGC.SuppressFinalize(System.IO.File.Create("a"))
il sera explicitement invité à ne pas exécuter le finaliseur duFileStream
produit.<!-- language: lang-c# -->
Merci pour cela et bonne réponse! (C'est C # alors j'adore ça.)Facteur , 13 octets
Factor a une gestion automatique de la mémoire, mais donne également accès à certaines fonctionnalités de la libc:
Alloue manuellement 1 octet de mémoire, renvoie son adresse et la supprime.
malloc
enregistre réellement une copie pour garder une trace des fuites de mémoire et double, mais identifier celle que vous avez fuite n’est pas une tâche facile.Si vous préférez vous assurer de perdre vraiment cette référence:
Tester les fuites avec
[ 1 malloc drop ] leaks.
dit:Tester les fuites avec
[ 1 (malloc) drop ] leaks.
dit:Oh non! Faible facteur, il a Alzheimer maintenant! RÉ:
la source
AutoIt , 39 octets
Alloue un octet du tas. Le descripteur renvoyé par
_MemGlobalAlloc
étant ignoré, il est impossible de libérer explicitement cette allocation.la source
Common Lisp (SBCL uniquement),
2826 octetsVous exécutez comme ceci:
sbcl --eval 'sb-alien::(make-alien int)'
; rien n'est imprimé ni retourné, mais l'allocation de mémoire se produit. Si j'emballe le formulaire à l'intérieur d'un(print ...)
, le pointeur est affiché dans le REPL.package::(form)
SBCL est une notation spéciale permettant de lier temporairement le package actuel lors de la lecture d'un formulaire. Ceci est utilisé ici pour éviter de préfixer les deuxmake-alien
etint
avecsb-alien
. Je pense qu'il serait trompeur de supposer que le package actuel est configuré sur celui-ci, car ce n'est pas le cas au démarrage.make-alien
alloue de la mémoire pour un type donné et une taille optionnelle (en utilisant malloc).Lorsque vous exécutez ceci dans la réplique, ajoutez
0
après l'allocation pour que la réplique ne retourne pas le pointeur, mais cette valeur. Dans le cas contraire, ce serait pas une fuite réelle parce que le REPL se rappelle les trois dernières valeurs retournées (voir*
,**
,***
) et nous pourrions encore avoir une chance de libérer la mémoire allouée.2 octets supprimés grâce à PrzemysławP, merci!
la source
1
(ou2
,3
etc.) au lieu de()
sorte que vous revenez valeur1
? Cela économiserait 1 octet. Est-ce que cette réponse est seulement REPL? Peut-être que si vous chargez du code avecload
vous ne pouvez pas inclure()
ou quoi que ce soit à la fin, car il ne sera pas accessible de toute façon?eval
et cela fonctionne comme vous l'avez dit. Merci beaucoup!C ++, 16 octets
Je n'ai pas Valgrind pour vérifier les fuites, mais je suis tout à fait sûr.Sinon j'essaierais:Résultat Valgrind
(Il fuit effectivement)
la source
g++ 4.3.2
(pas le plus récent) et il compile parfaitement. Aucun type de retour n'estint
par défaut, je pense. Avec-Wall
j'ai un avertissement cependant:plop.cpp:1: warning: ISO C++ forbids declaration of 'main' with no type
[]{new int;}
comme une fonction C ++ (le défi ne spécifiait pas un programme complet).Java (OpenJDK 9) ,
322220 octetsEssayez-le en ligne!
Ceci est une autre fuite de mémoire qui n'utilise pas le cache de chaînes. Il alloue la moitié de votre RAM et vous ne pouvez rien faire avec.
Merci à zeppelin pour avoir sauvegardé tous les octets
la source
Unsafe
instance à partir de la variable statique qui s'y trouve, comme ceci: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
par un initialiseur statiquestatic{try{}catch(Exception e){}}
(ce qui pourrait être un peu plus délicat à lancer mais reste néanmoins valide et compilable).a
placeargs
et supprimez public. tio.run/nexus/…c, 9 octets
Preuve:
la source
gcc
est. Cela devrait également fonctionner avec le programme vide. Essayezgcc src.c && valgrind ./a.out
, ce qui devrait produire un résultat propre.C #, 109 octets
Nous avons trouvé l'idée derrière cette fuite dans le code de production et les recherches conduisent à cet article. Le problème principal est dans cette longue citation de l'article (lisez-la pour plus d'informations):
L'exécution à partir du compilateur dans Visual Studio 2015 et à l'aide de la fenêtre Outils de diagnostic affiche les résultats suivants après environ 38 secondes. Notez que la mémoire de processus ne cesse de croître et que le Garbage Collector (GC) continue de fonctionner mais ne peut rien collecter.
la source
C 30 octets
Résultats Valgrind:
la source
main(){malloc(1);}
?Dart, 76 octets
Un peu comme la réponse JavaScript. Lorsque vous appelez
.listen
un objet de flux Dart, vous recevez un StreamSubscription qui vous permet de vous déconnecter du flux. Cependant, si vous le rejetez, vous ne pouvez jamais vous désabonner du flux, ce qui provoquerait une fuite. Le seul moyen de remédier à la fuite est de collecter le Stream lui-même, mais de le référencer en interne par un combo StreamController + Timer.Malheureusement, Dart est trop intelligent pour les autres choses que j'ai essayées.
()async=>await new Completer().future
ne fonctionne pas car utiliser wait est la même chose que fairenew Completer().future.then(<continuation>)
, ce qui permet de détruire la fermeture elle-même. Le second Completer n'est pas référencé (Completer détient une référence à Future de.future
, Future conserve une référence à la suite).De plus, les isolats (aka threads) sont nettoyés par GC. Par conséquent, vous créer un nouveau thread et le suspendre immédiatement (
import'dart:isolate';main(_)=>Isolate.spawn(main,0,paused:true);
) ne fonctionne pas. Même lors de la création d'un isolement avec une boucle infinie (import'dart:isolate';f(_){while(true){print('x');}}main()=>Isolate.spawn(f,0);
), cet isolement est supprimé et le programme est quitté.Tant pis.
la source
Rapide, 12 octets
Explication:
Il s’agit d’une fuite de mémoire de facto qui peut se produire dans n’importe quelle langue, que la langue utilise la gestion manuelle de la mémoire, le comptage automatique des références (ARC, comme Swift) ou même un balayage complet.
[3,5]
est juste un littéral de tableau. Ce tableau alloue suffisamment de mémoire pour au moins ces 2 éléments. Les3
et5
sont tout simplement arbitraire.La souscription (indexation) an
Array<T>
produit unArraySlice<T>
. AnArraySlice<T>
est une vue dans la mémoire du tableau à partir duquel il a été créé.[3,5][0...0]
produit unArraySlice<Int>
dont la valeur est[3]
. Notez que3
dans cette tranche est le même3
élément que3
dans l'originalArray
indiqué ci-dessus, pas une copie.La tranche obtenue peut ensuite être stockée dans une variable et utilisée. Le tableau d'origine n'est plus référencé, vous pouvez donc penser qu'il pourrait être désalloué. Cependant, cela ne peut pas.
Etant donné que la tranche expose une vue sur la mémoire du tableau d’où elle vient, le tableau d’origine doit rester en vie aussi longtemps que la tranche est conservée. Ainsi, sur la
2
taille des éléments d' origine alloués à la mémoire, seule la mémoire dont la taille est la première est utilisée, l'autre devant exister pour ne pas allouer le premier. La deuxième taille d'élément de la mémoire subit une fuite de facteur.La solution à ce problème est de ne pas garder en vie de petites tranches de grands tableaux. Si vous devez conserver le contenu de la tranche, transmettez-le à un tableau, ce qui déclenchera la copie de la mémoire, supprimant ainsi la dépendance vis-à-vis de la mémoire du tableau d'origine:
la source
Solution 1: C (Mac OS X x86_64), 109 octets
La source de golf_sol1.c
Le programme ci-dessus doit être compilé avec un accès d’exécution sur le segment __DATA.
Ensuite, pour exécuter le programme, exécutez ce qui suit:
Résultats:
Malheureusement, Valgrind ne surveille pas la mémoire allouée par les appels système. Je ne peux donc pas montrer une fuite détectée.
Cependant, nous pouvons regarder vmmap pour voir le gros bloc de mémoire allouée (métadonnées MALLOC).
Explication
Je pense donc avoir besoin de décrire ce qui se passe réellement ici avant de passer à la solution améliorée.
Cette fonction principale abuse de la déclaration de type manquante de C (elle est donc définie par défaut sur int sans que nous ayons besoin de gaspiller des caractères pour l'écrire), ainsi que sur le fonctionnement des symboles. L'éditeur de liens se soucie seulement de savoir s'il peut ou non trouver un symbole appelé
main
à appeler. Nous créons donc ici un tableau d’inters que nous initialisons avec notre shellcode qui sera exécuté. De ce fait, main ne sera pas ajouté au segment __TEXT, mais plutôt au segment __DATA. Nous devons donc compiler le programme avec un segment exécutable __DATA.Le shellcode trouvé dans main est le suivant:
Cela appelle la fonction syscall pour allouer une page de mémoire (le syscall mach_vm_allocate utilise en interne). RAX doit être égal à 0x100000a (indique au syscall quelle fonction nous voulons), alors que RDI conserve la cible pour l'allocation (dans notre cas, nous voulons que ce soit mach_task_self ()), RSI doit conserver l'adresse pour écrire le pointeur sur la mémoire nouvellement créée. (nous pointons simplement sur une section de la pile), RDX conserve la taille de l'allocation (nous passons juste en RAX ou 0x100000a juste pour économiser des octets), R10 détient les drapeaux (nous indiquons qu'il peut être alloué n'importe où).
À présent, il n’est pas clairement évident d’où RAX et RDI tirent leurs valeurs. Nous savons que RAX doit être 0x100000a et que RDI doit être la valeur renvoyée par mach_task_self (). Heureusement, mach_task_self () est en réalité une macro pour une variable (mach_task_self_), qui est toujours à la même adresse mémoire (devrait changer au redémarrage). Dans mon cas particulier, mach_task_self_ se trouve à 0x00007fff7d578244. Donc, pour réduire les instructions, nous allons plutôt transmettre ces données d’argv. C'est pourquoi nous lançons le programme avec cette expression
$(ruby -e 'puts "\xf5\xff\xff\xfe\xff\xff\x44\x82\x57\x7d\xff\x7f"')
pour le premier argument. La chaîne est constituée des deux valeurs combinées. La valeur RAX (0x100000a) n’est que de 32 bits et l’application est complétée par un complément (il n’ya donc pas d'octets nuls; nous ne voulons PAS la valeur pour obtenir l'original), la valeur suivante est le RDI (0x00007fff7d578244) qui a été décalé vers la gauche avec 2 octets supplémentaires inutiles ajoutés à la fin (encore une fois pour exclure les octets nuls, nous le décalons simplement vers la droite pour le ramener à l'original).Après l'appel système, nous écrivons dans notre mémoire nouvellement allouée. La raison en est que la mémoire allouée à l'aide de mach_vm_allocate (ou de cet appel système) est en fait une page de machine virtuelle et n'est pas automatiquement paginée en mémoire. Elles sont plutôt réservées jusqu'à ce que des données leur soient écrites, puis ces pages sont mappées en mémoire. Je ne savais pas si cela satisferait aux exigences s'il était seulement réservé.
Pour la prochaine solution, nous tirerons parti du fait que notre shellcode n'a pas d'octet nul, et peut donc le déplacer en dehors du code de notre programme pour réduire la taille.
Solution 2: C (Mac OS X x86_64), 44 octets
La source de golf_sol2.c
Le programme ci-dessus doit être compilé avec un accès d’exécution sur le segment __DATA.
Ensuite, pour exécuter le programme, exécutez ce qui suit:
Le résultat devrait être le même qu'auparavant, car nous faisons une allocation de la même taille.
Explication
Suit à peu près le même concept que la solution 1, à l'exception du fait que nous avons déplacé la majeure partie de notre code qui fuit en dehors du programme.
Le shellcode trouvé dans main est maintenant le suivant:
Cela copie essentiellement le shellcode que nous passons dans argv après ce code (donc, une fois copié, il exécutera le shellcode inséré). Ce qui fonctionne en notre faveur, c'est que le segment __DATA aura au moins une taille de page. Par conséquent, même si notre code n'est pas si volumineux, nous pouvons toujours en écrire plus en toute sécurité. L’inconvénient est la solution idéale ici, elle n’aurait même pas besoin de la copie, elle appellerait et exécuterait directement le shellcode dans argv. Mais malheureusement, cette mémoire ne dispose pas de droits d'exécution. Nous pourrions modifier les droits de cette mémoire, mais cela nécessiterait plus de code que la simple copie. Une stratégie alternative consisterait à modifier les droits d'un programme externe (mais nous en parlerons plus tard).
Le shellcode que nous passons à argv est le suivant:
Cela ressemble beaucoup à notre code précédent, la seule différence étant que nous incluons directement les valeurs pour EAX et RDI.
Solution possible 1: C (Mac OS X x86_64), 11 octets
L’idée de modifier le programme en externe nous donne la solution possible de transférer le système qui fuit vers un programme externe. Où notre programme actuel (soumission) est juste un programme factice, et le programme de fuite allouera de la mémoire dans notre programme cible. Maintenant, je ne savais pas si cela entrerait dans les règles de ce défi, mais je le partageais néanmoins.
Donc, si nous utilisions mach_vm_allocate dans un programme externe avec la cible définie pour notre programme de mise au défi, cela pourrait signifier que notre programme de mise à l'épreuve n'aurait besoin que de ressembler à ce qui suit:
Lorsque ce shellcode est simplement un saut bref vers lui-même (saut / boucle infini), le programme reste ouvert et nous pouvons le référencer à partir d'un programme externe.
Solution possible 2: C (Mac OS X x86_64), 8 octets
Curieusement, quand je regardais la sortie de valgrind, je voyais qu'au moins selon valgrind, dyld perdait de la mémoire. Si bien que chaque programme perd de la mémoire. Ceci étant le cas, nous pourrions en fait créer un programme qui ne fait rien (se ferme tout simplement) et qui perdra effectivement de la mémoire.
La source:
la source
Anglais simplifié ,
71705835 octetsSuppression d'un octet en supprimant une ligne vide. Suppression de 12 octets en éliminant la définition du type "bogon" et en utilisant le type "chose" parent au lieu du sous-type "bogon". Suppression de 23 octets en passant d'un programme complet à une routine qui perd de la mémoire.
Version golfée:
La version non-golfée qui est un programme complet, utilise une définition de sous-type et ne perd pas de mémoire:
Si la version golfée de "x" est appelée, elle perdra de la mémoire proportionnellement au nombre de fois que "x" est appelé. Dans la version golfée, "Deallocate the thing". réparerait la fuite de mémoire.
Plain English vérifie les fuites de mémoire par défaut. Lorsque la version qui perd de la mémoire est exécutée, une boîte de dialogue apparaît juste avant la fermeture du programme. La boîte de dialogue porte le titre "débogage", le message "1 goutte à goutte" et le bouton "OK". Plus on appelle la fonction qui fuit, plus le nombre de "gouttes" dans le message est grand. Lorsque la version qui ne perd pas de mémoire est exécutée, la boîte de dialogue ne s'affiche pas.
En clair, un "objet" est un pointeur sur un élément de la liste doublement chaînée. "Chose", "pour démarrer" et "pour arrêter" sont définis dans un module appelé "la nouille", qui doit être copié (généralement sous forme de fichier séparé) dans chaque projet. "A", "le", "à", "pour allouer de la mémoire" et "pour détruire" sont définis dans le compilateur.
la source