Fuite de mémoire dans le moins d'octets possible


79

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


2
Peut-être que vous pourriez avoir différentes gammes de fuites et qu'en fonction de votre fuite, vous perdez x% de votre nombre d'octets
Christopher

11
@ChristopherPeart Pour un, je ne suis pas fan des bonus sur les défis et pour deux, comme vous l'avez déjà montré, il est très facile de perdre de la mémoire illimitée.
Wheat Wizard

1
Connexes . Ce n’est cependant pas un doublon, car la plupart des réponses à cette question forment une structure infinie et joignable en mémoire plutôt qu’une fuite de mémoire.

2
quelle est l'idée? Que le mem ne peut pas être libéré? J'imagine que cela nécessiterait une exécution native pour les langages ramassés par les ordures ou l'exploitation de bugs.
akostadinov

7
Je vois comment les langues conçues pour le golf échouent lamentablement sur celui-ci ...
Kh40tiK

Réponses:


89

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

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

16
J'ai aimé la réponse Unlambda, mais celle-ci est ( 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.
Zeppelin

11
@zeppelin: D'accord, mais selon nos règles, c'est l'implémentation qui définit le langage. Ainsi, si l'implémentation perd de la mémoire, tous les programmes de la langue perdent de la mémoire. Je ne suis pas nécessairement sûr d'être d'accord avec cette règle, mais pour le moment, il est trop enraciné pour pouvoir réellement changer.

8
Cela fonctionne également dans le noeud JS.
Dennis

6
On se croirait dans une nouvelle échappatoire standard ...
Michael Hampton

46
Enfin un script Perl que je peux comprendre.
user11153

66

C, 48 31 22 octets

Attention: ne le lancez pas trop souvent.

Merci à Dennis pour son aide et ses idées!

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

Cela va un peu plus loin. shmgetalloue 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:

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

Ou, pour 26 octets:

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

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 &ksera toujours la même chose. De nos jours, ASLR est généralement activé par défaut.


Vérification:

Vous pouvez utiliser ipcs -mpour voir quelle mémoire partagée existe sur votre système. J'ai supprimé les entrées préexistantes pour plus de clarté:

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

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


$ ./leakMem 

$ ipcs -m

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

0x0000007b 3375157    Riley      0          1          0  

1
@AndrewSavinykh Théoriquement, le shmid aurait pu être stocké dans un fichier et un programme pourrait s'y attacher à l'avenir. C’est ainsi que fonctionne la mémoire partagée unix ...
vendredi

1
@AndrewSavinykh La mémoire partagée devient une ressource que le système d'exploitation peut attribuer à d'autres processus. Il ressemble à un fichier qui réside dans la RAM et tout processus connaissant le nom (clé) y a accès jusqu'à ce qu'il soit supprimé. Imaginez un processus qui calcule un nombre et le stocke en mémoire et se termine avant que le processus qui lit les données ne se connecte à la mémoire partagée. Dans ce cas, si le système d'exploitation libère de la mémoire, le second processus ne peut pas l'obtenir.
Riley

35
Merci d'avoir posté ceci. Je viens de protéger TIO contre les fuites de mémoire partagée.
Dennis

4
@ Dennis C'est pourquoi je n'ai pas posté de lien TIO. Je ne savais pas si c'était protégé ou non.
Riley

12
J'aime la façon dont vous utilisez le mot problème pour décrire le scénario dans lequel le programme perd moins de mémoire que prévu.
Kasperd

40

Unlambda ( c-refcnt/unlambda), 1 octet

i

Essayez-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-refcntest 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

$ wget ftp://ftp.madore.org/pub/madore/unlambda/unlambda-2.0.0.tar.gz
…couper…
2017-02-18 18:11:08 (975 KB / s) - 'unlambda-2.0.0.tar.gz' a été enregistré [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
…couper…
== 3417 == RÉSUMÉ DES FUITES:
== 3417 == définitivement perdu: 40 octets en 1 blocs
== 3417 == perdu indirectement: 0 octet sur 0 bloc
== 3417 == éventuellement perdu: 0 octet sur 0 bloc
== 3417 == toujours accessible: 0 octet sur 0 bloc
== 3417 == supprimé: 0 octet sur 0 bloc
== 3417 == 
== 3417 == Pour le nombre d'erreurs détectées et supprimées, réexécutez-le avec: -v
== 3417 == RÉSUMÉ DES ERREURS: 1 erreurs provenant de 1 contextes (supprimé: 0 à 0)

39

TI-Basic, 12 octets

While 1
Goto A
End
Lbl A
Pause 

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


7
Wow, je pense que je m'en souviens. Je n'arrêtais pas de sauter dans mes anciens programmes de base et j'ai remarqué que ma TI-84 + devenait de plus en plus lente ...
Ray

2
Oui, la plupart d'entre nous connaissent le sentiment;) @RayKoopa
Timtech

13
+1 pour Ti Basic. J'ai passé la majeure partie de ma 9e année à programmer ces choses.
markasoftware

Avez-vous besoin Pause à la fin? Vous pourriez économiser 2 octets.
kamoroso94

@ kamoroso94 Je pense que oui, car "si un programme est terminé, la fuite est supprimée et ne causera plus de problèmes", il s'agit donc d'empêcher le programme de se terminer.
Timtech le

32

Python <3.6.5, 23 octets

property([]).__init__()

property.__init__fuites références à la propriété de vieux fget, fset, fdelet __doc__si vous l' appelez sur un déjà initialisé par propertyexemple. 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.)


Un rapport de bogue a-t-il été envoyé?
mbomb007


27

C #, 34 octets

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

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 :

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

Sortie après 10 minutes:

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

C'est 2 684 476 624 octets. Le total WorkingSetdu processus était d'environ 4,8 Go

Cet article d'Eric Lippert a inspiré cette réponse: Quand tout ce que tu sais est faux .


C'est fascinant. Est-ce que le ramasse-miettes "oublie" que certaines choses existent et en perd la trace à cause de cela? Je ne sais pas beaucoup sur c #. Aussi maintenant je me demande, quelle est la différence entre une bombe et une fuite? J'imagine qu'un fiasco similaire pourrait être créé en appelant un constructeur de l'intérieur d'un constructeur, ou en ayant une fonction infinie récursive qui ne s'arrête jamais, bien que techniquement le système ne perde jamais la trace de ces références, il manque juste de l'espace ...
Don lumineux

1
Un constructeur dans un constructeur provoquerait un débordement de pile. Mais le destructeur d'une instance est appelé dans une hiérarchie à plat. Le GC ne perd jamais la trace des objets. Chaque fois qu'il essaie de les détruire, il crée involontairement de nouveaux objets. Le code utilisateur n'a par contre pas accès à ces objets. De plus, les incohérences mentionnées peuvent survenir puisque le GC peut décider de détruire un objet sans appeler son destructeur.
MrPaulch

Le défi ne serait-il pas complet en utilisant simplement class L{~L(){new L();}}? Autant for(;;)que je sache, il fuit la mémoire plus rapidement, non?
BgrWorker

1
Malheureusement non. Étant donné que pour chaque objet détruit, une seule nouvelle instance va être créée, qui est à nouveau inaccessible et destinée à la destruction. Répéter. Un seul objet sera en attente de destruction. Pas de population croissante.
MrPaulch

2
Pas vraiment. Finalement, une finalisée sera ignorée. L'objet correspondant sera mangé quand même.
MrPaulch

26

C (gcc) , 15 octets

f(){malloc(1);}

Vérification

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

26

Javascript, 14 octets

Golfé

setInterval(0)

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

entrez la description de l'image ici

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.


5
Haha, j'adore le fait que vous ayez tapé 'Golfed', me rend curieux à propos de la version sans-golf
Martijn

9
cela pourrait ressembler à ceciif(window && window.setInterval && typeof window.setInterval === 'function') { window.setInterval(0); }
Tschallacka

3
En réalité, il n'est pas impossible d'annuler: les ID d'intervalle (et le délai d'attente) sont numérotés de manière séquentielle. Il est donc assez facile d'annuler la chose en appelant simplement clearIntervalavec un ID incrémenté jusqu'à la disparition de votre intervalle. Par exemple:for(let i=0;i<1e5;i++){try{clearInterval(i);}catch(ex){}}
user2428118

5
@ user2428118 Comme le dit Zeppelin, ce n'est pas plus légitime que d'affirmer que les fuites C / C ++ ne sont pas "réelles", car vous pourriez appeler la force brutefree()
TripeHound 21/02/2017

1
Wow, pas beaucoup de défis où JavaScript est un vrai prétendant ...
Jared Smith

19

Java, 10 octets

Enfin, une réponse compétitive en Java!

Golfé

". "::trim

C'est une référence de méthode (par rapport à une constante de chaîne), qui peut être utilisée comme ça:

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

Une chaîne littérale ". "sera automatiquement ajoutée au pool global de chaînes internées , telle que gérée par la java.lang.Stringclasse, 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).

...

Un pool de chaînes, initialement vide, est maintenu en privé par la classe String.

Toutes les chaînes littérales et les expressions constantes de valeur chaîne sont internées. Les littéraux de chaîne sont définis dans la section 3.10.5 de la spécification de langage Java ™.

...

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.


2
J'ai considéré cela pour C # ... mais je ne pense pas que cela compte, car, comme vous le dites, vous pouvez accéder à cette mémoire en incluant un autre littéral de chaîne. Je serais également intéressé de savoir ce ("." + " ").intern()qu’il ferait (s’il s’agissait d’une saisie utilisateur ou non, nous optimisons donc les optimisations du compilateur).
VisualMelon

1
En effet, le seul consensus est mince au mieux, je suis juste fermement du côté "le code devrait compiler". Je ne suis toujours pas sûr d’acheter cette solution étant donné le libellé de la question (ces chaînes ne peuvent pas être libérées, et elles peuvent être trouvées dans le code d’exploitation normal même s’il est peu probable), mais j’invite les autres à se faire leur propre jugement
VisualMelon

3
Cette chaîne n'est même pas inaccessible , à plus forte raison une fuite. Nous pouvons le récupérer à tout moment en internant une chaîne égale. Si cela devait compter, toute variable globale inutilisée (statique privée en Java) constituerait une fuite. Ce n'est pas ainsi que sont définies les fuites de mémoire.
user2357112

3
@ user2357112 "... Cette chaîne n'est même pas inaccessible ..." Cela n'a l'air évident que parce que vous voyez le code. Maintenant, considérez que vous avez obtenu cette référence de méthode X () comme argument de votre code, vous savez qu'elle alloue (et interne) un littéral de chaîne à l'intérieur, mais vous ne savez pas exactement lequel, il pourrait être "." Ou "123". ou toute autre chaîne d'une longueur (généralement) inconnue. Souhaitez-vous s'il vous plaît démontrer comment vous pouvez toujours y accéder, ou désallouer l'entrée dans le pool "stagiaire" qu'il occupe?
Zeppelin

2
@ user2357112 Sur une machine avec une mémoire finie, vous pouvez accéder à une valeur stockée dans une partie de la mémoire 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 tooPourriez-vous le démontrer? (indice, String.intern () est implémenté dans le code natif ).
Zeppelin

17

Rouille, 52 octets

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

Alloue quelques octets avec le système malloc. Cela suppose que le mauvais ABI est acceptable.


Rouille (en théorie), 38 octets

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

Nous allouons de la mémoire sur le tas, extrayons un pointeur brut, puis l’ignorons et le perdons. ( Box::into_rawest plus court alors std::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:

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

impressionnant. des articles comme celui-ci m'ont amené à explorer Rust au cours de la dernière année, sans aucun doute l'une des langues les plus amusantes que j'ai essayé d'apprendre.
Don lumineux

16

8086 ASM, 3 octets

Cet exemple suppose qu'un environnement d'exécution C est lié.

jmp _malloc

cela assemble à e9 XX XXXX XXest 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!


L'implémentation normale de malloc entraînera la libération de la mémoire à la sortie du processus.
Josué

@ Joshua Oui, mais c'est un comportement défini par l'implémentation.
FUZxxl

12

Forth, 6 octets

Golfé

s" " *

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

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

10

aller 45 octets

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

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'


vérification du golf: c'est plus court de 2 octets que d'appeler C.malloc(8), puisque vous en avez besoinimport"C"
Riking

9

Java 1.3, 23 octets

void l(){new Thread();}

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.

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

Étant donné que cela ne fonctionne que sur certaines versions de Java, vous devez plutôt indiquer "Java 3" dans votre en-tête.

5
Il n'y a rien de tel que Java 3. C'est Java 1.3. Il y avait Java 1.0, 1.1, 2, 1.3, 1.4, 5, 6, 7, 8, 9. Numérotation étrange, mais c'est comme ça.
Olivier Grégoire

C'était aussi mon idée. Zut.
Urne magique Octopus le

8

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.


8

Rapide 3, 38 octets

Nouvelle version:

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

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:

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

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


Hmm, vous pouvez toujours référencer cette mémoire à travers xet y, 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).
Zeppelin

@zeppelin La dernière ligne pourrait être encapsulée dans une fonction permettant de résoudre ce problème
NobodyNada,

@NobodyNada, si je mettais la dernière ligne d'un dobloc qui résoudrait le problème que Zeppelin a soulevé, n'est-ce pas?
Daniel

@ Dopapp Yeah; un dofonctionnerait aussi bien. Bonne idée!
NobodyNada

Il peut être raccourci, vous n'avez pas besoin de deux classes - X peut se référer à lui-même:class X{var x: X!};do{let x=X();x.x=x}
Sebastian Osiński

7

Delphi (Object Pascal) - 33 octets

Création d'un objet sans variable, programme de console complet:

program;begin TObject.Create;end.

L'activation de FastMM4 dans le projet affichera la fuite de mémoire:

entrez la description de l'image ici


6

C # - 84bytes

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

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 que Closeou Disposenon 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 IDisposablesdans 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é.


1
System.IO.File.Create("a")ne fuira rien, mais GC.SuppressFinalize(System.IO.File.Create("a"))il sera explicitement invité à ne pas exécuter le finaliseur du FileStreamproduit.
Jon Hanna

@ JonHanna tout à fait raison. Ma paranoïa identifiable semble avoir eu raison de moi.
VisualMelon

Vous aurez peut-être l'occasion de faire une fuite de GDI + à l'aide de System.Drawing.Bitmap. Je ne sais pas si cela compte car c'est une bibliothèque Windows à l'origine de la fuite, pas le programme lui-même.
BgrWorker

@ BgrWorker, ils ont sans doute un finaliseur également, et j’ai tendance à éviter les bibliothèques externes en code-golf car je ne suis pas d’accord avec le consensus sur leur coût: si vous pouvez trouver une solution en laquelle vous avez confiance, n'hésitez pas à poster dans ta propre réponse!
VisualMelon

<!-- language: lang-c# -->Merci pour cela et bonne réponse! (C'est C # alors j'adore ça.)
Metoniem

5

Facteur , 13 octets

Factor a une gestion automatique de la mémoire, mais donne également accès à certaines fonctionnalités de la libc:

1 malloc drop

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:

1 (malloc) drop

Tester les fuites avec [ 1 malloc drop ] leaks.dit:

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

Tester les fuites avec [ 1 (malloc) drop ] leaks.dit:

| Disposable class | Instances | |

Oh non! Faible facteur, il a Alzheimer maintenant! RÉ:


4

AutoIt , 39 octets

#include<Memory.au3>
_MemGlobalAlloc(1)

Alloue un octet du tas. Le descripteur renvoyé par _MemGlobalAllocétant ignoré, il est impossible de libérer explicitement cette allocation.


4

Common Lisp (SBCL uniquement), 28 26 octets

sb-alien::(make-alien int)

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

  1. 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 deux make-alienet intavec sb-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.

  2. make-alien alloue de la mémoire pour un type donné et une taille optionnelle (en utilisant malloc).

  3. Lorsque vous exécutez ceci dans la réplique, ajoutez 0aprè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!


1
Tu ne peux pas utiliser 1(ou 2, 3etc.) au lieu de ()sorte que vous revenez valeur 1? Cela économiserait 1 octet. Est-ce que cette réponse est seulement REPL? Peut-être que si vous chargez du code avec loadvous ne pouvez pas inclure ()ou quoi que ce soit à la fin, car il ne sera pas accessible de toute façon?

1
@ PrzemysławP Vous avez raison sur les deux points, j'ai essayé avec evalet cela fonctionne comme vous l'avez dit. Merci beaucoup!
Coredump

3

C ++, 16 octets

main(){new int;}

Je n'ai pas Valgrind pour vérifier les fuites, mais je suis tout à fait sûr. Sinon j'essaierais:

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

Résultat Valgrind

(Il fuit effectivement)

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

@ WheatWizard que j'utilise g++ 4.3.2(pas le plus récent) et il compile parfaitement. Aucun type de retour n'est intpar défaut, je pense. Avec -Wallj'ai un avertissement cependant:plop.cpp:1: warning: ISO C++ forbids declaration of 'main' with no type
matovitch

2
@ WheatWizard Désolé, je viens de voir que vous avez donné l'exemple c ++ pour lancer le concours. En venant de reddit, j'ai seulement regardé les réponses et (étrangement) je n'ai vu aucun C ++. Je me sens un peu bête. : /
matovitch

Le consensus est que vous pouvez compter uniquement []{new int;}comme une fonction C ++ (le défi ne spécifiait pas un programme complet).
Toby Speight

3

Java (OpenJDK 9) , 322 220 octets

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

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


Vous pouvez sauvegarder de nombreux octets en obtenant l' Unsafeinstance à 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);}}
zeppelin

Et vous pouvez économiser un peu plus en remplaçant le public static void mainpar un initialiseur statique static{try{}catch(Exception e){}}(ce qui pourrait être un peu plus délicat à lancer mais reste néanmoins valide et compilable).
Zeppelin

yhea l'utilisation du constructeur a été utilisé dans une version Android du code que j'ai utilisé. Je vais changer certaines choses quand je suis à la maison mais je vais suivre le chemin que vous avez suivi avec une seule déclaration;)
Serverfrog

Supprimez les espaces, utilisez-les à la aplace argset supprimez public. tio.run/nexus/…
Pavel

true peut être remplacé par 1> 0
masterX244

3

c, 9 octets

main(){}

Preuve:

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

1
Vous ne perdez pas réellement de mémoire; gccest. Cela devrait également fonctionner avec le programme vide. Essayez gcc src.c && valgrind ./a.out, ce qui devrait produire un résultat propre.

3

C #, 109 octets

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

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

En recherchant PurchaseOrder dans mon code , je trouve cette ligne de code dans l' page_loadune de mes pages.XmlSerializer serializer = new XmlSerializer(typeof(PurchaseOrder), new XmlRootAttribute(“”));

Cela semblerait être un morceau de code assez innocent. Nous créons un XMLSerializerpour PurchaseOrder. Mais que se passe-t-il sous les couvertures?

Si nous examinons le XmlSerializerconstructeur avec Reflector, nous constatons qu’il appelle, this.tempAssembly = XmlSerializer.GenerateTempAssembly(this.mapping, type, defaultNamespace, location, evidence);ce qui génère un assemblage temporaire (dynamique). Ainsi, chaque fois que ce code sera exécuté (c'est-à-dire chaque fois que la page sera affichée), il générera un nouvel assemblage.

La raison pour laquelle il génère un assemblage est qu’il doit générer des fonctions de sérialisation et de désérialisation, lesquelles doivent résider quelque part.

Ok, d'accord… ça crée un assemblage, et alors? Quand on en aura fini, ça devrait juste disparaître non?

Eh bien… un assemblage n'est pas un objet sur le tas GC, le GC ignore vraiment les assemblages, il ne sera donc pas récupéré. Le seul moyen de se débarrasser des assemblys dans les versions 1.0 et 1.1 consiste à décharger le domaine d'application dans lequel il réside.

Et c'est là que réside le problème, Dr Watson.

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.

Fenêtre Outils de diagnostic


2

C 30 octets

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

Résultats Valgrind:

         ==26311== HEAP SUMMARY:
         ==26311==     in use at exit: 4 bytes in 1 blocks
         ==26311==   total heap usage: 1 allocs, 0 frees, 4 bytes allocated
         ==26311== 
         ==26311== LEAK SUMMARY:
         ==26311==    definitely lost: 4 bytes in 1 blocks
         ==26311==    indirectly lost: 0 bytes in 0 blocks
         ==26311==      possibly lost: 0 bytes in 0 blocks
         ==26311==    still reachable: 0 bytes in 0 blocks
         ==26311==         suppressed: 0 bytes in 0 blocks
         ==26311== Rerun with --leak-check=full to see details of leaked memory

2
Est-il possible de simplement faire main(){malloc(1);}?
Kirbyfan64sos

@ Oui c'est ça! Mais sa déjà été posté!
Abel Tom

2

Dart, 76 octets

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

Un peu comme la réponse JavaScript. Lorsque vous appelez .listenun 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().futurene fonctionne pas car utiliser wait est la même chose que faire new 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.


si votre programme principal continuait à fonctionner et à faire d'autres tâches, le ramasse-miettes serait-il en mesure d'arrêter l'isolement? Je pose la question parce que mon exemple goroutine semble similaire ... J'ai supposé que le fait que le programme existe et restitue toute la mémoire au système d'exploitation ne signifie pas nécessairement qu'il n'a pas fui.
Don brillant

2

Rapide, 12 octets

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

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. Les 3et 5sont tout simplement arbitraire.

La souscription (indexation) an Array<T>produit un ArraySlice<T>. An ArraySlice<T>est une vue dans la mémoire du tableau à partir duquel il a été créé.

[3,5][0...0]produit un ArraySlice<Int>dont la valeur est [3]. Notez que 3dans cette tranche est le même 3élément que 3dans l'original Arrayindiqué 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 2taille 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:

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

2

Solution 1: C (Mac OS X x86_64), 109 octets

La source de golf_sol1.c

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

Le programme ci-dessus doit être compilé avec un accès d’exécution sur le segment __DATA.

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

Ensuite, pour exécuter le programme, exécutez ce qui suit:

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

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

                                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 

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:

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

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

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

Le programme ci-dessus doit être compilé avec un accès d’exécution sur le segment __DATA.

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

Ensuite, pour exécuter le programme, exécutez ce qui suit:

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

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:

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

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:

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

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:

main=65259;

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:

main(){}


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

2

Anglais simplifié , 71 70 58 35 octets

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

To x:
Allocate memory for a thing.

La version non-golfée qui est un programme complet, utilise une définition de sous-type et ne perd pas de mémoire:

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

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.