Kebocoran Memori dalam byte sesedikit mungkin


79

Tugas Anda adalah menulis kode yang akan bocor setidaknya satu byte memori dalam sesedikit mungkin byte. Memori harus bocor bukan hanya dialokasikan .

Memori yang bocor adalah memori yang dialokasikan oleh program tetapi kehilangan kemampuan untuk mengaksesnya sebelum dapat mengalokasikan memori dengan benar. Untuk sebagian besar bahasa tingkat tinggi, memori ini harus dialokasikan pada heap.

Contoh dalam C ++ adalah program berikut:

int main(){new int;}

Ini membuat sebuah new intheap tanpa pointer ke sana. Memori ini langsung bocor karena kami tidak dapat mengaksesnya.

Berikut ini ringkasan ringkasan dari 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

Banyak bahasa memiliki debugger memori (seperti Valgrind ) jika Anda bisa, Anda harus menyertakan output dari debugger tersebut untuk mengonfirmasi bahwa Anda telah kehabisan memori.

Tujuannya adalah untuk meminimalkan jumlah byte di sumber Anda.


2
Mungkin Anda dapat memiliki rentang jumlah kebocoran yang berbeda dan tergantung pada seberapa banyak Anda bocor, Anda kehilangan x% dari jumlah byte Anda
Christopher

11
@ChristopherPeart Untuk satu saya bukan penggemar bonus pada tantangan dan untuk dua seperti yang telah Anda tunjukkan, sangat mudah untuk membocorkan memori tanpa batas.
Wheat Wizard

1
Terkait . Namun, bukan duplikat, karena sebagian besar jawaban untuk pertanyaan itu membentuk struktur yang tak terbatas yang dapat dijangkau dalam memori daripada benar-benar membocorkan memori.

2
apa idenya? Mem itu tidak bisa dibebaskan? Saya kira ini akan membutuhkan eksekusi asli untuk bahasa yang dikumpulkan sampah atau mengeksploitasi bug.
akostadinov

7
Saya melihat bagaimana bahasa yang dirancang untuk bermain golf gagal secara menyedihkan ...
Kh40tiK

Jawaban:


89

Perl (5.22.2), 0 byte

Cobalah online!

Saya tahu akan ada beberapa bahasa di luar sana yang membocorkan memori pada program kosong. Saya mengharapkannya menjadi esolang, tetapi ternyata perlmemori itu bocor pada program apa pun. (Saya berasumsi bahwa ini disengaja, karena membebaskan memori jika Anda tahu Anda akan keluar bagaimanapun juga hanya membuang-buang waktu; dengan demikian, rekomendasi umum saat ini adalah hanya membocorkan memori yang tersisa setelah Anda berada dalam rutinitas keluar program Anda .)

Verifikasi

$ 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
Saya menyukai jawaban Unlambda, tetapi yang ini (IMHO) terlalu berlebihan, karena jelas penerjemah itu sendiri yang membocorkan memori, yaitu saya `pasti hilang: 7,742 byte dalam 14 blok` ketika saya menjalankan perl --versionmesin saya , meskipun tidak pernah bisa menjalankan program apa pun, sama sekali.
zeppelin

11
@ zeppelin: Setuju, tetapi menurut aturan kami, implementasinya yang menentukan bahasa, jadi jika implementasinya bocor memori, semua program dalam bahasa bocor memori. Saya tidak selalu yakin saya setuju dengan aturan itu, tetapi pada titik ini terlalu mengakar untuk benar-benar dapat berubah.

8
Ini juga berfungsi di Node JS.
Dennis

6
Ini terasa seperti celah standar baru dalam pembuatan ...
Michael Hampton

46
Akhirnya skrip Perl yang bisa saya mengerti.
user11153

66

C, 48 31 22 byte

Peringatan: Jangan jalankan ini terlalu sering.

Terima kasih kepada Dennis untuk banyak bantuan / ide!

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

Ini selangkah lebih maju. shmgetmengalokasikan memori bersama yang tidak dialokasikan saat program berakhir. Ia menggunakan kunci untuk mengidentifikasi memori, jadi kami menggunakan int yang tidak diinisialisasi. Ini adalah perilaku yang secara teknis tidak terdefinisi, tetapi secara praktis itu berarti bahwa kami menggunakan nilai yang tepat di atas tumpukan ketika ini disebut. Ini akan ditulis pada waktu berikutnya bahwa apa pun ditambahkan ke tumpukan, jadi kami akan kehilangan kunci.


Satu-satunya kasus bahwa ini tidak berhasil adalah jika Anda dapat mengetahui apa yang ada di tumpukan sebelumnya. Untuk tambahan 19 byte, Anda dapat menghindari masalah ini:

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

Atau, untuk 26 byte:

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

Tetapi dengan yang ini, memori bocor setelah program keluar. Saat menjalankan program memiliki akses ke memori yang melanggar aturan, tetapi setelah program berakhir kami kehilangan akses ke kunci dan memori masih dialokasikan. Ini memerlukan pengacakan tata letak ruang alamat (ASLR), jika tidak &kakan selalu sama. Saat ini ASLR biasanya aktif secara default.


Verifikasi:

Anda dapat menggunakan ipcs -muntuk melihat apa memori bersama yang ada di sistem Anda. Saya menghapus entri yang sudah ada sebelumnya untuk kejelasan:

$ 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 Secara teoritis, shmid bisa disimpan dalam file dan program dapat melampirkannya di masa mendatang. Beginilah cara kerja unix shared memory ...
tbodt

1
@AndrewSavinykh Memori bersama pada dasarnya menjadi sumber daya yang OS dapat berikan untuk proses lain. Ini mirip dengan file yang hidup dalam RAM dan proses apa pun yang mengetahui nama (kunci) memiliki akses ke sana sampai dihapus. Bayangkan sebuah proses yang menghitung angka dan menyimpannya dalam memori dan keluar sebelum proses yang membaca data terhubung ke memori bersama. Dalam hal ini, jika OS membebaskan memori maka proses kedua tidak bisa mendapatkannya.
Riley

35
Terima kasih telah memposting ini. Saya baru saja melindungi TIO dari kebocoran memori bersama.
Dennis

4
@Dennis Itu sebabnya saya tidak memposting tautan TIO. Saya tidak tahu apakah itu dilindungi atau tidak.
Riley

12
Saya suka bagaimana Anda menggunakan kata masalah untuk menggambarkan skenario di mana program ini bocor lebih sedikit memori daripada yang dimaksudkan.
kasperd

40

Unlambda ( c-refcnt/unlambda), 1 byte

i

Cobalah online!

Ini benar-benar tantangan tentang menemukan juru bahasa yang sudah ada sebelumnya yang bocor memori pada program yang sangat sederhana. Dalam hal ini, saya menggunakan Unlambda. Ada lebih dari satu juru bahasa resmi Unlambda, tetapi c-refcntmerupakan salah satu yang paling mudah dibangun, dan memiliki properti yang berguna di sini yang bocor memori ketika sebuah program berjalan dengan sukses. Jadi yang perlu saya berikan di sini adalah program hukum Unlambda yang sesederhana mungkin, sebuah larangan. (Perhatikan bahwa program kosong tidak berfungsi di sini; memori masih dapat dijangkau pada saat penerjemah macet.)

Verifikasi

$ wget ftp://ftp.madore.org/pub/madore/unlambda/unlambda-2.0.0.tar.gz
…menggunting…
2017-02-18 18:11:08 (975 KB / s) - 'unlambda-2.0.0.tar.gz' disimpan [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
…menggunting…
== 3417 == LEAK RINGKASAN:
== 3417 == pasti hilang: 40 byte dalam 1 blok
== 3417 == hilang secara tidak langsung: 0 byte dalam 0 blok
== 3417 == mungkin hilang: 0 byte dalam 0 blok
== 3417 == masih dapat dijangkau: 0 byte dalam 0 blok
== 3417 == ditekan: 0 byte dalam 0 blok
== 3417 == 
== 3417 == Untuk jumlah kesalahan yang terdeteksi dan ditekan, jalankan kembali dengan: -v
== 3417 == RINGKASAN KESALAHAN: 1 kesalahan dari 1 konteks (ditekan: 0 dari 0)

39

TI-Basic, 12 byte

While 1
Goto A
End
Lbl A
Pause 

"... kebocoran memori adalah tempat Anda menggunakan Goto / Lbl dalam satu lingkaran atau Jika kondisional (apa pun yang memiliki perintah Akhir) untuk melompat keluar dari struktur kontrol sebelum perintah Akhir tercapai ..." (selengkapnya)


7
Wow, saya pikir saya ingat ini. Saya terus melompat keluar dari loop saya dalam program dasar lama saya dan memperhatikan bagaimana TI-84 + saya menjadi lebih lambat dan lebih lambat ...
Ray

2
Yap, sebagian besar dari kita tahu perasaan;) @RayKoopa
Timtech

13
+1 untuk Ti Basic. Saya menghabiskan sebagian besar tahun kelas 9 saya memprogram hal-hal itu.
markasoftware

Apa yang Anda butuhkan Pause pada akhirnya? Anda bisa menghemat 2 byte.
kamoroso94

@ kamoroso94 Saya kira begitu, karena "Jika sebuah program berakhir, kebocoran akan dihapus dan tidak akan menyebabkan masalah lebih lanjut", jadi itu adalah untuk menghentikan program dari berakhir.
Timtech

32

Python <3.6.5, 23 byte

property([]).__init__()

property.__init__kebocoran referensi untuk properti tua fget, fset, fdel, dan __doc__jika Anda menyebutnya pada yang sudah-diinisialisasi propertymisalnya. Ini adalah bug, akhirnya dilaporkan sebagai bagian dari masalah CPython 31787 dan diperbaiki dalam Python 3.6.5 dan Python 3.7.0 . (Juga, ya, property([])adalah hal yang dapat Anda lakukan.)


Sudahkah laporan bug dikirim?
mbomb007


27

C #, 34 byte

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

Ini solusi tidak memerlukan Heap. Itu hanya membutuhkan kerja keras yang nyata GC ( Pengumpul Sampah ).

Pada dasarnya itu mengubah GC menjadi musuhnya sendiri.

Penjelasan

Setiap kali destructor dipanggil, ia menciptakan instance baru dari kelas jahat ini selama batas waktu habis dan memberitahu GC untuk hanya membuang objek itu tanpa menunggu destruktor selesai. Pada saat itu ribuan instance baru telah dibuat.

"Kejahatan" ini adalah, semakin keras GC bekerja, semakin banyak ini akan meledak di wajah Anda.

Penafian : GC Anda mungkin lebih pintar dari milik saya. Keadaan lain dalam program dapat menyebabkan GC mengabaikan objek pertama atau destruktornya. Dalam kasus ini, ini tidak akan meledak. Tetapi dalam banyak variasi itu akan terjadi . Menambahkan beberapa byte di sana-sini mungkin memastikan kebocoran untuk setiap keadaan yang memungkinkan. Yah kecuali untuk saklar daya mungkin.

Uji

Berikut ini adalah test suite :

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

Output setelah 10 menit:

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

Itu 2 684 476 624 byte. Total WorkingSetprosesnya sekitar 4,8 GB

Jawaban ini telah terinspirasi oleh artikel indah Eric Lippert: Ketika semua yang Anda tahu salah .


Ini sangat menarik. Apakah pengumpul sampah "Lupa" bahwa ada beberapa hal yang ada dan kehilangan jejak mereka karena ini? Saya tidak tahu banyak tentang c #. Juga sekarang saya bertanya-tanya, apa perbedaan antara bom dan kebocoran? Saya membayangkan kegagalan serupa dapat dibuat dengan memanggil konstruktor dari dalam konstruktor, atau memiliki fungsi berulang tak terbatas yang tidak pernah berhenti, meskipun secara teknis sistem tidak pernah kehilangan jejak referensi tersebut, hanya kehabisan ruang ...
don cerah

1
Konstruktor di dalam konstruktor akan menyebabkan stack overflow. Tetapi destruktor dari instance dipanggil dalam hierarki datar. GC sebenarnya tidak pernah kehilangan jejak objek. Setiap kali ia mencoba menghancurkannya, tanpa disadari ia menciptakan objek baru. Kode pengguna di sisi lain tidak memiliki akses ke objek tersebut. Juga inkonsistensi yang disebutkan dapat muncul karena GC dapat memutuskan untuk menghancurkan suatu objek tanpa memanggil destruktornya.
MrPaulch

Bukankah tantangannya akan selesai hanya dengan menggunakan class L{~L(){new L();}}? AFAIK for(;;)satu - satunya membuatnya bocor memori lebih cepat, kan?
BgrWorker

1
Sayangnya tidak. Karena untuk setiap objek yang hancur, hanya satu instance baru yang akan dibuat, yang sekali lagi tidak dapat diakses dan ditandai untuk dihancurkan. Ulangi. Hanya satu objek yang akan ditangguhkan untuk kehancuran. Tidak ada peningkatan populasi.
MrPaulch

2
Tidak juga. Akhirnya satu yang diselesaikan akan diabaikan. Objek yang sesuai akan dimakan terlepas.
MrPaulch

26

C (gcc) , 15 byte

f(){malloc(1);}

Verifikasi

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

Golf

setInterval(0)

Mendaftarkan handler interval kosong dengan penundaan default, membuang id timer yang dihasilkan (membuatnya tidak mungkin dibatalkan).

masukkan deskripsi gambar di sini

Saya telah menggunakan interval non-default, untuk membuat beberapa juta timer, untuk menggambarkan kebocoran, karena menggunakan interval default memakan CPU seperti mad.


5
Haha Saya suka bahwa Anda mengetik 'Golf', membuat saya ingin tahu tentang versi yang ungolfed
Martijn

9
mungkin terlihat seperti iniif(window && window.setInterval && typeof window.setInterval === 'function') { window.setInterval(0); }
Tschallacka

3
Sebenarnya, ini bukan tidak mungkin untuk dibatalkan: ID interval (dan batas waktu) diberi nomor secara berurutan, jadi cukup mudah untuk membatalkannya hanya dengan menelepon clearIntervaldengan ID yang bertambah hingga interval Anda hilang. Sebagai contoh:for(let i=0;i<1e5;i++){try{clearInterval(i);}catch(ex){}}
user2428118

5
@ user2428118 Seperti yang dikatakan zeppelin, ini tidak lebih "sah" daripada mengatakan kebocoran C / C ++ tidak "nyata" karena Anda bisa memaksa panggilan kefree()
TripeHound

1
Wow, tidak banyak tantangan di mana JavaScript adalah pesaing sebenarnya ...
Jared Smith

19

Java, 10 byte

Akhirnya, jawaban kompetitif di Jawa!

Golf

". "::trim

Ini adalah referensi metode (terhadap konstanta string), yang dapat digunakan seperti itu:

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

String literal ". "akan secara otomatis ditambahkan ke kumpulan string global yang diinternir , seperti yang dikelola oleh java.lang.Stringkelas, dan saat kami segera memangkasnya, referensi untuk itu tidak dapat digunakan kembali lebih lanjut dalam kode (kecuali jika Anda mendeklarasikan string yang sama persis lagi).

...

Kumpulan string, awalnya kosong, dikelola secara pribadi oleh String kelas.

Semua string literal dan ekspresi konstan bernilai string diinternir. Literal string didefinisikan di bagian 3.10.5 dari Spesifikasi Bahasa Java ™.

...

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

Anda bisa mengubahnya dalam kebocoran memori "tingkat produksi", dengan menambahkan string ke dirinya sendiri dan kemudian memanggil metode intern () secara eksplisit, dalam satu lingkaran.


2
Saya menganggap ini untuk C # ... tapi saya tidak menganggapnya penting, karena seperti yang Anda katakan Anda dapat mengakses memori itu dengan memasukkan string literal lainnya. Saya juga tertarik untuk mengetahui apa yang ("." + " ").intern()akan dilakukan (jika mereka input pengguna atau w / e, jadi kami mengabaikan optimisasi kompiler).
VisualMelon

1
Memang, satu-satunya konsensus ramping yang terbaik, saya hanya tegas pada sisi "kode harus dikompilasi". Saya masih tidak yakin saya membeli solusi ini mengingat kata-kata pada pertanyaan (string ini tidak dapat dibebaskan, dan mereka dapat ditemukan dalam kode operasi normal meskipun tidak mungkin), tetapi saya mengundang orang lain untuk membuat penilaian sendiri
VisualMelon

3
Tali itu bahkan tidak bisa diakses , apalagi bocor. Kami dapat mengambilnya kapan saja dengan menginternir string yang sama. Jika ini dihitung, variabel global yang tidak digunakan (private static di Java) akan menjadi kebocoran. Itu bukan bagaimana kebocoran memori didefinisikan.
user2357112

3
@ user2357112 "... String itu bahkan tidak dapat diakses ..." Itu hanya terlihat jelas karena Anda melihat kode. Sekarang anggap Anda mendapat referensi metode ini X () sebagai argumen untuk kode Anda, Anda tahu bahwa itu mengalokasikan (dan magang) string literal di dalam, tetapi Anda tidak tahu yang mana tepatnya, itu mungkin "." Atau "123" atau string lain dengan panjang (umumnya) tidak diketahui. Tolong tunjukkan bagaimana Anda masih bisa mengaksesnya, atau membatalkan alokasi entri di kumpulan "intern" yang ditempati?
zeppelin

2
@ user2357112 Pada mesin dengan memori yang terbatas, Anda dapat mengakses nilai yang tersimpan di setiap bagian memori simply by guessing it correctly, tetapi itu tidak berarti bahwa hal seperti kebocoran memori tidak ada. there's probably some way to use reflection to determine the string's contents toodapatkah kamu menunjukkan ini? (hint, String.intern () diimplementasikan dalam kode asli ).
zeppelin

17

Karat, 52 byte

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

Mengalokasikan beberapa byte dengan sistem malloc. Ini mengasumsikan bahwa ABI yang salah dapat diterima.


Karat (dalam teori), 38 byte

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

Kami mengalokasikan memori pada heap, mengekstrak pointer mentah, dan kemudian mengabaikannya, membocorkannya secara efektif. ( Box::into_rawlebih pendek dari itu std::mem::forget).

Namun, Rust secara default menggunakan jemalloc, yang valgrind tidak dapat mendeteksi kebocoran . Kita bisa beralih ke pengalokasi sistem tetapi itu menambahkan 50 byte dan membutuhkan malam. Terima kasih banyak untuk keamanan memori.


Output dari program pertama:

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

luar biasa. posting seperti ini membuat saya menjelajahi Rust selama setahun terakhir, pasti salah satu bahasa paling asyik yang saya coba pelajari.
don bright

16

8086 ASM, 3 byte

Contoh ini mengasumsikan bahwa runtime C terhubung di.

jmp _malloc

ini berkumpul di e9 XX XXmana XX XXalamat relatif dari_malloc

Ini meminta mallocuntuk mengalokasikan jumlah memori yang tidak dapat diprediksi dan kemudian segera kembali, mengakhiri proses. Pada beberapa sistem operasi seperti DOS, memori mungkin tidak dapat dipulihkan sama sekali sampai sistem di-boot ulang!


Implementasi normal malloc akan menghasilkan memori yang dibebaskan pada proses keluar.
Joshua

@ Yosua Ya, tapi itu implementasi perilaku yang ditentukan.
FUZxxl

12

Keempat, 6 byte

Golf

s" " *

Mengalokasikan string kosong dengan s" ", meninggalkan alamat dan panjangnya (0) pada stack, kemudian mengalikannya (menghasilkan alamat memori yang hilang).

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

lanjutkan 45 byte

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

ini menciptakan goroutine anonim dengan loop tak terbatas di dalamnya. program dapat terus berjalan seperti biasa, karena memulai goroutine adalah sejenis seperti menelurkan thread kecil yang berjalan bersamaan, tetapi program tidak memiliki cara untuk merebut kembali memori yang dialokasikan untuk goroutine. pemulung tidak akan pernah mengumpulkannya karena masih berjalan. beberapa orang menyebut ini 'bocor goroutine'


cek golf: ini 2 byte lebih pendek daripada menelepon C.malloc(8), karena Anda perluimport"C"
Riking

9

Java 1.3, 23 byte

void l(){new Thread();}

Membuat utas tetapi tidak memulainya. Utas terdaftar di kumpulan utas internal, tetapi tidak akan pernah dimulai, jadi tidak pernah berakhir dan karenanya tidak pernah menjadi kandidat untuk GC. Ini adalah objek yang tidak dapat diperbaiki, terjebak dalam Java limbos.

Ini adalah bug di Java hingga 1.3 yang dimasukkan setelah diperbaiki.

Pengujian

Program berikut memastikan untuk mencemari memori dengan objek utas baru dan menunjukkan ruang memori bebas yang menurun. Demi pengujian kebocoran, saya secara intensif membuat GC berjalan.

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

Karena ini hanya berfungsi pada versi Java tertentu, Anda harus mengatakan "Java 3" di header Anda.

5
Tidak ada yang namanya Java 3. Ini Java 1.3. Ada Java 1.0, 1.1, 2, 1.3, 1.4, 5, 6, 7, 8, 9. Penomoran yang aneh, tapi begitulah adanya.
Olivier Grégoire

Ini juga ideku. Sial.
Magic Gurita Guci

8

Befunge ( jamur ), 1 byte

$

Ini mungkin tergantung pada platform dan bergantung pada versi (Saya baru menguji dengan versi 1.0.4 di Windows), tetapi secara historis jamur telah menjadi juru bahasa yang sangat bocor. Perintah $(drop) seharusnya tidak melakukan apa-apa pada tumpukan kosong, tetapi mengulangi kode ini entah bagaimana berhasil membocorkan banyak memori dengan sangat cepat. Dalam hitungan detik itu akan menghabiskan beberapa pertunjukan dan akan crash dengan kesalahan "kehabisan memori".

Perhatikan bahwa itu tidak harus berupa $perintah - apa pun bisa dilakukan. Itu tidak akan bekerja dengan file sumber kosong. Harus ada setidaknya satu operasi.


8

Swift 3, 38 byte

Versi baru:

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

x memiliki referensi yang kuat untuk dirinya sendiri, sehingga tidak akan dialokasikan, menyebabkan kebocoran memori.

Versi lama:

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

xberisi referensi yang kuat untuk y, dan sebaliknya. Dengan demikian, tidak ada yang akan dialokasikan, menyebabkan kebocoran memori.


Hmm, Anda masih bisa referensi memori itu melalui xdan y, jadi ini tidak benar-benar terlihat seperti kebocoran kepada saya (kecuali jika Anda menghancurkannya entah bagaimana).
zeppelin

@zeppelin Baris terakhir dapat dibungkus dengan fungsi untuk memperbaikinya
NobodyNada

@NobodyNada, jika saya meletakkan baris terakhir di doblok yang akan memperbaiki masalah yang zeppelin angkat bukan?
Daniel

@Dappapp Ya; a doakan bekerja juga. Ide bagus!
NobodyNada

Ini dapat dipersingkat, Anda tidak perlu dua kelas - X dapat menyimpan referensi untuk dirinya sendiri:class X{var x: X!};do{let x=X();x.x=x}
Sebastian Osiński

7

Delphi (Object Pascal) - 33 byte

Membuat objek tanpa variabel, program konsol penuh:

program;begin TObject.Create;end.

Mengaktifkan FastMM4 di proyek akan menunjukkan kebocoran memori:

masukkan deskripsi gambar di sini


6

C # - 84bytes

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

Ini mengalokasikan tepat 1 byte memori yang tidak dikelola, dan kemudian kehilangan IntPtr, yang saya percaya adalah satu-satunya cara untuk mendapatkan atau membebaskannya. Anda dapat mengujinya dengan memasukkannya dalam satu lingkaran, dan menunggu aplikasi mogok (mungkin ingin menambahkan beberapa nol untuk mempercepatnya).

Saya mempertimbangkan System.IO.File.Create("a");dan semacamnya, tetapi saya tidak yakin bahwa ini adalah kebocoran memori, karena aplikasi itu sendiri akan mengumpulkan memori, itu adalah OS di bawahnya yang mungkin bocor (karena Closeatau Disposetidak dipanggil). Hal-hal akses file juga memerlukan izin sistem file, dan tidak ada yang mau bergantung pada itu. Dan ternyata ini tidak akan bocor, karena tidak ada yang menghentikan finalis dipanggil (yang membebaskan sumber daya yang mendasarinya adalah mungkin), yang kerangka kerjanya termasuk untuk mengurangi kesalahan penilaian semacam ini (sampai tingkat tertentu), dan untuk membingungkan programmer dengan penguncian file yang tampaknya non-deterministik (jika Anda seorang yang sinis). Terima kasih kepada Jon Hanna karena telah meluruskan hal ini.

Saya sedikit kecewa karena saya tidak dapat menemukan cara yang lebih pendek. NET. GC berfungsi, saya tidak bisa memikirkan IDisposablesmscorlib yang pasti akan bocor (dan memang mereka semua tampaknya memiliki finalis, betapa menjengkelkan) , saya tidak mengetahui cara lain untuk mengalokasikan memori yang tidak dikelola (singkat PInvoke ), dan refleksi memastikan apa pun dengan referensi untuk itu (terlepas dari bahasa semantik (misalnya anggota pribadi atau kelas tanpa pengakses)) dapat ditemukan.


1
System.IO.File.Create("a")tidak akan membocorkan apa pun, tetapi GC.SuppressFinalize(System.IO.File.Create("a"))akan seperti itu secara eksplisit diminta untuk tidak menjalankan finaliser yang FileStreamdiproduksi.
Jon Hanna

@ JonHanna benar sekali. Paranoia IDisposable-ku sepertinya sudah lebih baik dari diriku.
VisualMelon

Anda mungkin memiliki kesempatan untuk membuat kebocoran GDI + menggunakan System.Drawing.Bitmap. Tidak tahu apakah itu diperhitungkan karena itu adalah pustaka windows yang menyebabkan kebocoran, bukan program itu sendiri.
BgrWorker

@ BgrWorker mereka tidak diragukan lagi memiliki finaliser juga, dan saya cenderung menghindari perpustakaan eksternal dalam kode-golf karena saya tidak setuju dengan konsensus mengenai biaya mereka: jika Anda dapat menemukan cara yang Anda yakini, jangan ragu untuk memposting itu dalam jawaban Anda sendiri!
VisualMelon

<!-- language: lang-c# -->Terima kasih untuk ini & jawaban yang bagus! (Ini C # jadi saya menyukainya)
Metoniem

5

Faktor , 13 byte

Factor memiliki manajemen memori otomatis, tetapi juga memberikan akses ke beberapa fungsi libc:

1 malloc drop

Secara manual mengalokasikan 1 byte memori, mengembalikan alamatnya, dan menjatuhkannya.

malloc sebenarnya mendaftar salinan untuk melacak kebocoran memori dan membebaskan ganda, tetapi mengidentifikasi yang Anda bocor bukanlah tugas yang mudah.

Jika Anda lebih suka memastikan Anda benar-benar kehilangan referensi itu:

1 (malloc) drop

Menguji kebocoran dengan [ 1 malloc drop ] leaks.mengatakan:

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

Menguji kebocoran dengan [ 1 (malloc) drop ] leaks.mengatakan:

| Disposable class | Instances | |

Oh tidak! Faktor yang buruk, ia menderita Alzheimer sekarang! D:


4

Otomatis , 39 byte

#include<Memory.au3>
_MemGlobalAlloc(1)

Mengalokasikan satu byte dari heap. Karena pegangan yang dikembalikan oleh _MemGlobalAllocdibuang, tidak ada cara untuk secara eksplisit membebaskan alokasi itu.


4

Gangguan Umum (hanya SBCL), 28 26 byte

sb-alien::(make-alien int)

Anda menjalankannya seperti ini sbcl --eval 'sb-alien::(make-alien int)':; tidak ada yang dicetak atau dikembalikan, tetapi alokasi memori terjadi. Jika saya membungkus formulir di dalam a (print ...), pointer ditampilkan di REPL.

  1. package::(form)adalah notasi khusus dalam SBCL untuk sementara mengikat paket saat ini saat membaca formulir. Ini digunakan di sini untuk menghindari awalan baik dengan make-alienmaupun intdengan sb-alien. Saya pikir akan curang untuk menganggap paket saat ini diatur untuk yang satu ini, karena itu tidak terjadi pada saat startup.

  2. make-alien mengalokasikan memori untuk jenis yang diberikan dan ukuran opsional (menggunakan malloc).

  3. Saat menjalankan ini di REPL, tambahkan 0setelah alokasi sehingga REPL tidak mengembalikan pointer, tetapi nilai itu sebagai gantinya. Jika tidak, bahwa tidak ada akan menjadi kebocoran nyata karena REPL ingat yang terakhir tiga nilai kembali (Lihat *, **,*** ) dan kami masih bisa memiliki kesempatan untuk membebaskan memori yang dialokasikan.

2 byte dihapus berkat PrzemysławP, terima kasih!


1
Tidak dapat Anda gunakan 1(atau 2, 3, dll) bukannya ()sehingga Anda mengembalikan nilai 1? Ini akan menghemat 1 byte. Juga apakah jawaban ini hanya REPL? Mungkin jika Anda memuat kode dengan loadAnda tidak dapat memasukkan ()atau apa pun pada akhirnya, karena itu tidak akan dapat diakses?

1
@ PrzemysławP Anda benar di kedua titik, saya mencoba evaldan ini berfungsi seperti yang Anda katakan. Terima kasih banyak!
coredump

3

C ++, 16 byte

main(){new int;}

Saya tidak punya valgrind untuk memeriksanya, tetapi cukup yakin seharusnya. Kalau tidak, saya akan mencoba:

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

Hasil Valgrind

(Itu memang bocor)

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

@WheatWizard saya menggunakan g++ 4.3.2(bukan yang terbaru) dan mengkompilasi dengan baik. Tidak ada tipe pengembalian intsecara default saya pikir. Dengan -Wallsaya punya peringatan:plop.cpp:1: warning: ISO C++ forbids declaration of 'main' with no type
matovitch

2
@WheatWizard Maaf, saya baru saja melihat Anda memberikan contoh c ++ untuk memulai kontes. Datang dari reddit saya hanya melihat jawaban dan (anehnya) tidak melihat C ++. Saya merasa agak bodoh. : /
matovitch

Konsensus adalah bahwa Anda dapat menghitung hanya []{new int;}sebagai fungsi C ++ (tantangannya tidak menentukan keseluruhan program).
Toby Speight

3

Java (OpenJDK 9) , 322 220 byte

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

Cobalah online!

Ini adalah kebocoran Memori lain yang tidak menggunakan String Cache. Mengalokasikan setengah dari RAM Anda dan Anda tidak dapat melakukan apa-apa dengannya.

Terima kasih kepada zeppelin untuk menyimpan semua byte


Anda dapat menyimpan banyak byte dengan memperoleh Unsafeinstance dari variabel statis di dalamnya, seperti itu: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

Dan Anda dapat menyimpan lebih banyak dengan mengganti public static void maindengan initializer statis static{try{}catch(Exception e){}}(yang mungkin sedikit lebih sulit untuk diluncurkan tetapi tetap valid dan dapat dikompilasi).
zeppelin

ya penggunaan konstruktor itu digunakan dalam versi Android dari kode yang saya gunakan. Saya akan mengubah beberapa hal ketika im @home tapi saya akan pergi jalan Anda pergi dengan satu pernyataan;)
Serverfrog

Hapus spasi putih, gunakan aalih-alih args, dan hapus publik. tio.run/nexus/…
Pavel

true dapat diganti dengan 1> 0
masterX244

3

c, 9 byte

main(){}

Bukti:

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
Anda sebenarnya tidak membocorkan memori; gccaku s. Ini juga harus bekerja dengan program kosong. Coba gcc src.c && valgrind ./a.out, yang seharusnya menghasilkan hasil yang bersih.

3

C #, 109 byte

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

Kami menemukan ide di balik kebocoran ini dalam kode produksi dan meneliti mengarah pada artikel ini. Masalah utama adalah dalam kutipan panjang dari artikel ini (baca untuk info lebih lanjut):

Mencari kode saya untuk PurchaseOrder, saya menemukan baris kode ini di page_loadsalah satu halaman sayaXmlSerializer serializer = new XmlSerializer(typeof(PurchaseOrder), new XmlRootAttribute(“”));

Ini akan tampak seperti potongan kode yang tidak bersalah. Kami membuat XMLSerializeruntuk PurchaseOrder. Tapi apa yang terjadi di balik selimut?

Jika kita melihat XmlSerializerkonstruktor dengan Reflector kita menemukan bahwa ia memanggil this.tempAssembly = XmlSerializer.GenerateTempAssembly(this.mapping, type, defaultNamespace, location, evidence);yang menghasilkan rakitan (dinamis). Jadi setiap kali kode ini berjalan (yaitu setiap kali halaman dihantam) akan menghasilkan rakitan baru.

Alasannya menghasilkan perakitan adalah bahwa ia perlu menghasilkan fungsi untuk membuat serial dan deserializing dan ini perlu berada di suatu tempat.

Ok, baiklah ... itu menciptakan sebuah perakitan, jadi apa? Ketika kita selesai dengan itu, itu seharusnya menghilang begitu saja?

Ya ... sebuah majelis bukanlah objek pada Heap GC, GC benar-benar tidak menyadari rakitan, sehingga tidak akan mengumpulkan sampah. Satu-satunya cara untuk menyingkirkan rakitan di 1.0 dan 1.1 adalah dengan membongkar domain aplikasi tempat ia berada.

Dan di situlah letak masalahnya Dr. Watson.

Berjalan dari kompiler di Visual Studio 2015 dan menggunakan Jendela Alat Diagnostik menunjukkan hasil berikut setelah sekitar 38 detik. Perhatikan bahwa memori Proses terus naik dan Garbage Collector (GC) terus berjalan tetapi tidak dapat mengumpulkan apa pun.

Jendela Alat Diagnostik


2

C 30 byte

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

Hasil 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
Apakah mungkin untuk melakukannya main(){malloc(1);}?
kirbyfan64sos

@ Ya itu! Tetapi sudah diposting!
Abel Tom

2

Dart, 76 byte

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

Agak seperti jawaban JavaScript. Saat Anda memanggil .listenobjek stream Dart, Anda diberikan kembali StreamSubscription, yang memungkinkan Anda untuk memutuskan koneksi dari stream. Namun, jika Anda membuangnya, Anda tidak akan pernah dapat berhenti berlangganan dari sungai, menyebabkan kebocoran. Satu-satunya cara kebocoran dapat diperbaiki adalah jika Stream itu sendiri dikumpulkan, tetapi masih dirujuk secara internal oleh kombo StreamController + Timer.

Sayangnya, Dart terlalu pintar untuk hal-hal lain yang pernah saya coba. ()async=>await new Completer().futuretidak bekerja karena menggunakan menunggu sama dengan melakukan new Completer().future.then(<continuation>), yang memungkinkan penutupan itu sendiri dihancurkan ketika Completer kedua tidak direferensikan (Completer memegang referensi ke Masa Depan dari .future, Future memegang referensi ke kelanjutan sebagai penutupan).

Juga, Isolat (alias utas) dibersihkan oleh GC, jadi menelurkan diri Anda di utas baru dan segera menghentikannya ( import'dart:isolate';main(_)=>Isolate.spawn(main,0,paused:true);) tidak berfungsi. Bahkan memijahkan Isolate dengan infinite loop ( import'dart:isolate';f(_){while(true){print('x');}}main()=>Isolate.spawn(f,0);) membunuh Isolate dan keluar dari program.

Baiklah.


jika program utama Anda terus berjalan dan melakukan hal-hal lain, akankah pemulung dapat menghentikan isolasi? saya bertanya karena contoh goroutine saya kedengarannya mirip ... saya berasumsi bahwa fakta bahwa program keluar dan mengembalikan semua memori ke OS tidak selalu berarti belum bocor.
don bright

2

Cepat, 12 byte

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

Penjelasan:

Ini adalah kebocoran memori de-facto yang dapat terjadi dalam bahasa apa pun, terlepas dari apakah bahasa tersebut menggunakan manajemen manual memori, penghitungan referensi otomatis (ARC, seperti Swift), atau bahkan pengumpulan sampah yang menyapu.

[3,5]hanyalah sebuah array literal. Array ini mengalokasikan cukup memori untuk setidaknya 2 elemen ini. The 3dan 5hanya sewenang-wenang.

Berlangganan (mengindeks) dan Array<T>menghasilkan ArraySlice<T>. An ArraySlice<T>adalah pandangan ke dalam memori Array tempat ia dibuat.

[3,5][0...0]menghasilkan ArraySlice<Int>, yang nilainya [3]. Perhatikan bahwa 3dalam irisan ini adalah 3elemen yang sama dengan 3yang asli yang Arrayditunjukkan di atas, bukan salinan.

Irisan yang dihasilkan kemudian dapat disimpan ke dalam variabel dan digunakan. Array asli tidak lagi direferensikan, jadi Anda akan berpikir bahwa itu bisa dibatalkan alokasinya. Namun, tidak bisa.

Karena irisan memperlihatkan pandangan ke memori array asalnya, array asli harus tetap hidup selama irisan hidup. Jadi dari 2memori ukuran elemen-ukuran asli yang dialokasikan, hanya memori ukuran-elemen pertama yang digunakan, dengan yang lain yang diperlukan untuk ada agar tidak mengalokasikan yang pertama. Elemen-ukuran kedua dari memori adalah faktor bocor.

Solusi untuk masalah ini adalah tidak menjaga irisan kecil array besar untuk waktu yang lama. Jika Anda perlu mempertahankan konten slice, maka promosikan ke Array, yang akan memicu memori untuk disalin, sehingga menghapus ketergantungan pada memori array asli:

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

2

Solusi 1: C (Mac OS X x86_64), 109 byte

Sumber untuk golf_sol1.c

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

Program di atas perlu dikompilasi dengan akses eksekusi pada segmen __DATA.

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

Kemudian untuk menjalankan program jalankan yang berikut:

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

Hasil:

Sayangnya Valgrind tidak melihat memori yang dialokasikan dari panggilan sistem, jadi saya tidak dapat menunjukkan kebocoran yang terdeteksi dengan baik.

Namun kita dapat melihat vmmap untuk melihat sebagian besar memori yang dialokasikan (MALLOC metadata).

                                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 

Penjelasan

Jadi saya pikir saya perlu menggambarkan apa yang sebenarnya terjadi di sini, sebelum pindah ke solusi yang ditingkatkan.

Fungsi utama ini adalah menyalahgunakan deklarasi tipe C yang hilang (sehingga default ke int tanpa kita perlu membuang karakter menulisnya), serta bagaimana simbol bekerja. Linker hanya peduli apakah mereka tidak dapat menemukan simbol yang dipanggil mainuntuk dipanggil. Jadi di sini kita membuat array int utama yang kita inisialisasi dengan shellcode kita yang akan dieksekusi. Karena itu, main tidak akan ditambahkan ke segmen __TEXT melainkan segmen __DATA, alasan kami perlu mengkompilasi program dengan segmen __DATA yang dapat dieksekusi.

Shellcode yang ditemukan di main adalah sebagai berikut:

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

Apa yang dilakukan adalah memanggil fungsi syscall untuk mengalokasikan halaman memori (syscall mach_vm_allocate menggunakan internal). RAX harus sama dengan 0x100000a (memberitahu syscall fungsi mana yang kita inginkan), sementara RDI memegang target untuk alokasi (dalam kasus kami kami ingin ini mach_task_self ()), RSI harus memegang alamat untuk menulis pointer ke memori yang baru dibuat (jadi kami hanya mengarahkannya ke bagian pada stack), RDX memegang ukuran alokasi (kami hanya meneruskan dalam RAX atau 0x100000a hanya untuk menghemat byte), R10 memegang bendera (kami menunjukkan itu bisa dialokasikan di mana saja).

Sekarang tidak jelas dari mana RAX dan RDI mendapatkan nilai-nilai mereka. Kami tahu RAX harus 0x100000a, dan RDI harus menjadi nilai mach_task_self () yang dikembalikan. Untungnya mach_task_self () sebenarnya adalah makro untuk variabel (mach_task_self_), yang berada di alamat memori yang sama setiap kali (harus berubah saat reboot). Dalam contoh khusus saya, mach_task_self_ kebetulan berada di 0x00007fff7d578244. Jadi untuk mengurangi instruksi, kami akan meneruskan data ini dari argv. Inilah sebabnya kami menjalankan program dengan ungkapan ini$(ruby -e 'puts "\xf5\xff\xff\xfe\xff\xff\x44\x82\x57\x7d\xff\x7f"')untuk argumen pertama. String adalah dua nilai yang digabungkan, di mana nilai RAX (0x100000a) hanya 32 bit dan telah memiliki komplemen seseorang yang diterapkan padanya (jadi tidak ada byte nol; kami hanya BUKAN nilai untuk mendapatkan yang asli), nilai berikutnya adalah RDI (0x00007fff7d578244) yang telah bergeser ke kiri dengan 2 byte sampah tambahan ditambahkan ke akhir (lagi untuk mengecualikan byte nol, kami hanya menggesernya kembali ke kanan untuk mendapatkannya kembali ke aslinya).

Setelah syscall, kami menulis ke memori yang baru dialokasikan. Alasannya adalah karena memori yang dialokasikan menggunakan mach_vm_allocate (atau syscall ini) sebenarnya adalah halaman VM, dan tidak secara otomatis dipetakan ke dalam memori. Sebaliknya mereka dicadangkan sampai data dituliskan kepada mereka, dan kemudian halaman-halaman itu dipetakan ke dalam memori. Tidak yakin apakah itu akan memenuhi persyaratan jika itu hanya dicadangkan.

Untuk solusi selanjutnya, kami akan memanfaatkan fakta bahwa shellcode kami tidak memiliki byte nol, sehingga dapat memindahkannya ke luar kode program kami untuk mengurangi ukurannya.

Solusi 2: C (Mac OS X x86_64), 44 byte

Sumber untuk golf_sol2.c

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

Program di atas perlu dikompilasi dengan akses eksekusi pada segmen __DATA.

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

Kemudian untuk menjalankan program jalankan yang berikut:

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

Hasilnya harus sama seperti sebelumnya, karena kami membuat alokasi dengan ukuran yang sama.

Penjelasan

Mengikuti konsep yang sama seperti solusi 1, dengan pengecualian bahwa kami telah memindahkan potongan kode bocor kami di luar program.

Shellcode yang ditemukan di main sekarang adalah sebagai berikut:

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

Ini pada dasarnya menyalin shellcode yang kami berikan dalam argv untuk menjadi setelah kode ini (jadi setelah itu disalin, itu akan menjalankan shellcode yang dimasukkan). Apa yang menguntungkan kami adalah bahwa segmen __DATA akan berukuran setidaknya satu halaman, jadi meskipun kode kami tidak sebesar itu, kami masih dapat "dengan aman" menulis lebih banyak. Kelemahannya adalah solusi ideal di sini, bahkan tidak perlu salinannya, melainkan hanya akan memanggil dan menjalankan shellcode di argv secara langsung. Namun sayangnya, memori ini tidak memiliki hak eksekusi. Kita dapat mengubah hak memori ini, namun itu membutuhkan lebih banyak kode daripada hanya menyalinnya. Strategi alternatif adalah mengubah hak dari program eksternal (tetapi lebih lanjut tentang itu nanti).

Shellcode yang kami berikan ke argv adalah sebagai berikut:

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

Ini hampir sama dengan kode kami sebelumnya, hanya perbedaannya adalah kami menyertakan nilai untuk EAX dan RDI secara langsung.

Kemungkinan Solusi 1: C (Mac OS X x86_64), 11 byte

Gagasan memodifikasi program secara eksternal, memberi kita kemungkinan solusi untuk memindahkan leaker ke program eksternal. Di mana program aktual kami (pengiriman) hanyalah program tiruan, dan program leaker akan mengalokasikan sebagian memori dalam program target kami. Sekarang saya tidak yakin apakah ini akan masuk dalam aturan untuk tantangan ini, tetapi tetap membagikannya.

Jadi jika kita menggunakan mach_vm_allocate dalam program eksternal dengan target yang ditetapkan untuk program tantangan kita, itu bisa berarti program tantangan kita hanya perlu menjadi sesuatu seperti:

main=65259;

Di mana shellcode itu hanyalah lompatan pendek ke dirinya sendiri (lompatan / loop tak terbatas), sehingga program tetap terbuka dan kita dapat merujuknya dari program eksternal.

Kemungkinan Solusi 2: C (Mac OS X x86_64), 8 byte

Lucunya ketika saya melihat output valgrind, saya melihat bahwa setidaknya menurut valgrind, memori kebocoran bocor. Jadi secara efektif setiap program membocorkan sejumlah memori. Dengan demikian, kita sebenarnya bisa membuat program yang tidak melakukan apa-apa (hanya keluar), dan itu sebenarnya akan membocorkan memori.

Sumber:

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

Bahasa Inggris Polos , 71 70 58 35 byte

Dihapus 1 byte dengan menghapus garis kosong. Menghapus 12 byte dengan menghilangkan definisi tipe "bogon", dan menggunakan tipe "thing" induk alih-alih subtipe "bogon". Dihapus 23 byte dengan beralih dari program lengkap, menjadi hanya rutin yang bocor memori.

Versi golf:

To x:
Allocate memory for a thing.

Versi tidak digabungkan yang merupakan program lengkap, menggunakan definisi subtipe, dan tidak bocor memori:

A bogon is a thing.

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

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

Jika versi golf "x" dipanggil, itu akan membocorkan memori sebanding dengan berapa kali "x" dipanggil. Dalam versi golf, "Deallocate the thing." akan memperbaiki kebocoran memori.

Bahasa Inggris Biasa memeriksa kebocoran memori secara default. Ketika versi yang bocor memori dijalankan, kotak dialog akan muncul tepat sebelum program dimatikan. Kotak dialog memiliki judul "debug", pesan "1 tetes", dan tombol "OK". Semakin sering fungsi bocor disebut, semakin besar jumlah "tetesan" dalam pesan. Ketika versi yang tidak bocor memori dijalankan, kotak dialog tidak muncul.

Dalam Plain English, "benda" adalah penunjuk ke item dalam daftar yang ditautkan dua kali lipat. "Hal", "untuk memulai", dan "untuk mematikan" didefinisikan dalam modul yang disebut "mie", yang perlu disalin (biasanya sebagai file terpisah) ke dalam setiap proyek. "A", "the", "to", "untuk mengalokasikan memori", dan "untuk menghancurkan" didefinisikan dalam kompiler.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.