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

Chris Jester-Young
źródło

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.

Patrick
źródło
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
291

Przeczytaj ten wiersz i zrób to, co w nim jest, dwa razy .

jrudolph
źródło
174

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

throw new StackOverflowException();
GateKiller
źródło
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()]}
Cody Brocious
źródł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).

Chris Jester-Young
źródło
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.

Adam Davis
źródło
77

DO#:

public int Foo { get { return Foo; } }
aku
źródło
57

Hoot overflow!

//              v___v
let rec f o = f(o);(o)
//             ['---']
//             -"---"-
Juliet
źródło
55

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

so
Konrad Rudolph
źródło
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
Pi.
źródło
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.

Dennis Munsie
źródło
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

Po angielsku:

recursion = n. See recursion.
Vinko Vrsalovic
źródło
32
Każdy rozsądny ludzki mózg wywoła ogon optymalizując interpretację tego, a nie wysadzając. :-P
Chris Jester-Young,
73
Chris, rozsądne ludzkie mózgi stają się w dzisiejszych czasach rzadkością.
Jason Z
20
rzadkość ... masz na myśli, że istnieją?
Adam Lerman,
11
Rekurencja Google
CodeFusionMobile
29

Kolejny przykład PHP:

<?
require(__FILE__);
disq
źródło
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).

stusmith
źródło
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

Chris Jester-Young
źródło
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

Chris Jester-Young
źródło
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
19

Korzystanie z pliku wsadowego okna o nazwie „s.bat”:

call s
Jude Allred
źródło
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)');
Travis Wilson
źródło
15

Groovy:

main()

$ groovy stack.groovy:

Caught: java.lang.StackOverflowError
    at stack.main(stack.groovy)
    at stack.run(stack.groovy:1)
 ...
Chris Broadfoot
źródło
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
15

Powiedz mi proszę, co oznacza skrót „ GNU ”.

Greg
źródło
4
Lub Eine (Eine to nie Emacs) lub Zwei (Zwei początkowo był Eine). :-P
Chris Jester-Young
Lub YAML, WINE, XNA lub dowolna z pozostałych na en.wikipedia.org/wiki/Recursive_acronym
TM.
Drei (Drei to Really Emacs Incognito), Fier (Fier to Emacs Reinvented) - ok, więc właśnie je wymyśliłem :-)
Ferruccio
14
Person JeffAtwood;
Person JoelSpolsky;
JeffAtwood.TalkTo(JoelSpolsky);

Mamy nadzieję na brak rekurencji ogona!

Ryan Fox
źródło
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;
}
bk1e
źródło
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
asksol
źródło
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()
Cody Brocious
źródł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)
Chris Jester-Young
źródło
8

Oto kolejny interesujący ze Scheme:

((lambda (x) (xx)) (lambda (x) (xx)))
Adam Rosenfield
źródło
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);}}
Misha
źródło
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
6
xor esp, esp
ret
a1k0n
źródło
to naprawdę nie jest przepełnienie stosu, ale jest też fajne
botismarius
5

3 bajty:

label:
  pusha
  jmp label

Aktualizacja

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

label:
  call label

Anders Sandvig
źródło
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