Belleği olabildiğince az bayt olarak sızdır


79

Göreviniz, olabildiğince az sayıda baytta en az bir bayt bellek sızdıracak kod yazmaktır. Hafıza sadece tahsis edilmemiş halde sızdırılmalıdır .

Sızan hafıza, programın ayırdığı hafızadır ancak hafızayı doğru bir şekilde tahsis etmeden önce erişim kabiliyetini kaybeder. Çoğu yüksek seviye dil için bu hafızanın öbek üzerinde tahsis edilmesi gerekir.

C ++ 'da bir örnek şu program olacaktır:

int main(){new int;}

Bu new intbir işaretçi olmadan öbek üzerinde yapar . Bu hafıza anında sızdı çünkü erişme şansımız yok.

Valgrind'den bir sızıntı özeti şöyle görünebilir:

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

Çoğu dilde, bellek sızıntısı olduğunu doğrulamak için böyle bir hata ayıklayıcısının çıktısını eklemeniz gerekebiliyorsa, bir bellek hata ayıklayıcısına ( Valgrind gibi ) sahiptir.

Amaç, kaynağınızdaki bayt sayısını en aza indirmektir.


2
Belki de farklı miktarlarda sızıntı olabilir ve ne kadar sızacağınıza bağlı olarak bayt sayınızın% x'ini kaybedersiniz
Christopher

11
@ChristopherPeart Birincisi, zorluklar konusundaki bonusların hayranı değilim ve gösterdiğiniz gibi ikisi için sınırsız hafızaya sızmak çok kolay.
Buğday Sihirbazı

1
İlgili . Yine de, yinelenen bir şey değil, çünkü bu sorunun çoğu cevabı gerçekte bellek sızdırmak yerine bellekte sınırsız erişilebilir bir yapı oluşturur.

2
fikir ne Mem'in serbest bırakılamayacağını mı? Sanırım bu, toplanan çöp dilleri için yerel uygulama veya böcekleri istismar etmeyi gerektirir.
akostadinov

7
Golf için tasarlanan dillerin bu
derste

Yanıtlar:


89

Perl (5.22.2), 0 bayt

Çevrimiçi deneyin!

Boş bir programda hafızayı sızdıran bir dil olacağını biliyordum. Bir esolang olmasını bekliyordum, ancak perlherhangi bir programda bellek sızdıran ortaya çıktı . (Bunun kasıtlı olduğunu varsayıyorum, çünkü herhangi bir şekilde çıkacağınızı biliyorsanız hafızayı boşaltmak sadece zamanınızı boşa harcıyor; bu nedenle, bugünlerde genel olarak önermek, programınızın çıkış yordamlarında olduğunuzda kalan herhangi bir hafızayı sızdırmaktır. .)

Doğrulama

$ 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
Ben Unlambda cevabı sevdim, ama belli ki bellek sızdırıyor tercüman kendisi yani ben `kesinlikle kaybolmak olduğu gibi bu bir, (IMHO) bir streç çok fazla: Ben çalıştırdığınızda 7742 bayt 14 blocks` içinde perl --versionbenim makinede , hiçbir zaman bir programı çalıştırmayacak olmasına rağmen.
zeplin

11
@zeppelin: Kabul, ancak kurallarımıza göre, dili tanımlayan uygulama, bu nedenle uygulama hafıza sızdırıyorsa, dildeki tüm programlar hafızayı sızdırıyor. Bu kural ile aynı fikirde olduğumdan emin değilim ama bu noktada gerçekten değişemeyecek kadar sağlam.

8
Bu ayrıca Node JS'de çalışır.
Dennis,

6
Bu yapımda yeni bir standart boşluk gibi görünüyor ...
Michael Hampton

46
Sonunda anlayabildiğim bir Perl betiği.
user11153

66

C, 48 31 22 bayt

Uyarı: Bunu çok fazla çalıştırmayın.

Çok sayıda yardım / fikir için Dennis'e teşekkürler !

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

Bu bir adım daha ileri gider. shmgetProgram sona erdiğinde, ayrılmayan paylaşılan hafıza ayırır. Belleği tanımlamak için bir anahtar kullanır, bu yüzden başlatılmamış bir int kullanırız. Bu teknik olarak tanımlanmamış bir davranıştır, ancak pratik olarak, bu çağrıldığında yığının hemen üstünde olan değeri kullandığımız anlamına gelir. Bir dahaki sefere yığına herhangi bir şey eklendiğinde yazılacak, bu yüzden anahtarı kaybedeceğiz.


Bunun işe yaramadığı tek durum, daha önce yığında ne olduğunu bulabilmenizdir. Fazladan 19 bayt için bu sorunu önleyebilirsiniz:

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

Veya, 26 bayt için:

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

Ancak bununla, program bittikten sonra bellek sızdırıyor. Programı çalıştırırken kurallara aykırı olan belleğe erişebilir, ancak program sona erdikten sonra anahtara erişimimizi kaybeder ve bellek hala tahsis edilir. Bu, adres alanı düzen randomizasyonunu (ASLR) gerektirir, aksi takdirde &kher zaman aynı olacaktır. Günümüzde ASLR genellikle varsayılan olarak açıktır.


Doğrulama:

ipcs -mSisteminizde hangi paylaşılan hafızanın olduğunu görmek için kullanabilirsiniz . Netlik için önceden var olan girişleri kaldırdım:

$ 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 Teorik olarak, shmid bir dosyada saklanmış olabilir ve bir program gelecekte buna ekleyebilirdi. Unix paylaşılan hafızanın işleyiş şekli budur ...
17:07

1
@AndrewSavinykh Paylaşılan bellek temel olarak işletim sisteminin diğer işlemlere verebileceği bir kaynak haline gelir. RAM'de yaşayan bir dosyaya benzer ve bu adın (anahtar) adını bilen herhangi bir işlem silinene kadar erişebilir. Bir sayıyı hesaplayan ve onu bellekte depolayan ve verileri okuyan işlemlerin paylaşılan belleğe bağlanmasından önce çıkan bir işlem hayal edin. Bu durumda, işletim sistemi belleği boşaltırsa, ikinci işlem bunu alamaz.
Riley,

35
Bunu gönderdiğiniz için teşekkür ederiz. TIO'yu sadece paylaşılan bellek sızıntılarına karşı korudum.
Dennis,

4
@Dennis Bu yüzden bir TIO bağlantısı kurmadım. Korunup korunmadığını bilmiyordum.
Riley,

12
Problem kelimesini , programın amaçlanandan daha az hafıza sızdırdığı senaryoyu tanımlamak için kullanma şeklini seviyorum .
kasperd

40

Unlambda ( c-refcnt/unlambda), 1 bayt

i

Çevrimiçi deneyin!

Bu gerçekten çok basit programlarda hafıza sızdıran önceden var olan bir tercüman bulma konusunda bir zorluktur. Bu durumda, Unlambda kullandım. Birden fazla resmi Unlambda tercümanı var, ancak c-refcntyapımı en kolay olanlardan biri ve burada bir program başarılı bir şekilde çalıştığında hafızayı sızdırması yararlı bir özelliği var. Bu yüzden burada vermem gereken tek şey mümkün olmayan en basit yasal Unlambda programıydı. (Boş programın burada çalışmadığını unutmayın; hafızaya tercüman çöktüğünde de erişilebilir durumdadır.)

Doğrulama

$ wget ftp://ftp.madore.org/pub/madore/unlambda/unlambda-2.0.0.tar.gz
... snip ...
2017-02-18 18:11:08 (975 KB / s) - 'unlambda-2.0.0.tar.gz' kaydedildi [492894]
$ tar xf unlambda-2.0.0.tar.gz 
$ cd unlambda-2.0.0 / c-refcnt /
$ gcc unlambda.c
$ echo -ni | valgrind. / a.out / dev / stdin
... snip ...
== 3417 == SIZIN ÖZETİ:
== 3417 == kesinlikle kayıp: 1 blokta 40 bayt
== 3417 == dolaylı olarak kayıp: 0 blokta 0 bayt
== 3417 == muhtemelen kaybedildi: 0 blokta 0 bayt
== 3417 == hala erişilebilir: 0 blokta 0 bayt
== 3417 == bastırılmış: 0 blokta 0 bayt
== 3417 == 
== 3417 == Algılanan ve engellenen hataların sayısı için, şunu tekrarlayın: -v
== 3417 == HATA ÖZETİ: 1 bağlamdan 1 hata (bastırılmış: 0'dan 0)

39

TI-Basic, 12 bayt

While 1
Goto A
End
Lbl A
Pause 

"... bir bellek sızıntısı, bir döngü içinde bir Goto / Lbl kullandığınız yerde veya End komutuna erişilmeden önce bu kontrol yapısından çıkmak için koşullu ise (End komutuna sahip herhangi bir şey) ..." (diğer)


7
Vay, sanırım bunu hatırladım. Eski temel programlarımdaki döngülerden atlamaya devam ettim ve TI-84 + 'ümün nasıl daha yavaş ve yavaşlaştığını fark ettim ...
Ray

2
Evet, çoğumuz bu duyguyu biliyoruz;) @RayKoopa
Timtech

13
Ti Basic için +1. 9. sınıf yılımın çoğunu bu şeyleri programlayarak geçirdim.
markasoftware

Pause Sonunda ihtiyacınız var mı? 2 byte tasarruf edebilirsiniz.
kamoroso94

@ kamoroso94 Sanırım, çünkü "Bir program sona ererse, sızıntı temizlenir ve başka sorun çıkmaz", bu nedenle programın bitmesini durdurmaktır.
Timtech

32

Python <3.6.5, 23 bayt

property([]).__init__()

property.__init__için sızıntıları referanslar mülkiyet eski fget, fset, fdelve __doc__sen halihazırda başlatılmış üzerinde dersek propertyörneği. Sonunda CPython sayısının 31787'nin bir parçası olarak bildirilen ve Python 3.6.5 ve Python 3.7.0'da düzeltilen bir hatadır . (Ayrıca, evet, property([])yapabileceğiniz bir şeydir.)


Bir hata raporu gönderildi mi?
mbomb007


27

C #, 34 bayt

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

Bu çözüm Öbek gerektirmez. Sadece çalışkan bir GC'ye ( Çöp Toplayıcı ) ihtiyacı var.

Temel olarak GC'yi kendi düşmanı haline getirir.

açıklama

Yıkıcı ne zaman çağrılırsa, zaman aşımı sona erdiği ve GC'nin yıkıcı bitmesini beklemeden o nesneyi sadece kesmesini söylediği sürece, bu kötü sınıfın yeni örneklerini yaratır. O zamana kadar binlerce yeni örnek yaratıldı.

Bunun “kötülüğü”, GC ne kadar çok çalışırsa, bu o kadar çok yüzünüze patlar.

Yasal Uyarı : GC'niz benimkinden daha akıllı olabilir. Programdaki diğer koşullar, GC'nin ilk nesneyi veya onun yıkıcısını yoksaymasına neden olabilir. Bu durumlarda bu patlamayacaktır. Fakat birçok varyasyonda olacaktır . Buraya birkaç bayt eklemek ve olası her koşul için bir sızıntı olabilir. Belki güç düğmesi hariç.

Ölçek

İşte bir test paketi :

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

10 dakika sonra çıktı:

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

Bu 2 684 476 624 bayt. ToplamWorkingSet işleminin yaklaşık 4.8 GB oldu

Bu cevap Eric Lippert'in harika makalesinden esinlenmiştir: Bildiğiniz her şey yanlış olduğunda .


Bu büyüleyici. Çöp toplayıcı, bazı şeylerin var olduğunu unutur ve bu sebeple izlerini kaybeder mi? C # hakkında fazla bir şey bilmiyorum. Ayrıca şimdi merak ediyorum, bir bomba ile sızıntı arasındaki fark nedir? Benzer bir fiyaskoyu, bir kurucuyu bir kurucunun içinden çağırmakla ya da hiç durmayan, sonsuz bir özyinleme işlevine sahip olmakla yaratılabileceğini hayal ediyorum, teknik olarak sistem bu referansları izlemiyor olsa da, sadece boşluğu kalmaz ...
don parlak

1
Bir yapıcı içindeki bir yapıcı, yığın taşmasına neden olur. Fakat bir örneğin yıkıcısı düz bir hiyerarşide çağrılır. GC aslında nesnelerin izini asla kaybetmez. Ne zaman onları yok etmeye çalıştığında, farkında olmadan yeni nesneler yaratır. Öte yandan, kullanıcı kodunun bahsedilen nesnelere erişimi yoktur. Ayrıca, söz konusu tutarsızlıklar ortaya çıkabilmektedir, çünkü GC bir nesneyi imha edici demeden çağırmaya karar vermiştir.
MrPaulch

Meydan okuma sadece kullanarak tamam olmaz mıydı class L{~L(){new L();}}? AFAIK for(;;)sadece hafızasını daha hızlı sızdırıyor, değil mi?
BgrWorker

1
Üzgünüm hayır. Çünkü her tahrip edilmiş nesne için sadece bir tane yeni örnek yaratılacak, bu da tekrar erişilemez ve imha edilmek üzere işaretlendi. Tekrar et. Sadece bir nesne yıkım için beklemede olacak. Artan nüfus yok.
MrPaulch

2
Pek sayılmaz. Sonunda bir kesinleşmiş göz ardı edilir. İlgili nesne ne olursa olsun yenilecektir.
MrPaulch

26

C (gcc) , 15 bayt

f(){malloc(1);}

Doğrulama

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

golfed

setInterval(0)

Boş bir zamanlayıcı işleyicisini varsayılan gecikmeyle kaydederek, sonuçtaki zamanlayıcı kimliğini atar (iptal etmeyi imkansız hale getirir).

enter image description here

Varsayılan olmayan bir aralık kullandım, birkaç milyon zamanlayıcı oluşturmak için, sızıntıyı göstermek için, varsayılan bir aralık kullanmak gibi deli gibi CPU yiyor.


5
Haha 'Golfed' yazdığını seviyorum, beni eski sürüm hakkında merak ediyor
Martijn

9
Bu gibi görünebilirif(window && window.setInterval && typeof window.setInterval === 'function') { window.setInterval(0); }
Tschallacka

3
Aslında, bu iptal etmek imkansız değildir: aralık (ve zaman aşımı) ID'leri sırayla numaralandırılmıştır, bu yüzden sadece clearIntervalaralığınız sona erene kadar artan ID ile arayarak bu durumu iptal etmek oldukça kolaydır . Örneğin:for(let i=0;i<1e5;i++){try{clearInterval(i);}catch(ex){}}
user2428118

5
@ user2428118 Zeplin'in dediği gibi, bu, C / C ++ sızıntılarının "gerçek" olmadığını söylemek yerine "meşru" değildir; free()
TripeHound

1
Vay canına, JavaScript'in gerçek bir rakip olduğu pek fazla zorluk yok ...
Jared Smith

19

Java, 10 bayt

Sonunda, Java'da rekabetçi bir cevap!

golfed

". "::trim

Bu, şu şekilde kullanılabilen bir yöntem referansıdır (bir dizge sabitine karşı):

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

Bir hazır bilgi dizesi ". " , sınıf tarafından tutulan genel interned dizeleri havuzuna otomatik olarak eklenir java.lang.Stringve onu derhal kırptığımız zaman, ona yapılan başvuru kodda daha fazla tekrar kullanılamaz (tam olarak aynı dizeyi tekrar belirtmezseniz).

...

Başlangıçta boş bir dizge havuzu, String sınıfı tarafından özel olarak korunur.

Tüm değişmez dizgiler ve dizge değerli sabit ifadeler interned. Dize değişmezleri, Java ™ Dil Belirtimi'nin 3.10.5 bölümünde tanımlanmıştır.

...

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

Bunu, bir "üretim sınıfı" bellek sızıntısında, diziyi kendisine ekleyerek ve daha sonra açıkça bir döngü içinde intern () yöntemini çağırarak döndürebilirsiniz .


2
Ben C # için bu kabul ... ama dediğim gibi, çünkü sayar sanmıyorum olabilir başka dize dahil ederek bu belleğe erişim. Ne yapacağını bilmek de ilgimi çeker ("." + " ").intern()(eğer kullanıcı girişi olsaydı ya da olmasaydı, derleyici optimizasyonlarını indirim altına alırız).
VisualMelon

1
Nitekim, tek fikir birliği en iyisi ince, sadece "kod derlenmeli" tarafındayım. Ben hala soru üzerine ifadeleri verilen bu çözüm satın emin değilim (bu dizeleri edemez serbest bırakılacak ve onlar olabilir bu olası olsa bile normal çalışma koduna bulunabilir), ama kendi yargıda başkalarını davet
VisualMelon

3
Bu dizgeye ulaşılamıyor , sızdırılmış olsa bile . Eşit bir dizge içerek onu istediğimiz zaman alabiliriz. Bu sayılırsa, kullanılmayan global değişken (Java'da özel statik) bir sızıntı olabilir. Hafıza sızıntısı böyle tanımlanmaz.
user2357112

3
@ user2357112 "... Bu dizgeye erişilemiyor ..." Bu, kodu gördüğünüz için yalnızca açık görünüyor. Şimdi bu yöntem referansını X () 'e kodunuzun argümanı olarak aldığınızı düşünün, onun içinde bir dize değişmezi tahsis ettiğini biliyorsunuz, ancak hangisinin tam olarak "," veya "123" olabileceğini bilmiyorsunuz. veya (genellikle) bilinmeyen bir uzunluğa sahip herhangi bir dize. Lütfen nasıl erişebildiğinizi gösterebilir misiniz ya da bulunduğu "stajyer" havuzundaki girişi devre dışı bırakabilir misiniz?
zeplin

2
@ user2357112 Sonlu belleği olan bir makinede, herhangi bir bellekte depolanan bir değere erişebilirsiniz simply by guessing it correctly, ancak bu bellek sızıntısı gibi bir şeyin olmadığı anlamına gelmez. there's probably some way to use reflection to determine the string's contents toobunu gösterebilir misin? (ipucu, String.intern () yerel kodda uygulanır).
zeplin

17

Pas, 52 bayt

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

Bazı baytları sistem malloc ile ayırır. Bu yanlış ABI'nin kabul edilebilir olduğunu varsayar.


Pas (teoride), 38 bayt

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

Yığın üzerine bellek ayırır, ham bir işaretçi çıkarırız ve sonra onu görmezden gelir, etkili bir şekilde sızdırırız. ( Box::into_rawsonra kısastd::mem::forget ).

Ancak, Rust varsayılan olarak valgrind herhangi bir sızıntı tespit edemediği jemalloc kullanır . Sistem ayırıcısına geçebiliriz ancak bu 50 bayt ekler ve gecelik gerektirir. Hafıza güvenliği için çok teşekkürler.


İlk programın çıktısı:

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

müthiş. Bunun gibi mesajlar, öğrenmeye çalıştığım en eğlenceli dillerden biri olan geçen yıl boyunca Rust'u keşfetmemi sağladı.
don

16

8086 ASM, 3 bayt

Bu örnekler, bir C çalışma zamanının bağlandığını varsayar.

jmp _malloc

bu göreceli adresin e9 XX XXbulunduğu yere toplanırXX XX_malloc

Bu malloc, tahmin edilemeyen bir miktarda bellek tahsis etmeye çağırır ve ardından işlemleri derhal sonlandırır. DOS gibi bazı işletim sistemlerinde, sistem yeniden başlatılıncaya kadar bellek geri alınmayabilir!


Malloc'un normal uygulanması, işlem çıkışında hafızanın boşalmasına neden olur.
Joshua

@Joshua Evet, ama bu uygulama tanımlı davranış.
FUZxxl

12

İleri, 6 bayt

golfed

s" " *

Boş bir dize ile s" "adresini ve uzunluğunu (0) bırakarak, sonra da çarparak (bir hafıza adresinin kaybolmasıyla sonuçlanır) atar.

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

45 bayt git

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

bu, içinde sonsuz bir döngüye sahip anonim bir goroutin yaratır. Program, normal bir şekilde çalışmaya devam edebilir, çünkü goroutine başlamak, aynı anda çalışan küçük bir iplik oluşturmak gibi bir şeydir, ancak programın, goroutine tahsis edilen hafızayı geri kazanması mümkün değildir. çöp toplayıcı da hala çalıştığı için hiçbir zaman toplamaz. Bazı insanlar buna 'goroutine sızdırıyor' diyor


golf kontrolü: Bu aramadan 2 bayt daha kısa C.malloc(8), çünkü gerekimport"C"
Riking

9

Java 1.3, 23 bayt

void l(){new Thread();}

Bir iş parçacığı oluşturma, ancak başlatılamıyor. İş parçacığı iç iş parçacığı havuzunda kayıtlı, ancak hiçbir zaman başlatılmayacak, bu yüzden hiç bitmedi ve bu nedenle GC için hiçbir zaman aday olamaz. Java limbolarına sıkışmış, geri dönüşü olmayan bir nesnedir.

1.3'ten sonraya sabitlenene kadar Java'da bir hata var .

Test yapmak

Aşağıdaki program hafızayı yeni iş parçacığı nesnelerle kirletmeyi ve azalan boş hafıza alanı göstermeyi sağlar. Sızıntı testi için, yoğun olarak GC çalışmasını sağlar.

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

Bu yalnızca belirli Java sürümlerinde çalıştığından, başlığınızda bunun yerine "Java 3" demelisiniz.

5
Java 3 diye bir şey yoktur. Java 1.3. Java 1.0, 1.1, 2, 1.3, 1.4, 5, 6, 7, 8, 9 oldu. Garip numaralandırma, ama işte böyle.
Olivier Grégoire

Bu benim de fikrimdi. Lanet olsun.
Magic Octopus Urn,

8

Befunge ( mantarlar ), 1 bayt

$

Bu, platforma bağlı ve sürüme bağlı olabilir (yalnızca Windows'ta sürüm 1.0.4 ile test ettim), ancak mantarlar tarihsel olarak çok sızdıran bir tercüman oldu. $(Damla) komut boş yığın şey yapamaz, ama bu kod üzerinde döngü nedense çok çabuk çok fazla bellek sızıntısı için yönetir. Birkaç saniye içinde birkaç konser kullanmış ve "yetersiz bellek" hatasıyla çökecektir.

Bunun mutlaka bir $komut olması gerekmediğine dikkat edin - hemen hemen her şey yapacaktır. Yine de boş bir kaynak dosyayla çalışmaz. En az bir operasyon olmalı.


8

Swift 3, 38 bayt

Yeni sürüm:

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

x Kendisine güçlü bir referansı vardır, bu nedenle bir bellek sızıntısına neden olacak şekilde ayrılmaz.

Eski versiyon:

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

xiçerir yve tam tersi. Böylece, ikisi de ayrılmayacak ve bellek sızıntısına neden olacaktır.


Hmm, hala bu belleğe başvurabilirsin xve ybu yüzden bu bana bir sızıntı gibi görünmüyor (bir şekilde onları yok etmediğin sürece).
zeplin

@zeppelin Son satır bunu düzeltmek için bir fonksiyona sarılabilir
NobodyNada

@ HiçkimseNada, eğer son çizgiyi dozeplin doğru düzelttiği sorunu giderecek bir bloğa koyarsam ?
Daniel

@Dopapp Evet; bir dode işe yarar. İyi bir fikir!
KimseNada

Kısaltılabilir, iki sınıfa ihtiyaç class X{var x: X!};do{let x=X();x.x=x}
duymazsınız

7

Delphi (Nesne Pascal) - 33 bayt

Değişken, tam konsol programı olmayan bir nesne oluşturma:

program;begin TObject.Create;end.

Projede FastMM4'ü etkinleştirmek bellek sızıntısını gösterecektir:

görüntü tanımını buraya girin


6

C # - 84 bayt

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

Bu, yönetilmeyen belleğin tam olarak 1 baytını ayırır ve sonra onu kaybetmenin IntPtrveya serbest bırakmanın tek yolu olduğuna inanıyorum. Bir döngü içinde doldurarak ve uygulamanın kilitlenmesini bekleyerek test edebilirsiniz (işleri hızlandırmak için birkaç sıfır eklemek isteyebilirsiniz).

Ben kabul System.IO.File.Create("a");ve böyle ama bu uygulamanın kendisi gibi mutlaka bellek sızıntıları olduğuna ikna değilim olacaktır hafızayı toplamak, o kadar da altından bir işletim sistemi bulunuyor olabilir (çünkü sızıntı Closeveya Disposeadlandırılan değildi). Dosya erişimi işleri ayrıca dosya sistemi izinleri gerektirir ve kimse bunlara güvenmek istemez. Ve bunun yine de sızdırmayacağı ortaya çıktı, çünkü sonlandırıcının çağrılmasını durduracak hiçbir şey yok (temel kaynakları serbest bırakmak mümkün), çerçevenin bu tür karar verme türlerini (bir dereceye kadar) hafifletmek için içerdiği ve programcıları belirsiz görünen dosya kilitleme ile karıştırmak (alaycıysanız). Jon Hanna'ya beni doğrudan konuştuğun için teşekkürler.

Biraz daha kısa bir yol bulamadığım için biraz hayal kırıklığına uğradım. .NET GC herhangi düşünemiyorum, işleri IDisposableskesinlikle sızıntı olacağı mscorlib içinde (ve aslında hepsi ne kadar can sıkıcı finalisers, var gibi) , ben PInvoke (kısa yönetilmeyen bellek ayırmaya başka bir şekilde farkında değilim ) ve yansıma) (dil anlambilimin (hayır erişimcilerle örneğin özel üyeleri veya sınıflar) buna bir referansla şey garanti edebilir bulunabilir.


1
System.IO.File.Create("a")hiçbir şey sızdırmaz, ancak GC.SuppressFinalize(System.IO.File.Create("a"))açıkça FileStreamüretilen kesinleştiriciyi çalıştırmamanın istendiği gibi olacaktır .
Jon Hanna

@JonHanna oldukça haklı. Kullanılabilir durumdaki paranoyam benden daha iyisini yapmış gibi görünüyor.
VisualMelon

System.Drawing.Bitmap kullanarak GDI + sızıntısı yapma şansınız olabilir. Sayılır mı bilmiyorum çünkü programın kendisi değil, sızıntıya neden olan bir windows kütüphanesi.
BgrWorker

@BgrWorker'ın ayrıca kesinleştiricileri olduğundan şüpheliyim ve kod kütüphanesinde dış kütüphanelerden kaçınma eğilimindeyim, çünkü onlara maliyet koyma konusunda fikir birliğine varmıyorum: kendinize güvendiğiniz bir yol bulabilirseniz, göndermekten çekinmeyin kendi cevabın içinde!
VisualMelon

<!-- language: lang-c# -->Bu ve güzel cevap için teşekkürler! (C # bu yüzden onu seviyorum)
Metoniem

5

Faktör , 13 bayt

Faktör otomatik bellek yönetimine sahiptir, ancak bazı libc işlevlerine de erişim sağlar:

1 malloc drop

1 bayt belleği manuel olarak ayırır, adresini döndürür ve düşürür.

malloc aslında bellek sızıntılarını takip etmek için bir kopya kaydeder ve çift serbest bırakmalara neden olur, ancak sızdırdığınız birini tanımlamak kolay bir iş değildir.

Bu referansı gerçekten kaybettiğinizden emin olmayı tercih ediyorsanız:

1 (malloc) drop

Sızıntı testi ile [ 1 malloc drop ] leaks.diyor ki:

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

Sızıntı testi ile [ 1 (malloc) drop ] leaks.diyor ki:

| Disposable class | Instances | |

Oh hayır! Zavallı faktör, şimdi Alzheimer var! D:


5

Ortak Lisp (yalnızca SBCL), 28 26 bayt

sb-alien::(make-alien int)

Öyle koşuyorsun sbcl --eval 'sb-alien::(make-alien int)':; hiçbir şey yazdırılmaz veya iade edilmez, ancak bellek ayırma gerçekleşir. Formu a içine (print ...)kaydırırsam, işaretçi REPL'de görüntülenir.

  1. package::(form)bir formu okurken geçerli paketi geçici olarak bağlamak için SBCL'deki özel bir nottur. Bu, hem önek önlemek için burada kullanılır make-alienve intile sb-alien. Mevcut paketin buna ayarlanmış olduğunu varsaymanın hile olacağını düşünüyorum, çünkü başlangıçta durum böyle değil.

  2. make-alien Belli bir tip ve isteğe bağlı boyutta (malloc kullanarak) hafıza ayırır.

  3. Bunu REPL'de yürütürken, REPL'in 0işaretçiyi döndürmemesi, ancak bunun yerine bu değeri vermesi için ayırmadan sonra ekleyin . Aksi takdirde, bu gerçek bir sızıntı olmayacaktır çünkü REPL geri gönderilen son üç değeri (Bkz *. **,*** ) Hatırlar ve tahsis edilen hafızayı boşaltmak için hala bir şansımız olabilir.

PrzemysławP sayesinde 2 bayt kaldırıldı, teşekkürler!


1
Kullanmak Can 1(veya 2, 3vs.) yerine ()sen değeri döndürmek, böylece 1? 1 bayt kazandıracak. Ayrıca bu cevap sadece REPL mi? Belki de kod yüklerseniz sonunda hiçbir şey loadekleyemezsiniz (), çünkü yine de erişilebilir olmayacak çünkü?
PrzemysławP

1
@ PrzemysławP Her iki noktada da haklısınız, denedim evalve bu sizin dediğiniz gibi çalışıyor. Çok teşekkürler!
coredump

4

OtomatikIt , 39 bayt

#include<Memory.au3>
_MemGlobalAlloc(1)

Öbekten bir bayt ayırır. Döndürülen tanıtıcı _MemGlobalAllocatıldığı için, bu ayırımı açıkça serbest bırakmanın bir yolu yoktur.


3

C ++, 16 bayt

main(){new int;}

Sızıntılarını kontrol etmek için valgrind'im yok, ama olması gerektiğinden eminim. Aksi takdirde denerim:

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

Valgrind sonucu

(Gerçekten sızdırıyor)

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

@WheatWizard Ben kullanıyorum g++ 4.3.2(en sonuncusu değil) ve sadece iyi derler. Hiçbir iade türü intvarsayılan olarak bence değil. Bununla birlikte -Wallbir uyarı var:plop.cpp:1: warning: ISO C++ forbids declaration of 'main' with no type
matovitch

2
@WheatWizard Maalesef, yarışmaya başlamak için c ++ örneğini verdiğinizi gördüm. Reddit'ten geldiğimde sadece cevaplara baktım ve (garip bir şekilde) herhangi bir C ++ görmedim. Kendimi biraz aptal hissediyorum. : /
matovitch

Mutabakata varmak, tıpkı []{new int;}bir C ++ işlevi olarak sayılabilir (zorluk tam bir program belirtmedi).
Toby Speight

3

Java (OpenJDK 9) , 322 220 bayt

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

Çevrimiçi deneyin!

Bu, String Önbelleğini kullanmayan başka bir Bellek sızıntısıdır. RAM'inin yarısını ayırır ve onunla hiçbir şey yapamazsın.

Tüm baytları kaydettiği için zeplin'e teşekkürler


Örneği Unsafeiçindeki statik değişkenden elde ederek bir demet bayttan tasarruf edebilirsiniz , bunun gibi: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

Ayrıca, public static void mainstatik bir başlatıcıyla değiştirerek biraz daha tasarruf edebilirsiniz static{try{}catch(Exception e){}}(bu işlem başlatmak için biraz daha zor olabilir ancak yine de geçerli ve derlenebilir).
zeplin

Yapıcı kullanımı, kullandığım kodun Android sürümünde kullanıldı. İm @ home'dayken bazı şeyleri değiştireceğim ama tek bir bildiriyle gittiğiniz yola gideceğim;)
Serverfrog

Boşluğu kaldırın, ayerine kullanın argsve herkesi kaldırın. tio.run/nexus/…
Pavel

true 1> 0
masterX244

3

c, 9 bayt

main(){}

Kanıt:

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
Aslında hafızayı sızdırmazsın; gccdır-dir. Bu boş programla da çalışmalıdır. gcc src.c && valgrind ./a.outTemiz bir sonuç vermesi gereken deneyin .

3

C #, 109 bayt

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

Bu sızıntının ardındaki fikri üretim kodunda bulduk ve araştırmayı bu yazıya yönlendirdik. Asıl sorun makaleden bu uzun alıntıda (daha fazla bilgi için okuyun):

Kodumu PurchaseOrder için ararken , bu kod satırını page_loadsayfalarımdan birinde bulabilirim.XmlSerializer serializer = new XmlSerializer(typeof(PurchaseOrder), new XmlRootAttribute(“”));

Bu oldukça masum bir kod parçasına benziyor. Biz oluşturmak XMLSerializerFOR PurchaseOrder. Ama kapakların altında ne olur?

Eğer bir göz atarsak XmlSerializer Reflektör yapıcısı bunu çağırır bulmak this.tempAssembly = XmlSerializer.GenerateTempAssembly(this.mapping, type, defaultNamespace, location, evidence);geçici (dinamik) montaj üretir. Bu nedenle, bu kod her çalıştığında (yani, sayfaya her basıldığında) yeni bir derleme oluşturur.

Bir derleme oluşturmasının nedeni, serileştirme ve seri hale getirme için işlevler oluşturması gerektiği ve bunların bir yerde kalması gerektiğidir.

Tamam, peki… bu bir meclis yaratıyor, peki ne? Bununla işimiz bittiğinde, sadece ortadan kaybolması gerekir mi?

Şey… bir montaj GC Yığınında bir nesne değildir, GC gerçekten montajlardan habersizdir, bu yüzden çöp toplanmayacaktır. 1.0 ve 1.1'deki meclislerden kurtulmanın tek yolu içinde bulunduğu uygulama alanını boşaltmaktır.

Ve orada bir problem var Dr. Watson.

Derleyiciden Visual Studio 2015'te çalıştırmak ve Teşhis Araçları Penceresini kullanmak, yaklaşık 38 saniye sonra aşağıdaki sonuçları gösterir. İşlem belleğinin düzenli olarak arttığına ve Çöp Toplayıcı'nın (GC) çalışmaya devam ettiğini, ancak hiçbir şey toplayamadığını unutmayın.

Teşhis Araçları Penceresi


2

C 30 bayt

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

Valgrind Sonuçları:

         ==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
Bunun yerine sadece yapmak mümkün mü main(){malloc(1);}?
kirbyfan64sos

@Evet öyle! Ancak çoktan gönderildi!
Abel Tom

2

Dart, 76 bayt

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

JavaScript cevabı gibi biraz. Aradığın zaman.listen bir Dart akışı nesne üzerinde akışta kesmek sağlayan bir StreamSubscription geri verilir. Ancak, bunu bir kenara atarsanız, dereden aboneliğinizi iptal edip bir sızıntıya neden olamazsınız. Sızıntının giderilmesinin tek yolu, Stream'in kendisi toplanırsa, ancak yine de bir StreamController + Timer combo tarafından dahili olarak başvurulur.

Ne yazık ki Dart, denediğim diğer şeyler için fazla akıllı. ()async=>await new Completer().futureçalışmaz çünkü beklemenin kullanılması aynıdır new Completer().future.then(<continuation>), bu da kapanmanın kendisinin tahrip edilmesine izin verir, ikinci Tamamlayıcıya referans verilmez (Tamamlayıcı, geleceğe referans verir, Geleceğin .futurekapanış olarak referansı vardır).

Ayrıca, İzolatlar (aka dişliler) GC ile temizlenir, böylece yeni bir iş parçacığında kendinizi yumuşatır ve hemen duraklatır ( import'dart:isolate';main(_)=>Isolate.spawn(main,0,paused:true);) çalışmaz. Sonsuz bir döngüye sahip bir İzolat ( import'dart:isolate';f(_){while(true){print('x');}}main()=>Isolate.spawn(f,0);) oluşturmak bile İzolatı öldürür ve programdan çıkar.

Oh iyi.


Ana programınız çalışmaya devam ederse ve başka şeyler yapmaya devam ederse, çöp toplayıcı izolatı durdurabilir mi? Goroutine örneğim benzer göründüğü için soruyorum ... Programın çıkıp tüm belleği işletim sistemine geri vermesinin, kesinlikle sızmadığı anlamına gelmediğini varsaydım.
don

2

Swift, 12 bayt

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

Açıklama:

Bu, dilin bellek manuel yönetimi, otomatik referans sayımı (Swift gibi ARC), hatta süpürme çöp toplaması kullanmasından bağımsız olarak, herhangi bir dilde meydana gelebilecek fiili bir bellek sızıntısıdır.

[3,5]sadece bir dizi değişmez. Bu dizi, en azından bu 2 öğe için yeterli bellek ayırır. 3Ve 5sadece keyfi vardır.

Abonelik (indeksleme) bir Array<T>üretir ArraySlice<T>. An ArraySlice<T>, yaratıldığı Dizinin anısına bir görünümdür.

[3,5][0...0]ArraySlice<Int>değeri olan bir üretir [3]. 3Bu dilimin içinde, yukarıda gösterilen orijinalin kopyası değil , aynı 3öğe olduğuna dikkat edin .3Array

Elde edilen dilim daha sonra bir değişkende saklanabilir ve kullanılabilir. Orijinal diziye artık başvuruda bulunulmuyor, bu nedenle değiştirilebileceğini düşünürsünüz. Ancak, olamaz.

Dilim, geldiği dizinin belleğine bir görünüm getirdiğinden, dilimin yaşadığı sürece orijinal dizinin canlı tutulması gerekir. Öyleyse, 2tahsis edilen hafıza değerine sahip orijinal eleman büyüklükleri için, sadece bir birinci eleman boyutundaki hafıza kullanılır, diğeri ise ilk tahsis etmemek için var olması gerekir. Hafızanın ikinci element büyüklüğü de-faktör sızdırıyor

Bu sorunun çözümü, büyük dizilerin küçük dilimlerini uzun süre canlı tutmamaktır. Dilim içeriğini sürdürmeniz gerekirse, kopyalanacak belleği tetikleyen bir diziye yükseltin, böylece orijinal dizinin belleğindeki bağımlılığı giderin:

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

2

1. Çözüm: C (Mac OS X x86_64), 109 bayt

Golf_sol1.c kaynağı

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

Yukarıdaki programın __DATA segmentinde yürütme erişimi ile derlenmesi gerekir.

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

Ardından programı çalıştırmak için aşağıdakileri çalıştırın:

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

Sonuçlar:

Ne yazık ki Valgrind, sistem çağrılarından ayrılan belleği izlemiyor, bu yüzden iyi bir tespit sızıntısı gösteremiyorum.

Ancak ayrılan belleğin (MALLOC meta verileri) büyük bölümünü görmek için vmmap'a bakabiliriz.

                                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 

açıklama

Bu nedenle, gelişmiş çözüme geçmeden önce burada neler olup bittiğini açıklamam gerektiğini düşünüyorum.

Bu ana işlev, C'nin eksik tip bildirimini kötüye kullanıyor (bu yüzden, karakterleri yazarken karakterleri boşa harcamamız gerekmeden int olarak varsayılan değer). Bağlayıcı yalnızca, çağrılması maingereken bir simgeyi bulamama konusunda umurunda değil . Yani burada ana idam edilecek kabuk kodumuzla başlattığımız bir int dizisi yapıyoruz. Bu nedenle, ana __TEXT segmentine eklenmeyecek, bunun yerine __DATA segmentine eklenecektir, bu nedenle programı çalıştırılabilir bir __DATA segmentiyle derlememiz gerekir.

Anada bulunan kabuk kodu şudur:

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

Bunun yaptığı şey, bir sayfa hafıza ayırmak için syscall işlevini çağırmaktır (syscall mach_vm_allocate dahili olarak kullanır). RAX, 0x100000a'ya eşit olmalıdır (sisteme hangi işlevi istediğimizi söyler), RDI ayırma hedefini tutarken (bizim durumumuzda bunun mach_task_self () olmasını istiyoruz), RSI işaretçiyi yeni oluşturulan belleğe yazmak için adresi tutmalıdır (bu yüzden sadece yığındaki bir kısma işaret ediyoruz), RDX tahsisin büyüklüğünü tutar (sadece bayttan tasarruf etmek için RAX veya 0x100000a'yı geçiyoruz), R10 bayraklarını tutar herhangi bir yere tahsis edilecek).

Şimdi RAX ve RDI'nin değerlerini nereden aldıkları açıkça belli değil. RAX'in 0x100000a olması gerektiğini ve RDI'nin mach_task_self () döndürdüğü değer olması gerektiğini biliyoruz. Neyse ki mach_task_self () aslında her seferinde aynı hafıza adresinde olan bir değişken (mach_task_self_) için bir makrodur (bununla birlikte yeniden başlatılması gerekir). Özel örneğimde mach_task_self_, 0x00007fff7d578244 konumunda bulunuyor. Böylece talimatları kısmak için, bu verileri argv'den iletiyoruz. Bu yüzden programı bu ifadeyle çalıştırıyoruz.$(ruby -e 'puts "\xf5\xff\xff\xfe\xff\xff\x44\x82\x57\x7d\xff\x7f"')ilk argüman için. Dize, RAX değerinin (0x100000a) yalnızca 32 bittiği ve kendisine uygulanmış birinin tamamlayıcısına sahip olduğu iki değerdir (bu nedenle boş bayt yoktur; orijinali elde etmek için DEĞİLDİR), sonraki değer sonuna kadar 2 ekstra önemsiz bayt eklenerek sola kaydırılan RDI (0x00007fff7d578244) (yine de boş baytları dışlamak için tekrar orijinal haline getirmek için sağa kaydırıyoruz).

Sistemden sonra yeni ayrılan hafızamıza yazıyoruz. Bunun nedeni, mach_vm_allocate (veya bu sistem çağrısı) kullanılarak ayrılan hafızanın aslında VM sayfaları olması ve otomatik olarak hafızaya kaydedilmemesidir. Aksine, veriler kendilerine yazılana kadar saklanır ve daha sonra bu sayfalar belleğe eşlenir. Sadece ayrılsaydı, şartları yerine getirip getirmediğinden emin değildi.

Bir sonraki çözüm için, kabuk kodumuzda boş bayt bulunmadığı gerçeğinden faydalanacağız ve böylece boyutu küçültmek için program kodumuzun dışına taşıyabiliriz.

2. Çözüm: C (Mac OS X x86_64), 44 bayt

Golf_sol2.c kaynağı

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

Yukarıdaki programın __DATA segmentinde yürütme erişimi ile derlenmesi gerekir.

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

Ardından programı çalıştırmak için aşağıdakileri çalıştırın:

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

Sonuç, aynı boyutta bir tahsisat yaptığımızdan önceki gibi olmalıdır.

açıklama

Sızıntı kodumuzun yığınını programın dışına çıkarmamız haricinde, çözüm 1 ile aynı kavramı takip eder.

Anada bulunan kabuk kodu şimdi şöyledir:

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

Bu temelde argv içinde geçirdiğimiz kabuk kodunu bu koddan sonra kopyalar (böylece kopyaladıktan sonra, eklenen kabuk kodunu çalıştırır). Bizim lehimize olan şey, __DATA segmentinin en azından bir sayfa boyutunda olacağı, bu yüzden kodumuz o kadar büyük olmasa da, yine de "güvenle" daha fazla yazabiliriz. Dezavantajı, buradaki ideal çözümdür, kopyaya bile ihtiyaç duymaz, bunun yerine doğrudan kodu kullanarak argv kodunu çağırıp çalıştırır. Ancak ne yazık ki, bu hafızanın icra hakkı yoktur. Bu hafızanın haklarını değiştirebiliriz, ancak kopyalamaktan daha fazla kod gerektirebilir. Alternatif bir strateji, harici bir programdan hakları değiştirmek olacaktır (ancak daha fazlası için).

Argv'ye ilettiğimiz kabuk kodu şöyledir:

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

Bu, önceki kodumuzla aynıdır, sadece fark, doğrudan EAX ve RDI için değerleri dahil ettiğimizdir.

Olası Çözüm 1: C (Mac OS X x86_64), 11 bayt

Programın harici olarak değiştirilmesi fikri bize sızıntının harici bir programa taşınması için olası bir çözüm sunar. Asıl programımızın (başvuruların) sadece sahte bir program olduğu ve sızıntı programının hedef programımıza bir miktar bellek ayıracağı yer. Şimdi bunun, bu meydan okuma için kurallara uyup uymayacağından emin değildim, ancak yine de paylaşıyordum.

Bu nedenle, mach_vm_allocate'i hedef programımıza göre hedefle birlikte harici bir programda kullanacak olsaydık, bu meydan okuma programımızın sadece aşağıdakiler boyunca bir şey olması gerektiği anlamına gelebilirdi:

main=65259;

Bu kabuk kodunun kendisine kısa bir atlama (sonsuz atlama / döngü) olduğu için, program açık kalır ve harici bir programdan başvuru yapabiliriz.

Olası Çözüm 2: C (Mac OS X x86_64), 8 bayt

Valgrind çıkışına bakarken yeterince komikti, en azından valgrind'e göre, dyld'in hafızayı sızdırdığını gördüm. Bu yüzden etkili bir şekilde her program bir miktar bellek sızdırıyor. Durum böyle olunca, aslında sadece hiçbir şey yapmayan bir program yapabiliriz (basitçe çıkar) ve bu aslında hafızadan sızıyor olur.

Kaynak:

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

Düz İngilizce , 71 70 58 35 bayt

Boş bir satırı silerek 1 bayt kaldırıldı. "Bogon" tür tanımını ortadan kaldırarak ve "bogon" alt türü yerine üst "thing" türünü kullanarak 12 bayt kaldırıldı. Tam bir programdan, yalnızca bellek sızdıran bir rutine geçerek 23 bayt kaldırıldı.

Golf versiyonu:

To x:
Allocate memory for a thing.

Eksiksiz bir program olan bir alt sürüm tanımı kullanır ve hafızayı sızdırmaz.

A bogon is a thing.

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

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

Eğer "x" nin golflü versiyonu çağrılırsa, “x” denilen sayıya oranla hafıza sızdırıyor. Golf sürümünde, "şey deallocate." bellek sızıntısını düzeltirdi.

Düz İngilizce varsayılan olarak bellek sızıntılarını denetler. Belleği sızdıran sürüm çalıştırıldığında, program kapanmadan hemen önce bir iletişim kutusu görünür. İletişim kutusunda "hata ayıklama" başlığı, "1 damla" mesajı ve "Tamam" düğmesi bulunur. Sızıntı fonksiyonu ne kadar çok çağrılırsa, mesajdaki "damlama" sayısı o kadar büyük olur. Belleği sızdırmayan sürüm çalıştırıldığında, iletişim kutusu görünmez.

Düz İngilizce'de, "şey", iki kat bağlantılı bir listedeki bir öğeye işaretçidir. "Şey", "başlamak" ve "kapatmak", her bir projeye kopyalanması gereken "erişte" adlı bir modülde tanımlanır. Derleyicide "A", "", "to", "bellek ayırmak" ve "yok etmek" tanımlanmıştır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.