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.