Пам'ять протікає в якомога менше байтах


79

Ваше завдання - написати код, який просочить хоча б один байт пам'яті в якомога менше байтах. Пам'ять повинна просочуватися, а не просто виділятися .

Пам'ять, що протікає, - це пам'ять, яку програма виділяє, але втрачає можливість доступу, перш ніж вона зможе правильно розподілити пам'ять. Для більшості мов високого рівня ця пам’ять повинна бути розподілена в купі.

Прикладом в C ++ може бути наступна програма:

int main(){new int;}

Це робить a new intна купі без вказівника на нього. Ця пам'ять миттєво просочується, оскільки ми не маємо доступу до неї.

Ось як може виглядати резюме витоку з 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

У багатьох мовах є налагоджувач пам'яті (наприклад, Valgrind ), якщо ви можете, вам слід включити вихід з такого налагоджувача, щоб підтвердити, що ви просочилися в пам'яті.

Мета - мінімізувати кількість байтів у вашому джерелі.


2
Можливо, у вас може бути різний діапазон просоченої кількості, і залежно від того, скільки ви протікаєте, ви втрачаєте x% від кількості байтів
Крістофер,

11
@ChristopherPeart Для одного я не прихильник бонусів за виклики, а для двох, як ви вже показали, дуже легко витікати без обмеженої пам'яті.
Пшеничний майстер

1
Пов'язані . Однак це не дублікат, тому що більшість відповідей на це питання утворюють нескінченну структуру, доступну в пам'яті, а не фактичну протікання пам'яті.

2
яка ідея? Що пам’ятку не можна звільнити? Я думаю, що для цього потрібно мати натурне виконання для зібраних сміттями мов чи використання помилок.
акостадінов

7
Я бачу, як мови, призначені для гри в гольф,
збиваються

Відповіді:


89

Perl (5.22.2), 0 байт

Спробуйте в Інтернеті!

Я знав, що там буде якась мова, яка просочилася пам’яттю в порожній програмі. Я очікував, що це буде езоланг, але виявляється, що perlпротікає пам'ять на будь-якій програмі. (Я припускаю, що це навмисно, оскільки звільнення пам’яті, якщо ви знаєте, що ви все-таки збираєтеся вийти, просто витрачає час; як така, загальна рекомендація в даний час полягає в тому, щоб просто просочити залишок пам’яті, коли ви знаходитесь у програмі виходу програми. .)

Перевірка

$ 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
Мені сподобалась відповідь Unlambda, але ця (IMHO) занадто велика розтягнення, оскільки, очевидно, сам інтерпретатор просочує пам'ять, тобто я виразно втрачаю: 7,742 байти в 14 блоків`, коли біжу perl --versionна своїй машині , незважаючи на те, що він ніколи не запускає жодну програму.
зеппелін

11
@zeppelin: Погоджено, але згідно з нашими правилами, це реалізація, яка визначає мову, таким чином, якщо впровадження просочує пам'ять, усі програми в мові протікають в пам'яті. Я не обов'язково впевнений, що згоден з цим правилом, але в цей момент він занадто закріпився, щоб дійсно міг змінитися.

8
Це також працює в Node JS.
Денніс

6
Це виглядає як нова стандартна лазівка ​​в процесі створення ...
Майкл Хемптон

46
Нарешті сценарій Perl, який я можу зрозуміти.
користувач11153

66

C, 48 31 22 байт

Попередження: Не запускайте це занадто багато разів.

Дякуємо Деннісу за велику допомогу / ідеї!

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

Це йде на крок далі. shmgetвиділяє спільну пам’ять, яка не розміщена після закінчення програми. Він використовує ключ для ідентифікації пам'яті, тому ми використовуємо неініціалізований int. Це технічно невизначена поведінка, але практично це означає, що ми використовуємо значення, яке знаходиться трохи вище вершини стека, коли це викликається. Це буде написано в наступний раз, коли що-небудь буде додано в стек, тому ми втратимо ключ.


Єдиний випадок, коли це не працює, це якщо ви можете з'ясувати, що було в стеці раніше. Для додаткових 19 байтів ви можете уникнути цієї проблеми:

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

Або на 26 байт:

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

Але за допомогою цього пам'ять просочується після виходу програми. Під час роботи програми є доступ до пам'яті, яка суперечить правилам, але після завершення програми ми втрачаємо доступ до ключа і пам'ять все одно виділяється. Для цього потрібна рандомізація розмітки адресного простору (ASLR), інакше &kзавжди буде однаковою. На сьогодні ASLR, як правило, увімкнено за замовчуванням.


Підтвердження:

Ви можете використовувати, ipcs -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 Теоретично shmid міг бути збережений у файлі, і програма може приєднатись до нього в майбутньому. Ось як працює спільна пам'ять Unix ...
tbodt

1
@AndrewSavinykh Спільна пам'ять в основному стає ресурсом, який ОС може надавати іншим процесам. Він схожий на файл, який живе в оперативній пам'яті, і будь-який процес, який знає ім'я (ключ), має доступ до нього, поки не буде видалений. Уявіть собі процес, який обчислює число і зберігає його в пам'яті і виходить перед тим, як процес, який читає дані, підключиться до спільної пам'яті. У цьому випадку, якщо ОС звільняє пам'ять, другий процес не може її отримати.
Райлі

35
Дякуємо, що опублікували це. Я просто захистив TIO від спільних витоків пам'яті.
Денніс

4
@Dennis Ось чому я не опублікував TIO-посилання. Я не знав, захищено це чи ні.
Райлі

12
Мені подобається, як ви використовуєте проблему зі словом, щоб описати сценарій, коли програма просочується менше пам'яті, ніж передбачалося.
kasperd

40

Unlambda ( c-refcnt/unlambda), 1 байт

i

Спробуйте в Інтернеті!

Це справді складне завдання щодо пошуку вже існуючого перекладача, який просочує пам'ять на дуже прості програми. У цьому випадку я використав Unlambda. Тут є не один офіційний інтерпретатор Unlambda, але він c-refcntє одним з найпростіших для побудови, і він має тут корисну властивість, що він просочує пам'ять при успішному запуску програми. Тому мені потрібно було надати найпростішу можливу юридичну програму Unlambda, но-оп. (Зверніть увагу, що порожня програма тут не працює; пам'ять все ще доступна в момент виходу інтерпретатора.)

Перевірка

$ wget ftp://ftp.madore.org/pub/madore/unlambda/unlambda-2.0.0.tar.gz
… Сніп…
2017-02-18 18:11:08 (975 Кб / с) - "unlambda-2.0.0.tar.gz" збережено [492894]
$ tar xf unlambda-2.0.0.tar.gz 
$ cd unlambda-2.0.0 / c-refcnt /
$ gcc unlambda.c
$ ехо -ні | valgrind ./a.out / dev / stdin
… Сніп…
== 3417 == ПІДСУМКИ ЛІКУ:
== 3417 == Виразно втрачено: 40 байт в 1 блоці
== 3417 == побічно втрачено: 0 байт в 0 блоках
== 3417 == можливо втрачено: 0 байт в 0 блоках
== 3417 == досі доступний: 0 байт у 0 блоках
== 3417 == придушено: 0 байт у 0 блоках
== 3417 == 
== 3417 == Для підрахунку виявлених та прихованих помилок повторіть з: -v
== 3417 == РЕЗЮМЕ ПОМИЛКИ: 1 помилка з 1 контексту (придушено: 0 від 0)

39

TI-Basic, 12 байт

While 1
Goto A
End
Lbl A
Pause 

"... витік пам'яті - це те, коли ви використовуєте Goto / Lbl в циклі або If умовно (все, що має команду End), щоб вискочити з цієї керуючої структури до досягнення команди" End "..." (більше)


7
Нічого собі, я думаю, це пам’ятаю. Я продовжував стрибати з циклів у своїх старих базових програмах і помітив, як мій TI-84 + стає повільнішим і повільнішим ...
Промінь

2
Так, більшість із нас знає почуття;) @RayKoopa
Timtech

13
+1 для Ti Basic. Більшу частину свого 9-го класу я провів, програмуючи ці речі.
markasoftware

Вам потрібно Pause в кінці? Ви можете зберегти 2 байти.
kamoroso94

@ kamoroso94 Я думаю, що так, оскільки "Якщо програма закінчилася, витік очищається і більше не спричинятиме проблем", тож зупинити програму не потрібно.
Timtech

32

Python <3.6.5, 23 байти

property([]).__init__()

property.__init__Витоку посилання на майно СТАРИЙ fget, fset, fdelі __doc__якщо ви називаєте його на вже инициализирован propertyінстанції. Це помилка, зрештою повідомляється як частина випуску 31787 CPython та виправлена ​​в Python 3.6.5 та Python 3.7.0 . (Також так, property([])це річ, яку ви можете зробити.)


Надіслано звіт про помилку?
mbomb007


27

C #, 34 байти

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

Це рішення не вимагає купи. Просто потрібен справжній працьовитий GC ( Garbage Collector ).

По суті, це перетворює ГК у власного ворога.

Пояснення

Щоразу, коли викликається деструктор , він створює нові екземпляри цього злого класу до тих пір, поки не закінчиться час та скаже GC просто скинути цей об'єкт, не чекаючи, поки деструктор закінчиться. На той час було створено тисячі нових примірників.

"Злості" в цьому є, чим важче працює ГК, тим більше це підірветься тобі в обличчя.

Відмова : Ваш GC може бути розумнішим, ніж мій. Інші обставини програми можуть призвести до того, що GC ігнорує перший об’єкт або його деструктор. У цих випадках це не вибухне. Але в багатьох варіаціях це буде . Додавання декількох байтів тут і там може забезпечити витік для всіх можливих обставин. Ну, крім вимикача живлення, можливо.

Тест

Ось тест люкс :

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 хвилин:

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

Це 2 684 476 624 байти. Загальний WorkingSetобсяг процесу становив близько 4,8 ГБ

Ця відповідь була натхнена чудовою статтею Еріка Ліпперта: Коли все, що ти знаєш, не так .


Це захоплююче. Чи збирач сміття «забуде», що деякі речі існують, і через це втратити їх? Я не знаю багато про c #. Також зараз мені цікаво, яка різниця між бомбою і витоком? Я думаю, подібне фіаско можна створити, зателефонувавши до конструктора зсередини конструктора, або мати нескінченну повторювану функцію, яка ніколи не припиняється, хоча технічно система ніколи не втрачає слідів цих посилань, просто не вистачає місця ...
не яскравий

1
Конструктор всередині конструктора може викликати переповнення стека. Але деструктор екземпляра викликається в плоскій ієрархії. GC насправді ніколи не втрачає слідів об'єктів. Щоразу, коли він намагається їх знищити, він мимоволі створює нові об'єкти. З іншого боку, код користувача не має доступу до зазначених об'єктів. Також зазначені невідповідності можуть виникнути, оскільки GC може вирішити знищити об'єкт, не викликаючи його руйнівника.
MrPaulch

Чи не буде виклик завершеним лише використанням class L{~L(){new L();}}? AFAIK - for(;;)лише те, що вона швидше просочує пам'ять, правда?
BgrWorker

1
На жаль, ні. Оскільки для кожного знищеного об’єкта буде створений лише один новий екземпляр, який потім знову є недоступним і позначений для знищення. Повторіть. Тільки коли-небудь один об’єкт буде чекати на знищення. Не збільшується чисельність населення.
MrPaulch

2
Не зовсім. Врешті-решт один доопрацьований буде проігнорований. Відповідний предмет буде з'їдено незалежно.
MrPaulch

26

C (gcc) , 15 байт

f(){malloc(1);}

Перевірка

$ 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 байт

Гольф

setInterval(0)

Реєструє порожній обробник інтервалу із затримкою за замовчуванням, відкидаючи отриманий ідентифікатор таймера (унеможливлюючи його скасування).

введіть тут опис зображення

Я використовував інтервал, що не використовується за замовчуванням, щоб створити кілька мільйонів таймерів, щоб проілюструвати витік, оскільки використання інтервалу за замовчуванням їсть процесор, як божевільний.


5
Ха-ха, я люблю те, що ти набрав "Гольф", викликає цікавість з приводу версії, яка не перебуває у вовку
Martijn

9
це може виглядати if(window && window.setInterval && typeof window.setInterval === 'function') { window.setInterval(0); }
приблизно так

3
Насправді скасувати це неможливо: ідентифікатори інтервалу (та таймауту) нумеруються послідовно, тому скасувати річ досить просто, зателефонувавши clearIntervalіз збільшенням ідентифікатора, поки не пропаде ваш інтервал. Наприклад:for(let i=0;i<1e5;i++){try{clearInterval(i);}catch(ex){}}
user2428118

5
@ user2428118 Як каже zeppelin, це не більше "законного закону", ніж те, що сказати, що витоки C / C ++ не є "справжніми", тому що ви можете жорстоко free()
викликати

1
Нічого не так багато, коли JavaScript фактичний конкурент ...
Джаред Сміт,

19

Java, 10 байт

Нарешті, конкурентна відповідь на Java!

Гольф

". "::trim

Це посилання на метод (проти постійної константи), яке можна використовувати так:

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

Буквальний рядок ". "буде автоматично доданий до глобального пулу інтернованих рядків, як підтримується java.lang.Stringкласом, і, як ми негайно його обріжемо, посилання на нього не можна повторно використовувати в коді (якщо ви знову не оголосите точно той самий рядок).

...

Пул рядків, спочатку порожній, підтримується приватно класом String.

Усі буквальні рядки та постійні вирази, що оцінюються за рядком, інтерновані. Лінійні рядки визначені в розділі 3.10.5 Специфікації мови Java ™.

...

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

Ви можете перетворити це на витік пам’яті «виробничого класу», додавши рядок до себе, а потім явно викликаючи метод intern () у циклі.


2
Я вважав це для C # ..., але я не думаю, що він рахується, тому що, як ви кажете, ви можете отримати доступ до цієї пам'яті, включивши інший рядковий літерал. Мені б також цікаво дізнатися, що ("." + " ").intern()робити (якщо вони були б введеннями користувачів або ж / е, тому ми знижуємо оптимізацію компілятора).
VisualMelon

1
Дійсно, єдиний консенсус у кращому випадку тонкий, я просто твердо сторона "код повинен скласти". Я все ще не впевнений, що купую це рішення, враховуючи формулювання цього питання (ці рядки не можна звільнити, і їх можна знайти в звичайному операційному коді, хоча це малоймовірно), але я закликаю інших зробити власне судження
VisualMelon

3
Ця струна не є навіть недоступною , не кажучи вже протікає. Ми можемо отримати його в будь-який час, інтернувавши рівний рядок. Якби це рахувати, будь-яка невикористана глобальна змінна (приватна статична в Java) була б витоком. Ось так не визначаються витоки пам'яті.
user2357112

3
@ user2357112 "... Цей рядок не є навіть недоступним ..." Це виглядає очевидно лише тому, що ви бачите код. Тепер розгляньте, що ви отримали цей метод посилання X () як аргумент свого коду, ви знаєте, що він виділяє (та інтернів) рядковий літерал всередині, але ви не знаєте, який саме, це може бути "." Або "123" або будь-який інший рядок (загалом) невідомої довжини. Чи можете ви продемонструвати, як ви все ще можете отримати доступ до нього, або розмістити запис у пулі "стажистів", який він займає?
зеппелін

2
@ user2357112 На машині з обмеженою пам'яттю ви можете отримати доступ до значення, збереженого в будь-якому фрагменті пам'яті simply by guessing it correctly, але це не означає, що такого поняття, як витоки пам'яті, не існує. there's probably some way to use reflection to determine the string's contents tooви могли б це продемонструвати? (підказка, String.intern () реалізований у рідному коді).
zeppelin

17

Іржа, 52 байти

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

Виділяє кілька байтів за допомогою системи malloc. Це передбачає, що неправильний ABI є прийнятним.


Іржа (теоретично), 38 байт

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

Ми виділяємо пам’ять на купу, витягуємо необроблений вказівник, а потім просто ігноруємо її, ефективно протікаючи. ( Box::into_rawтоді коротше std::mem::forget).

Однак за замовчуванням Руст використовує jemalloc, який valgrind не може виявити витоку . Ми можемо перейти на системний розподільник, але це додає 50 байт і вимагає щоночі. Дякую за безпеку пам’яті.


Вихід першої програми:

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

приголомшливий такі посади змусили мене вивчити Іржу за останній рік, безумовно, одну з найсмішніших мов, яку я намагався вивчити.
нехай яскравий

16

8086 ASM, 3 байти

Цей приклад передбачає, що час виконання C пов'язаний.

jmp _malloc

це збирається, e9 XX XXде XX XXє відносна адреса_malloc

Це викликає mallocвиділення непередбачуваного обсягу пам'яті, а потім негайно повертається, припиняючи процеси. У деяких операційних системах, таких як DOS, пам'ять взагалі не може бути відновлена, поки система не перезавантажиться!


Нормальна реалізація malloc призведе до звільнення пам'яті при виході з процесу.
Джошуа

@Joshua Так, але це поведінка, визначена реалізацією.
FUZxxl

12

По-четверте, 6 байт

Гольф

s" " *

Виділяє порожній рядок з s" ", залишаючи його адресу та довжину (0) на стеці, а потім помножує їх (у результаті чого втрачається адреса пам'яті).

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 байт

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

це створює анонімну гіропрограму з нескінченною петлею всередині неї. програма може продовжувати працювати як звичайно, так як запуск goutut подібний до нерестування одночасно працюючого невеликого потоку, але програма не має можливості відновити пам'ять, виділену для goroutine. збирач сміття ніколи не збирає його, оскільки він все ще працює. деякі люди називають це "протіканням goroutine"


перевірка гольфу: це на 2 байти коротше, ніж дзвінок C.malloc(8), оскільки вам потрібноimport"C"
Riking

9

Java 1.3, 23 байти

void l(){new Thread();}

Створення потоку, але не запуск його. Нитка зареєстрована у внутрішньому пулі потоків, але ніколи не буде запущена, тому ніколи не закінчувалася і тому ніколи не бути кандидатом у GC. Це непоправний предмет, застряг у кінцівках Java.

Помилка в Java, поки не включено 1.3, як було виправлено згодом.

Тестування

Наступна програма гарантує забруднення пам’яті новими об’єктами потоку і показує зменшення вільного простору пам’яті. Заради тестування на витоки я інтенсивно змушує GC працювати.

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

Оскільки це працює лише для певних версій Java, вам слід сказати "Java 3" у своєму заголовку.

5
Немає такої речі, як Java 3. Це Java 1.3. Була Java 1.0, 1.1, 2, 1.3, 1.4, 5, 6, 7, 8, 9. Дивна нумерація, але це так.
Олів'є Грегоар

Це теж була моя ідея. Блін.
Чарівний восьминога Урна

8

Befunge ( гриби ), 1 байт

$

Це може бути залежно від платформи та залежної від версії (я тестував лише версію 1.0.4 в Windows), але гриби історично дуже інтерпретувались. Команда $(drop) не повинна нічого робити на порожньому стеку, але перебираючи цей код якось вдається витікати багато пам'яті дуже швидко. За лічені секунди він витратив пару концертів і вийде з ладу з помилкою "поза пам'яттю".

Зауважте, що це не обов'язково має бути $командою - майже все, що можна було б зробити. Він не працюватиме з пустим вихідним файлом. Має бути хоча б одна операція.


8

Швидкий 3, 38 байт

Нова версія:

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

x має чітке посилання на себе, тому його не буде розміщувати, що призводить до витоку пам'яті.

Стара версія:

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

xмістить чітке посилання на y, і навпаки. Таким чином, жоден з них не буде розміщений, що призведе до витоку пам'яті.


Хм, ви можете посилатися на цю пам'ять через xта y, таким чином , це не виглядає як витік мені (якщо ви не знищити їх як - то).
зеппелін

@zeppelin Останній рядок міг бути загорнутий у функцію, щоб виправити це
NobodyNada

@NobodyNada, якби я поставив останній рядок у doблок, який би вирішив проблему цеппелін, підняту правильно?
Даніель

@Dopapp Так; a також doбуде працювати. Гарна ідея!
НіхтоНада

Це можна скоротити, вам не потрібні два заняття - X може посилатися на себе:class X{var x: X!};do{let x=X();x.x=x}
Себастьян Осінський

7

Delphi (Object Pascal) - 33 байти

Створення об'єкта без змінної повної консольної програми:

program;begin TObject.Create;end.

Увімкнення FastMM4 в проекті покаже витік пам'яті:

введіть тут опис зображення


6

C # - 84 байт

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

Це виділяє рівно 1 байт некерованої пам'яті, а потім втрачає те IntPtr, що, на мою думку, є єдиним способом отримати або звільнити його. Ви можете перевірити його, заповнивши його в циклі і чекаючи, коли додаток завершиться (можливо, потрібно додати кілька нулів, щоб прискорити роботу).

Я вважав System.IO.File.Create("a");і таке, але я не впевнений , що це обов'язково витоку пам'яті, так як сам додаток буде збирати пам'ять, це ОС під які може просочитися (бо Closeчи Disposeне називалося). Для доступу до файлів також потрібні дозволи файлової системи, і ніхто не хоче покладатися на них. І виявляється, це все одно не просочиться, тому що ніщо не зупиняє виклик фіналізатора (що дозволяє звільнити основні ресурси), що в рамки входить для зменшення подібних помилок судження (певною мірою), і плутати програмістів із начебто недетермінованим блокуванням файлів (якщо ви цинік). Дякую Джону Ханну за те, що він мене прямо поставив до цього.

Я розчарований, що не можу знайти коротший шлях. .NET GC працює, я не можу придумати жодного IDisposablesв mscorlib, який безумовно просочиться (і, справді, всі вони мають фіналізатори, як дратує) , я не знаю жодного іншого способу виділення некерованої пам'яті (за винятком PInvoke ), і відображення забезпечує що-небудь з посиланням на нього (незалежно від мовної семантики (наприклад, приватні члени або класи, що не мають приналежності)) можна знайти.


1
System.IO.File.Create("a")нічого не просочиться, але GC.SuppressFinalize(System.IO.File.Create("a"))буде, оскільки явно просять не запускати фіналізатор FileStreamзробленого.
Джон Ханна

@JonHanna цілком прав. Моя паразита, яка не використовується, начебто покращила мене.
VisualMelon

Можливо, у вас є шанс зробити GDI + витік за допомогою System.Drawing.Bitmap. Не знаю, чи зараховується це, тому що це бібліотека Windows, що викликає протікання, а не сама програма.
BgrWorker

@BgrWorker вони, без сумніву, також мають фіналізатор, і я схильний уникати зовнішніх бібліотек у коді-гольф, тому що я не згоден з консенсусом щодо їх вартості: якщо ви зможете знайти спосіб, в який ви впевнені, не соромтеся публікувати це у власній відповіді!
VisualMelon

<!-- language: lang-c# -->Дякую за цю & приємну відповідь! (Це C #, тому я люблю це)
Metoniem

5

Фактор , 13 байт

Фактор має автоматичне управління пам'яттю, але також надає доступ до деяких функцій libc:

1 malloc drop

Вручну виділяє 1 байт пам’яті, повертає адресу та скидає її.

malloc насправді реєструє копію, щоб відслідковувати витоки пам’яті та подвійне звільнення, але виявити витік, який ви просочилися, непросте завдання.

Якщо ви хочете переконатися, що ви справді втрачаєте цю посилання:

1 (malloc) drop

Тестування витоків за допомогою [ 1 malloc drop ] leaks.говорить:

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

Тестування витоків за допомогою [ 1 (malloc) drop ] leaks.говорить:

| Disposable class | Instances | |

О ні! Поганий фактор, зараз у нього Альцгеймера! D:


5

Лист звичайний (тільки SBCL), 28 26 байт

sb-alien::(make-alien int)

Ви запускаєте його так sbcl --eval 'sb-alien::(make-alien int)':; нічого не друкується і не повертається, але розподіл пам'яті відбувається. Якщо я оберну форму всередині a (print ...), вказівник відображається у відповіді.

  1. package::(form)- це спеціальне позначення в SBCL для тимчасового прив'язування поточного пакету під час читання форми. Це використовується тут, щоб уникнути префіксів як make-alienі intз sb-alien. Я думаю, що було б обманювати припущення, що поточний пакет встановлений на цей, тому що це не так у випадку запуску.

  2. make-alien виділяє пам'ять для заданого типу та необов'язковий розмір (використовуючи malloc).

  3. Виконуючи це в REPL, додайте 0після виділення так, щоб REPL не повертав покажчик, а замість цього значення. В іншому разі не, що б не бути реальною витоком , тому що реплєї запам'ятовує останні три повертаються значень (див *, **,*** ) , і ми могли б все ще є шанс , щоб звільнити виділену пам'ять.

2 байти видалено завдяки PrzemysławP, дякую!


1
Хіба ви не можете використовувати 1(або 2, 3і т.д.) , замість того , ()щоб ви повернути значення 1? Це дозволило б заощадити 1 байт. Чи відповідає ця відповідь лише ПОВТОРЕННЯ? Можливо, якщо ви завантажуєте код з собою, loadви не можете включити ()або що-небудь в кінці, тому що він все одно не буде доступний?
ПеремиславP

1
@ PrzemysławP Ви маєте рацію в обох питаннях, я намагався, evalі це працює, як ви сказали. Дуже дякую!
coredump

4

AutoIt , 39 байт

#include<Memory.au3>
_MemGlobalAlloc(1)

Виділяє один байт з купи. Оскільки ручка, яку повернув, _MemGlobalAllocвідкидається, немає можливості явно звільнити це виділення.


3

C ++, 16 байт

main(){new int;}

Я не маю valgrind, щоб перевірити його протікання, але майже впевнений, що він повинен. Інакше я б спробував:

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

Результат Valgrind

(Це дійсно протікає)

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

@WheatWizard я використовую g++ 4.3.2(не найновіший), і він збирається чудово. Не intдумаю, що тип повернення за замовчуванням. З -Wallмене є попередження , хоча:plop.cpp:1: warning: ISO C++ forbids declaration of 'main' with no type
matovitch

2
@WheatWizard Вибачте, я щойно бачив, що ви дали приклад c ++, щоб розпочати змагання. Походячи з reddit, я лише подивився на відповіді і (як не дивно) не побачив жодного C ++. Я почуваюся трохи німим. : /
matovitch

Консенсус полягає в тому, що ви можете вважати лише []{new int;}функцію C ++ (у виклику не було вказано всієї програми).
Toby Speight

3

Java (OpenJDK 9) , 322 220 байт

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

Спробуйте в Інтернеті!

Це інша витік пам'яті, яка не використовує кеш-пам'ять рядків. Він виділяє половину оперативної пам’яті, і ви нічого не можете з цим зробити.

Завдяки zeppelin за збереження всіх байтів


Ви можете заощадити купу байтів, отримавши Unsafeпримірник з статичної змінної всередині нього, як то:import sun.misc.*;class M{static void main(String[]a)throws Exception{java.lang.reflect.Field f=Unsafe.class.getDeclaredField("theUnsafe");f.setAccessible(1<2);((Unsafe)f.get(1)).allocateMemory(1);}}
дирижабль

І ви можете заощадити ще трохи, замінивши public static void mainстатичний ініціалізатор static{try{}catch(Exception e){}}(який може бути трохи складніше запускати, але він все-таки дійсний і компілюється).
зеппелін

yhea використання конструктора було використано у версії Android коду, який я використовував. Я
змінитиму

Видаліть пробіли, використовуйте aзамість них argsта видаліть загальнодоступні. tio.run/nexus/…
Павло

true може бути замінено на 1> 0
masterX244

3

c, 9 байт

main(){}

Доказ:

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
Ти насправді не просочуєш пам’ять; gccє. Це також повинно працювати з порожньою програмою. Спробуйте gcc src.c && valgrind ./a.out, що має дати чистий результат.

3

C #, 109 байт

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

Ми знайшли ідею цього витоку у виробничому коді, і вивчення його призводить до цієї статті. Основна проблема полягає в цій довгій цитаті статті (читайте її для отримання додаткової інформації):

Шукаючи цей код для PurchaseOrder, я знаходжу цей рядок коду на page_loadодній зі своїх сторінокXmlSerializer serializer = new XmlSerializer(typeof(PurchaseOrder), new XmlRootAttribute(“”));

Це здавалося б досить невинним фрагментом коду. Ми створюємо XMLSerializerдля PurchaseOrder. Але що відбувається під прикриттями?

Якщо ми подивимось на XmlSerializer конструктор за допомогою Reflector, то виявимо, що він викликає, this.tempAssembly = XmlSerializer.GenerateTempAssembly(this.mapping, type, defaultNamespace, location, evidence);що генерує тимчасову (динамічну) збірку. Тому кожен раз, коли цей код запускається (тобто кожного разу, коли сторінка потрапляє), він генерує нову збірку.

Причина, по якій він створює збірку, полягає в тому, що йому потрібно генерувати функції для серіалізації та десеріалізації, і вони повинні десь проживати.

Гаразд, добре ... це створює збірку, і що? Коли ми з цим закінчимось, воно просто повинно зникнути, правда?

Ну ... збірка не є об'єктом на GC Heap, GC насправді не знає про складання, тому не збирає сміття. Єдиний спосіб позбутися від збірок в 1.0 і 1.1 - це розвантажити домен додатка, в якому він знаходиться.

І в цьому полягає проблема доктора Ватсона.

Запуск від компілятора в Visual Studio 2015 та використання вікна діагностичних інструментів показує наступні результати приблизно через 38 секунд. Зверніть увагу, що Пам'ять процесів постійно піднімається, і збирач сміття (GC) продовжує працювати, але нічого не може зібрати.

Вікно діагностичних інструментів


2

C 30 байт

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

Результати 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
Чи можна замість цього просто зробити main(){malloc(1);}?
kirbyfan64sos

@ Так, це! Але його вже розміщено!
Абель Том

2

Дарт, 76 байт

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

Трохи як відповідь JavaScript. Під час виклику .listenоб'єкта потоку Dart вам повертається StreamSubscription, який дозволяє відключитись від потоку. Однак якщо ви викинете це, ви ніколи не зможете скасувати підписку на потік, спричинивши витік. Єдиний спосіб усунення витоку полягає в тому, якщо сам Потік збирається, але на нього все ще посилається всередині комбінація StreamController + Timer.

На жаль, Дарт занадто розумний для інших речей, які я спробував. ()async=>await new Completer().futureне працює, тому що використання await - це те саме, що робити new Completer().future.then(<continuation>), що дозволяє знищити саме закриття, другий Комплект не посилається (Completer має посилання на Майбутнє з.future , Future має посилання на продовження як на закриття).

Крім того, ізоляти (ака-нитки) очищуються GC, тому нереститися в новій нитці та негайно призупинити її ( import'dart:isolate';main(_)=>Isolate.spawn(main,0,paused:true);) не працює. Навіть нерест Ізоляту з нескінченним циклом ( import'dart:isolate';f(_){while(true){print('x');}}main()=>Isolate.spawn(f,0);) вбиває Ізолят і виходить з програми.

Ну добре.


якщо ваша основна програма продовжує працювати та виконувати інші речі, чи зможе збирач сміття колись зупинити ізоляцію? я запитую, тому що мій приклад goroutine звучить схоже ... Я припускав, що те, що програма виходить з програми і повертає всю оперативну пам'ять в операційну систему, не обов'язково означає, що вона не просочилася.
нехай яскравий

2

Швидкий, 12 байт

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

Пояснення:

Це фактичне витоку пам’яті, яке може статися на будь-якій мові, незалежно від того, чи мова використовує ручне управління пам’яттю, автоматизований підрахунок довідок (ARC, як Swift), або навіть широке збирання сміття.

[3,5]це просто буквальний масив. Цей масив виділяє достатню кількість пам'яті принаймні для цих 2 елементів. 3І 5просто довільно.

Підписка (індексація) a Array<T>виробляє ArraySlice<T>. Огляд ArraySlice<T>- це пам'ять масиву, з якого він створений.

[3,5][0...0]виробляє ArraySlice<Int>, значення якого [3]. Зауважте, що 3цей фрагмент є тим самим 3елементом, що і 3в оригіналі, Arrayпоказаному вище, а не копія.

Отриманий фрагмент може бути збережений у змінній та використаний. На початковий масив більше не посилається, тому ви можете подумати, що він може бути розміщений. Однак не може.

Оскільки фрагмент відкриває вид на пам'ять масиву, з якого він вийшов, оригінальний масив повинен залишатися живим до тих пір, поки фрагмент живе. Таким чином, з оригінальних 2розмірів елементів, що мають значення пам'яті, які були виділені, використовується лише перша пам'ять розміру елемента пам'яті, а інша потрібна для існування, щоб не виділяти першу. Другий елемент пам’яті розміру елементарний.

Рішення цієї проблеми полягає в тому, щоб довго не тримати в живих невеликі шматочки великих масивів. Якщо вам потрібно зберегти вміст фрагмента, тоді просувайте його до масиву, який запустить копіювання пам'яті, тим самим видаливши залежність від пам'яті вихідного масиву:

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

2

Рішення 1: C (Mac OS X x86_64), 109 байт

Джерело для golf_sol1.c

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

Вищевказану програму потрібно скласти з доступом до виконання на сегменті __DATA.

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

Потім для виконання програми запустіть наступне:

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

Результати:

На жаль, Valgrind не спостерігає за пам'яттю, виділеною з системних дзвінків, тому я не можу показати приємну виявлену витік.

Однак ми можемо подивитися на vmmap, щоб побачити великий фрагмент виділеної пам'яті (метаданих MALLOC).

                                VIRTUAL   REGION 
REGION TYPE                        SIZE    COUNT (non-coalesced) 
===========                     =======  ======= 
Kernel Alloc Once                    4K        2 
MALLOC guard page                   16K        4 
MALLOC metadata                   16.2M        7 
MALLOC_SMALL                      8192K        2         see MALLOC ZONE table below
MALLOC_TINY                       1024K        2         see MALLOC ZONE table below
STACK GUARD                       56.0M        2 
Stack                             8192K        3 
VM_ALLOCATE (reserved)             520K        3         reserved VM address space (unallocated)
__DATA                             684K       42 
__LINKEDIT                        70.8M        4 
__TEXT                            5960K       44 
shared memory                        8K        3 
===========                     =======  ======= 
TOTAL                            167.0M      106 
TOTAL, minus reserved VM space   166.5M      106 

Пояснення

Тому я думаю, що мені потрібно описати, що відбувається насправді, перш ніж перейти до вдосконаленого рішення.

Ця основна функція - це зловживання декларацією про тип C, що відсутня (тому вона за замовчуванням застосовується до int, без того, щоб ми витрачали її на символи), а також як символи працюють. Лінкер дбає лише про те, чи може він знайти символ, покликаний mainзателефонувати. Отже, тут ми робимо основний масив int, який ми ініціалізуємо з нашим кодом shell, який буде виконуватися. Через це основний буде доданий не до сегмента __TEXT, а до сегмента __DATA, тому нам потрібно скомпілювати програму з виконуваним сегментом __DATA.

Шлакод, знайдений в основному, такий:

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

Це робиться - викликати функцію syscall для виділення сторінки пам'яті (syscall mach_vm_allocate використовує всередині). RAX повинен дорівнювати 0x100000a (повідомляє syscall, яку функцію ми хочемо), тоді як RDI утримує ціль для розподілу (в нашому випадку ми хочемо, щоб це було mach_task_self ()), RSI повинен містити адресу, щоб записати покажчик на щойно створену пам'ять (тому ми просто вказуємо на розділ на стеку), RDX містить розмір виділення (ми просто передаємо в RAX або 0x100000a просто для економії на байтах), R10 тримає прапори (ми вказуємо, що це може виділяються куди завгодно).

Тепер не зовсім очевидно, звідки отримують свої значення RAX та RDI. Ми знаємо, що RAX має бути 0x100000a, а RDI має бути значенням Mach_task_self (). На щастя, mach_task_self () - це фактично макрос для змінної (mach_task_self_), яка є однією і тією ж адресою пам'яті (повинна змінюватися при перезавантаженні). У моєму конкретному випадку екземпляр mach_task_self_ розташований у 0x00007fff7d578244. Отже, щоб скоротити інструкції, ми замість цього передамо ці дані з argv. Ось чому ми запускаємо програму з цим виразом$(ruby -e 'puts "\xf5\xff\xff\xfe\xff\xff\x44\x82\x57\x7d\xff\x7f"')для першого аргументу. Рядок - це два значення разом, де значення RAX (0x100000a) становить лише 32 біти і до нього застосовано доповнення (тому немає нульових байтів; ми просто НЕ значення для отримання оригіналу), наступне значення - RDI (0x00007fff7d578244), який змістили вліво, додавши до кінця 2 зайвих байта небажаного (знову ж таки, щоб виключити нульові байти, ми просто змістимо його назад вправо, щоб повернути його до оригіналу).

Після системного виклику ми записуємо до нашої щойно виділеної пам'яті. Причиною цього є те, що пам'ять, що виділяється за допомогою mach_vm_allocate (або цього системного виклику), є насправді сторінками VM, і вони автоматично не передаються в пам'ять. Швидше вони зарезервовані до тих пір, поки дані не будуть записані до них, а потім ці сторінки не відобразяться в пам'ять. Не була впевнена, чи відповідає вона вимогам, якщо вона лише зарезервована.

Для наступного рішення ми скористаємося тим, що наш код коду не має нульових байтів, і тому можемо перемістити його поза кодом нашої програми для зменшення розміру.

Рішення 2: C (Mac OS X x86_64), 44 байти

Джерело для golf_sol2.c

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

Вищевказану програму потрібно скласти з доступом до виконання на сегменті __DATA.

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

Потім для виконання програми запустіть наступне:

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

Результат повинен бути таким же, як і раніше, оскільки ми робимо виділення такого ж розміру.

Пояснення

Дотримується майже тієї ж концепції, що і рішення 1, за винятком того, що ми перемістили фрагмент коду, що витікає, поза програмою.

Шелкод, знайдений в основному, тепер такий:

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

Це в основному копіює код оболонки, який ми передаємо в argv, після цього коду (тому після його копіювання він запустить вставлений оболонку). На нашу користь - те, що сегмент __DATA матиме принаймні розмір сторінки, тож навіть якщо наш код не такий великий, ми все одно можемо «сміливо» писати більше. Мінус є ідеальним рішенням тут, навіть копія не потребуватиме, замість цього вона буде просто зателефонувати та виконати shellcode в argv безпосередньо. Але, на жаль, ця пам’ять не має прав на виконання. Ми могли б змінити права на цю пам’ять, однак для цього знадобиться більше коду, ніж просто копіювати його. Альтернативною стратегією було б зміна прав із зовнішньої програми (але про це пізніше).

Код оболонки, який ми передаємо argv, такий:

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

Це майже те саме, що і наш попередній код, лише різниця полягає в тому, що ми включаємо значення для EAX та RDI безпосередньо.

Можливе рішення 1: C (Mac OS X x86_64), 11 байт

Ідея змінити програму зовнішньо, дає нам можливе рішення про переміщення виводчика до зовнішньої програми. Якщо наша фактична програма (подання) - це лише фіктивна програма, і програма витікання виділить деяку пам’ять у нашій цільовій програмі. Зараз я не був впевнений, чи буде це підпадати під правила цього виклику, але все-таки поділитися ним.

Отже, якщо ми маємо використовувати mach_vm_allocate у зовнішній програмі з цільовим завданням для нашої викликової програми, це може означати, що нашій програмі-виклику потрібно буде лише щось, що відповідає:

main=65259;

Якщо цей shellcode - це просто короткий стрибок до себе (нескінченний стрибок / цикл), тому програма залишається відкритою, і ми можемо посилатися на неї із зовнішньої програми.

Можливе рішення 2: C (Mac OS X x86_64), 8 байт

Досить дивно, коли я дивився на вихід вальгринд, я побачив, що, принаймні, згідно з вальгрингом, руйнів протікає пам'ять. Тож ефективно кожна програма просочує деяку пам’ять. В такому випадку ми насправді можемо просто зробити програму, яка нічого не робить (просто виходить), і це насправді просочується пам'ять.

Джерело:

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

Звичайна англійська , 71 70 58 35 байт

Видалено 1 байт, видаливши порожній рядок. Вилучено 12 байт, усунувши визначення типу "bogon" та використовуючи батьківський тип "річ" замість підтипу "bogon". Вилучено 23 байти шляхом переходу з повної програми, просто на звичайну програму, яка просочує пам'ять.

Версія для гольфу:

To x:
Allocate memory for a thing.

Версія Ungolfed, яка є повноцінною програмою, використовує визначення підтипу і не просочує пам'ять:

A bogon is a thing.

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

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

Якщо буде викликана гольф-версія "x", вона просочить пам'ять пропорційно кількості викликів "x". У версії для гольфу "Розмістіть річ". виправить витік пам'яті.

Звичайна англійська перевірка на предмет витоку пам’яті за замовчуванням. Коли запущена версія, що протікає в пам'яті, перед діалоговим вікном з’явиться діалогове вікно перед тим, як програма вимкнеться. Діалогове вікно має назву "налагодження", повідомлення "1 крапелька" та кнопку "ОК". Чим більше разів викликається функція протікання, тим більша кількість "капає" в повідомленні. Коли запущена версія, що не протікає пам'ять, діалогове вікно не з’являється.

У звичайній англійській мові "річ" - це вказівник на предмет, що перебуває у подвійному зв'язку. "Річ", "запустити" та "відключити" визначаються в модулі під назвою "локшина", який потрібно скопіювати (як правило, як окремий файл) у кожен проект. "A", "the", "to", "to виділити пам'ять", і "знищити" визначені в компіляторі.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.