Jaki jest najkrótszy kod powodujący przepełnienie stosu? [Zamknięte]


160

Aby upamiętnić publiczne uruchomienie Stack Overflow, jaki jest najkrótszy kod powodujący przepełnienie stosu? Mile widziane w każdym języku.

ETA: Żeby wyjaśnić to pytanie, ponieważ jestem sporadycznym użytkownikiem Schematu: rekurencja wywołania ogonowego jest tak naprawdę iteracją, a każde rozwiązanie, które można stosunkowo trywialnie przekształcić w rozwiązanie iteracyjne przez przyzwoity kompilator, nie być policzonym. :-P

ETA2: Wybrałem teraz „najlepszą odpowiedź”; zobacz ten post dla uzasadnienia. Dziękujemy wszystkim, którzy wnieśli swój wkład! :-)

Odpowiedzi:


212

Wszystkie te odpowiedzi i brak Befunge? Założę się, że to najkrótsze rozwiązanie ze wszystkich:

1

Nie żartuję. Wypróbuj sam: http://www.quirkster.com/iano/js/befunge.html

EDYCJA: Chyba muszę to wyjaśnić. Operand 1 wypycha 1 na wewnętrzny stos Befunge, a brak czegokolwiek innego umieszcza go w pętli zgodnie z regułami języka.

Korzystając z dostarczonego interpretera, w końcu - i mam na myśli ostatecznie - trafisz w punkt, w którym tablica JavaScript reprezentująca stos Befunge stanie się zbyt duża, aby przeglądarka mogła ją ponownie przydzielić. Gdybyś miał prosty interpreter Befunge z mniejszym i ograniczonym stosem - tak jak w przypadku większości poniższych języków - ten program szybciej spowodowałby bardziej zauważalne przepełnienie.


8
Hmm… ale czy to naprawdę przepełnienie stosu, czy tylko nieskończona pętla? Mój interpreter JS nie przepełnił się, po prostu pojechał na wakacje, że tak powiem.
— Konrad Rudolph

3
Nie jest to nieskończona pętla, ponieważ instrukcja 1 umieszcza 1 na stosie. W końcu interpreterowi Befunge zabraknie miejsca na stosie, ale zajmie to chwilę. :)
— Patrick,

18
Ty ... rozbiłeś moją przeglądarkę i ... wysłałeś mój wentylator procesora na przesterowanie.
— Sam152

2
Niesamowity! Mój komputer lub przeglądarka (Opera) nie uległa awarii, ale oba procesory działały na 100%, a prędkość wentylatora wynosiła 3.
— Secko,

28
Oto program Befunge, który przepełnia się szybciej: " ładuje 79 kopii liczby 32 co dwa razy, a nie 2 kopie liczby 1.
— KirarinSnow


174

Możesz również spróbować tego w C # .net

throw new StackOverflowException();

29
Pedant we mnie mówi, że nie powoduje przepełnienia stosu, po prostu zgłasza wyjątek. To tak, jakby powiedzieć, że najszybszym sposobem, by zostać zaatakowanym przez rekiny, jest stanąć w morzu i krzyczeć „Atak rekina!”. Mimo to zagłosuję za tym. :)
— Bernard,

Cóż - czy jest różnica? Czy możesz to złapać i kontynuować? Czy jest to dokładnie jak przepełnienie stosu w języku C #? W takim razie jest to jakoś przepełnienie stosu, ponieważ jest nie do odróżnienia od jednego ... Jednak - głosuj za wszystkimi powyższymi powodami
— pn.

18
Jeśli stos przepełnia się w lesie i nie ma w pobliżu nikogo do złapania, czy stanowi wyjątek?

Nie powiedziałbym „najkrótszego”, ponieważ to nie jest tak, że można skompilować taki jeden wiersz. To nie rzucać przepełnienie stosu szybko chociaż chyba.
— Dominic K

159

Nemerle :

To powoduje awarię kompilatora z wyjątkiem StackOverflowException:

def o(){[o()]}

119

Mój obecny najlepszy (w zestawie x86) to:

push eax
jmp short $-1

co daje 3 bajty kodu wynikowego ( 50 EB FD). W przypadku kodu 16-bitowego jest to również możliwe:

call $

co również daje 3 bajty ( E8 FD FF).


6
Liczenie bajtów po „kompilacji” (lub asemblacji) nie jest kodowaniem.
— Louis Brandy,

37
Pytanie brzmi: „[...] jaki jest najkrótszy kod powodujący przepełnienie stosu?” Nie określa kodu źródłowego, interpretowanego, maszynowego, wynikowego ani zarządzanego ...
— Anders Sandvig

Dla przypomnienia, serwer golfowy Shina pozwala na wysyłanie kodu wynikowego do oceny, chociaż zlicza również wszystkie nagłówki ELF. Hmm ...
— Chris Jester-Young,

Zobacz np. Golf.shinh.org/p.rb?FizzBuzz#x86, aby zobaczyć kilka przykładów. (Szczerze mówiąc nie wiem, jak ludzie mogą tworzyć 99-bajtowe pliki binarne ELF.) :-P
— Chris Jester-Young

7
@lbrandy: Jest wystarczająco dużo ludzi, którzy mogą bezpośrednio pisać kod obiektowy. Nie mogę tego zrobić dla x86, ale dla pewnego mikroprocesora mogę. Policzyłbym taki kod.
— Joey,

113

PIC18

Odpowiedź PIC18 udzielona przez TK skutkuje następującymi instrukcjami (binarnymi):

overflow
   PUSH
   0000 0000 0000 0101
   CALL overflow
   1110 1100 0000 0000
   0000 0000 0000 0000

Jednak samo CALL wykona przepełnienie stosu:

CALL $
1110 1100 0000 0000
0000 0000 0000 0000

Mniejszy, szybszy PIC18

Ale RCALL (wywołanie względne) jest jeszcze mniejsze (nie pamięć globalna, więc nie ma potrzeby dodatkowych 2 bajtów):

RCALL $
1101 1000 0000 0000

Zatem najmniejsza w PIC18 to pojedyncza instrukcja, 16 bitów (dwa bajty). Zajmie to 2 cykle instrukcji na pętlę. Przy 4 cyklach zegara na cykl instrukcji masz 8 cykli zegara. PIC18 ma 31-poziomowy stos, więc po 32. pętli przepełni stos w 256 cyklach zegara. Przy 64 MHz przepełnienie stosu zajmie 4 mikro sekundy i 2 bajty .

PIC16F5x (jeszcze mniejszy i szybszy)

Jednak seria PIC16F5x wykorzystuje instrukcje 12-bitowe:

CALL $
1001 0000 0000

Ponownie, dwa cykle instrukcji na pętlę, 4 zegary na instrukcję, czyli 8 cykli zegara na pętlę.

Jednak PIC16F5x ma stos dwupoziomowy, więc w trzeciej pętli przepełniłby się w 24 instrukcjach. Przy 20 MHz przepełnienie zajmie 1,2 mikrosekundy i 1,5 bajta .

Intel 4004

Intel 4004 posiada 8-bitowe instrukcje wywołanie podprogramu:

CALL $
0101 0000

Dla ciekawskich, co odpowiada ascii „P”. Z 3-poziomowym stosem, który zajmuje 24 cykle zegara, łącznie 32,4 mikrosekundy i jeden bajt . (Chyba że podkręcisz swój 4004 - daj spokój, wiesz, że chcesz.)

Co jest tak małe jak odpowiedź befunge, ale znacznie, dużo szybsze niż kod befunge działający w obecnych interpreterach.



57

Hoot overflow!

//              v___v
let rec f o = f(o);(o)
//             ['---']
//             -"---"-

55

Każde zadanie wymaga odpowiedniego narzędzia. Poznaj język SO Overflow , zoptymalizowany do tworzenia przepełnień stosu:

so

7
Jeśli tworzysz wyspecjalizowany język do generowania przepełnień z minimalną ilością kodu, oczywiście chciałbyś (1) puste wejście tworzy kod przepełniający stos (prawdopodobnie mały plik binarny, który uruchamia kod natywny wygenerowany z wpisu kodu asemblera) lub (2 ) wszystkie programy wejściowe tworzą wspomniany plik binarny.
— Jared Updike,

Hmmm, Turing nie jest kompletny. Nie wiem, czy można to jeszcze nazwać językiem ...
— Adam Davis,

42

TeX:

\def~{~.}~

Prowadzi do:

! Przekroczono pojemność TeX-a, przepraszam [rozmiar stosu wejściowego = 5000].
~ -> ~
    .
~ -> ~
    .
~ -> ~
    .
~ -> ~
    .
~ -> ~
    .
~ -> ~
    .
...
<*> \ def ~ {~.} ~

Lateks:

\end\end

Prowadzi do:

! Przekroczono pojemność TeX-a, przepraszam [rozmiar stosu wejściowego = 5000].
\ end # 1 -> \ csname end # 1
                      \ endcsname \ @checkend {# 1} \ expandafter \ endgroup \ if @ e ...
<*> \ end \ end

Ponieważ ~jest aktywny, może być używany zamiast \a. I całkowicie przypadkowo odkryłem kod LaTeX. :)
— Josh Lee

35

Asembler Z-80 - w lokalizacji pamięci 0x0000:

rst 00

jeden bajt - 0xC7 - niekończąca się pętla wpychania bieżącego komputera na stos i przeskakiwania do adresu 0x0000.


2
Pamiętam, że pusty eprom to wszystkie 0xffs, które są pierwszymi 7 (= call 0x0038) instrukcjami. Byłoby to przydatne do debugowania sprzętu za pomocą oscyloskopu. Magistrala adresowa przechodziłaby cyklicznie przez przestrzeń 64K, gdy stos wielokrotnie przepełniał, przeplatany odczytami 0xff z 0x0038.
— Bill Forster,


29

Kolejny przykład PHP:

<?
require(__FILE__);

4
Możesz nawet zostać zwarty, pomijając nawias (ale uwzględniając spację zamiast pierwszego).
— Alex

26

A co z następującymi w języku BASIC:

10 GOSUB 10

(Obawiam się, że nie mam tłumacza języka BASIC, więc to przypuszczenie).


3
Nie jest to przepełnienie stosu, ponieważ BASIC jest językiem bez stosu. Nawet VB (który ma stos) nie przepełniłby tego, ponieważ po prostu skacze, a nie tworzy ramkę stosu.
— Daniel Spiewak

21
To jest, a GOSUBnie GOTO. Skoro RETURNpochodzi z tego miejsca, z którego został wywołany, na pewno używa stosu?
— Tom

3
Tak! Zgadzam się. Miałem wiele przepełnień stosu w BASICu w latach 80-tych.
— nickd

6
Uruchomiłem ten w yabasic tylko dla zabawy i prawie wyłączył mój komputer. Dzięki Bogu, Malloc w końcu zawiódł, ale jutro wzywam.
— Adam Rosenfield

2
Ups, przepraszam, Adam ... przypomina mi czasy na uniwersytecie, kiedy ktoś przypadkowo napisał program, który rekursywnie się rozwidlał: wyłączył cały serwer Silicon Graphics.
— stusmith

26

Uwielbiałem mnóstwo odpowiedzi Cody'ego, więc oto mój podobny wkład w C ++:

template <int i>
class Overflow {
    typedef typename Overflow<i + 1>::type type;
};

typedef Overflow<0>::type Kaboom;

W żadnym wypadku nie jest to kod do gry w golfa, ale nadal nic dla przepełnienia meta stosu! :-P


21

Oto mój wkład w C, ważący 18 znaków:

void o(){o();o();}

Jest to o wiele trudniejsze do optymalizacji połączeń końcowych! :-P


4
Nie kompiluje się dla mnie: „undefined reference to„ main ””: P
— Andrew Johnson

1
Nie rozumiem: po co dzwonić o () 2x?
— Dinah

3
@Dinah: Jednym z ograniczeń mojego konkursu było to, że optymalizacja wywołań ogonowych nie liczy się jako rekurencja; to tylko iteracyjna pętla. Jeśli napisałeś o () tylko raz, można to zoptymalizować wywołanie ogonowe do czegoś takiego (przez kompetentny kompilator): "o: jmp o". Przy dwóch wywołaniach o, kompilator musi użyć czegoś takiego jak: „o: call o; jmp o”. Jest to rekurencyjna instrukcja „call”, która powoduje przepełnienie stosu.
— Chris Jester-Young

Masz rację - nie zwróciłem uwagi na tę część. Dziękuję za wyjaśnienie.
— Dinah


17

Javascript

Aby przyciąć kilka dodatkowych znaków i wyrzucić nas z większej liczby sklepów z oprogramowaniem, przejdźmy do:

eval(i='eval(i)');

15

Groovy:

main()

$ groovy stack.groovy:

Caught: java.lang.StackOverflowError
    at stack.main(stack.groovy)
    at stack.run(stack.groovy:1)
 ...

Głosowano, bo to całkiem interesujące. Jednak ujawnia dość irytującą słabość kompilatora Groovy (takie wywołania końcowe mogą być wbudowane w czasie kompilacji).
— Daniel Spiewak,

czy na pewno to rozmowa z ogonem? że wypadnięcie z końca programu nie wywołuje powłoki interpretera?
— Aaron


14
Person JeffAtwood;
Person JoelSpolsky;
JeffAtwood.TalkTo(JoelSpolsky);

Mamy nadzieję na brak rekurencji ogona!


1
Hej, zabawne. Jeśli chodzi o rozmowy, dość ciekawa jest też idea „efektu komory echa”. Niezupełnie powodujące przepełnienie stosu, ale nadal.
— Chris Jester-Young,

8
Czy nie byłby to wyjątek zerowego wskaźnika? Przepraszam, wiem, że to żart.
— jamesh

12

C - To nie jest najkrótsze, ale wolne od rekurencji. Nie jest również przenośny: ulega awarii w systemie Solaris, ale niektóre implementacje przydzielania () mogą zwracać tutaj błąd (lub wywoływać malloc ()). Wywołanie printf () jest konieczne.

#include <stdio.h>
#include <alloca.h>
#include <sys/resource.h>
int main(int argc, char *argv[]) {
    struct rlimit rl = {0};
    getrlimit(RLIMIT_STACK, &rl);
    (void) alloca(rl.rlim_cur);
    printf("Goodbye, world\n");
    return 0;
}

Możesz także po prostu wykonać "ulimit -s16", aby ustawić stos na naprawdę mały. Każde mniejsze niż około 16, a program nawet się nie uruchamia (najwyraźniej niewystarczające argumenty!).
— Andrew Johnson,

11

perl w 12 znakach:

$_=sub{&$_};&$_

bash 10 znakami (ważna jest spacja w funkcji):

i(){ i;};i

11

spróbuj umieścić więcej niż 4 paszteciki na jednym burgerze. przepełnienie stosu.


1
W Nowej Zelandii mamy Burger Wisconsin, gdzie podają duże, ale cienkie paszteciki. Jestem pewien, że jeśli chcesz, możesz ułożyć więcej niż 4 z nich; ale to będzie bardzo drogi burger!
— Chris Jester-Young,



11

Python :

so=lambda:so();so()

Alternatywnie:

def so():so()
so()

A jeśli zoptymalizowane pod Pythonem wywołania tail ...:

o=lambda:map(o,o());o()

Na szczęście dla ciebie Python nie przeprowadza optymalizacji wywołań końcowych; w przeciwnym razie zostanie zdyskwalifikowana, podobnie jak dwie inne dotychczasowe odpowiedzi. :-P
— Chris Jester-Young,

10

Po tym poście wybieram „najlepszą odpowiedź”. Ale najpierw chciałbym podziękować za bardzo oryginalny wkład:

  1. aku. Każdy z nich bada nowy i oryginalny sposób powodowania przepełnienia stosu. Pomysł zrobienia f (x) ⇒ f (f (x)) to ten, który omówię w następnym wpisie poniżej. :-)
  2. Cody'ego, który dał kompilatorowi Nemerle przepełnienie stosu.
  3. I (trochę niechętnie), GateKiller o rzuceniu wyjątku przepełnienia stosu. :-P

Mimo że uwielbiam powyższe, wyzwanie polega na robieniu kodu golfa i aby być uczciwym wobec respondentów, muszę przyznać „najlepszą odpowiedź” najkrótszemu kodowi, czyli wpisowi Befunge; Nie wierzę, że ktokolwiek będzie w stanie to pokonać (chociaż Konrad z pewnością próbował), więc gratuluję Patrickowi!

Widząc dużą liczbę rozwiązań polegających na przepełnieniu stosu przez rekursję, jestem zaskoczony, że nikt (w chwili obecnej) nie przywołał kombinatora Y (zajrzyj do eseju Dicka Gabriela The Why of Y ). Mam rozwiązanie rekurencyjne, które wykorzystuje kombinator Y, a także podejście aku f (f (x)). :-)

((Y (lambda (f) (lambda (x) (f (f x))))) #f)

8

Oto kolejny interesujący ze Scheme:

((lambda (x) (xx)) (lambda (x) (xx)))

Bardzo ładne i jest w tym również dobra symetria. Również użycie formuły (lambda (x) (xx)): ((Y (lambda (x) (xx))) #f) to też świetna zabawa!
— Chris Jester-Young,

Och, to jest ładne. Działa również w Rubim, choć nie tak ładnie jak w Scheme: lambda {| x | x.call x} .call lambda {| x | x.call x}
— Wayne Conrad

7

Jawa

Nieco krótsza wersja rozwiązania Java.

class X{public static void main(String[]a){main(a);}}

5
Lub (taka sama liczba znaków): public static void main (String ... a) {main ();}
— Michael Myers

Lub dla facetów z TDD (ta sama liczba znaków): klasa publiczna ${@org.junit.Test public void $ () {$ ();}}
— mhaller

Wciąż nie jest to najkrótsza (zobacz moją odpowiedź)
— Draemon


5

3 bajty:

label:
  pusha
  jmp label

Aktualizacja

Według (starej?) Dokumentacji Intela (?) Jest to również 3 bajty:

label:
  call label


To 3 bajty w trybie 32-bitowym. Dobra odpowiedź, biorąc pod uwagę, że wypełni ona stos znacznie szybciej niż moja odpowiedź!
— Chris Jester-Young,

Według penguin.cz/~literakl/intel/j.html#JMP , jmp ma 3 bajty z 8, 16 lub 32 bitowym względnym adresem docelowym. Pusha to również 1 bajt, co daje w sumie 4
— Anders Sandvig

Istnieją trzy typy jmp, krótkie, bliskie i dalekie. Krótki jmp (przy użyciu kodu operacji 0xEB) ma dwa bajty. Miejsce docelowe musi znajdować się w odległości od -128 do 127 bajtów od następnej instrukcji. :-)
— Chris Jester-Young,

Może masz rację. Jestem zbyt leniwy, żeby wykopać mojego asemblera i sprawdzić ...;)
— Anders Sandvig
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.