Seltsamster Weg, um einen Stapelüberlauf zu erzeugen [geschlossen]

146

Als Programmierer kennen Sie sicherlich den Fehler eines Stapelüberlaufs aufgrund einer offensichtlichen Rekursion. Aber es gibt sicherlich viele seltsame und ungewöhnliche Möglichkeiten, Ihre Lieblingssprache dazu zu bringen, diesen Fehler auszuspucken.

Ziele:

  1. Muss einen Stapelüberlauf verursachen, der auf der Fehlerausgabe deutlich sichtbar ist.
  2. Darf keine offensichtliche Rekursion verwenden.

Beispiele für ungültige Programme:

// Invalid, direct obvious recursion.
methodA(){ methodA(); }
// Invalid, indirect, but obvious recursion.
methodA(){ methodB(); }
methodB(){ methodA(); }

Die kreativsten Wege sind die besten, da dies ein . Vermeiden Sie langweilige offensichtliche Antworten wie diese:

throw new StackOverflowError(); // Valid, but very boring and downvote-deserving.

Auch wenn ich jetzt eine Antwort akzeptiert habe, ist das Hinzufügen weiterer Antworten immer noch in Ordnung :)

masterX244
quelle
14
Ich neige dazu zu produzieren, indem ich zu stackoverflow.com navigiere, obwohl ich dafür bekannt bin, dass ich in meiner Suchmaschine der Wahl "Stapelüberlauf" abfrage.
OJFord
21
Verwenden Sie den Internet Explorer. Ein sicherer Weg, um einen zu fangen :)
asgoth
64
Der seltsamste Weg, einen Stapelüberlauf zu erzeugen, besteht darin, einen Beliebtheitswettbewerb auf codegolf.stackexchange.com zu veröffentlichen, in dem die Leute aufgefordert werden, den seltsamsten Weg zu veröffentlichen, um einen Stapelüberlauf zu erzeugen. Die Antwortenden erzeugen beim Testen ihrer Lösungen für die Frage einen Stapelüberlauf. Ich habe es jedoch nicht getestet, daher kann ich nicht sicher sein, ob es funktioniert (weshalb ich es nicht als Antwort gepostet habe).
Tim Seguine
3
Ich bin teilweise zu dieser Methode: joelonsoftware.com/items/2008/09/15.html
robert
11
Fahre einen Toyota (Hey, warte mal, mein Auto ist ein Toyota ...)
zimperliches Ossifrage

Antworten:

224

Python

import sys
sys.setrecursionlimit(1)

Dies führt dazu, dass der Interpreter sofort ausfällt:

$ cat test.py
import sys
sys.setrecursionlimit(1)
$ python test.py
Exception RuntimeError: 'maximum recursion depth exceeded' in <function _remove at 0x10e947b18> ignored
Exception RuntimeError: 'maximum recursion depth exceeded' in <function _remove at 0x10e8f6050> ignored
$ 

Anstatt eine Rekursion zu verwenden, wird der Stapel nur verkleinert, sodass er sofort überläuft.

Marinus
quelle
12
Nett, aber nicht ganz das, worauf die ursprüngliche Frage meiner Meinung nach abzielte.
Nit
12
@Nit Ich sehe das Problem nicht. Was ist mit dieser Lösung unbefriedigend?
SimonT
17
@ masterX244 Ja, der springende Punkt der Frage ist "Mach es nicht auf die übliche Weise".
Plutor
24
@Plutor richten Sie normalerweise absichtlich einen StackOverFlow ein?
Kiwy
15
Dies war nur ein bisschen clever, als es zum ersten Mal veröffentlicht wurde .
Primo
189

Python

import webbrowser
webbrowser.open("http://stackoverflow.com/")
Der Arzt
quelle
7
Fantastisch. Schön und elegant.
James Webster
103
Sehr wörtlich. Sehr antworte.
Tim Seguine
47
Nun, dies zeigt einen Stapelüberlauf, aber um einen zu erzeugen , funktioniert dies nur, wenn Jeff Atwood oder Joel Spolsky es ausführen.
Mechanische Schnecke
10
@ TimSeguine Wow.
Sean Allred
9
Keine Antwort, da sie nicht in der Fehlerausgabe enthalten ist.
Pierre Arlaud
131

C / Linux 32bit

void g(void *p){
        void *a[1];
        a[2]=p-5;
}
void f(){
        void *a[1];
        g(a[2]);
}
main(){
        f();
        return 0;
}

Funktioniert durch Überschreiben der Rücksendeadresse. Kehrt also vor dem Anruf gzum Punkt in zurück . Funktioniert auf allen Plattformen, auf denen sich Rücksprungadressen auf dem Stapel befinden, jedoch möglicherweise Optimierungen erforderlich sind.mainf

Natürlich ist das Schreiben außerhalb eines Arrays ein undefiniertes Verhalten , und Sie können nicht garantieren, dass es zu einem Stapelüberlauf kommt, anstatt beispielsweise Ihren Schnurrbart blau zu färben. Details der Plattform-, Compiler- und Compiler-Flags können einen großen Unterschied machen.

ugoren
quelle
1
Wäre das nicht ein Segfehler?
11684
4
+1 für die seltsame Stapelmanipulation und absolut keine Rekursion!
RSFalcon7
6
@ 11684, Es ist undefiniertes Verhalten, daher kann es im Allgemeinen zum Absturz kommen. Unter 32-Bit-Linux (auf dem ich getestet habe) schreibt es außerhalb des Arrays, überschreibt die Rücksprungadresse und stürzt nicht ab, bis der Stapel überläuft.
Ugoren
46
"Malen Sie Ihren Schnurrbart blau" -> Diese Linie hatte mich.
Aneesh Dogra
2
Beeindruckend. Das ist fantastisch verschleiert.
MirroredFate
108

JavaScript / DOM

with (document.body) {
    addEventListener('DOMSubtreeModified', function() {
        appendChild(firstChild);
    }, false);

    title = 'Kill me!';
}

Wenn Sie Ihren Browser beenden möchten, probieren Sie dies in der Konsole aus.

Vision
quelle
53
Ich bin sicher, dass Sie mehr +1 Stimmen verdienen, aber leider haben die Leute dies vor der Abstimmung versucht ... lol
Reactgular
5
Nun, das war effektiv. Ich konnte nicht einmal meinen Task-Manager öffnen, um es zu töten.
Primo
1
Verwenden Sie Chrome, schließen Sie die Registerkarte. Problem gelöst.
Cole Johnson
1
with (document.body) { addEventListener('DOMSubtreeModified', function() { appendChild(firstChild); }, false); title = 'Kill me!'; } 15:43:43.642 TypeError: can't convert undefined to object
Brian Minton
1
Damn My Firefox wurde gehängt
Farhad
91

Java

Habe hier irgendwo so etwas gesehen:

Bearbeiten: Gefunden, wo ich es gesehen habe: Joe Ks Antwort auf das kürzeste Programm, das den StackOverflow-Fehler auslöst

public class A {
    String val;
    public String toString() {
        return val + this;
    }

    public static void main(String[] args) {
        System.out.println(new A());
    }
}

Das kann einige Java-Anfänger verwirren. Der rekursive Aufruf wird einfach ausgeblendet. val + thiswird val + this.toString()weil valist ein String.

Sehen Sie es hier laufen: http://ideone.com/Z0sXiD

Justin
quelle
30
Tatsächlich ist dies der Fall new StringBuilder().append(val).append("").append(this).toString(), und der letzte Anhang ruft String.valueOf (...) auf, wodurch wiederum String aufgerufen wird. Dies macht Ihren Stack-Trace etwas abwechslungsreicher (drei Methoden stehen zur Verfügung).
Paŭlo Ebermann
2
@ PaŭloEbermann Ja, das ist richtig. Es ist jedoch viel einfacher zu sagen, dass es wird "" + this.toString().
Justin
2
Ich denke, das + "" +könnte Leute hinweisen, da es auf den ersten Blick nutzlos aussieht. String val;und return val + this;vielleicht etwas hinterhältiger sein
Cruncher
@Cruncher überhaupt nicht. Wenn Sie ein Java-Codierer sind, wissen Sie, dass die einfache Möglichkeit, ein Int während der ""String-Konstruktion mit einem String zu verknüpfen, mit dem+ ""
Justin
5
In einer realen Anwendung würde ich es vorziehen, unendliche Rekursions- und Stapelüberlauffehler zu vermeiden
jon_darkstar
77

C

Ziemlich leicht:

int main()
{
    int large[10000000] = {0};
    return 0;
}
Shahbaz
quelle
10
+1 für nicht offensichtlich! Trotzdem ist es sehr systemabhängig (ein ulimit -s unlimitedin der Shell löst dies unter Linux)
RSFalcon7
4
@ RSFalcon7, danke für die +1, aber für mich war das eigentlich das offensichtlichste !!
Shahbaz
14
@Cruncher: Es kommt nicht zu einer Rekursion. Das Problem bestand darin, den Stapel zu sprengen. Auf vielen Betriebssystemen hat der Stapel eine feste Größe und ist weitaus kleiner als zehn Millionen Zoll. Dadurch wird der Stapel gesprengt.
Eric Lippert
2
@ HannoBinder, Unter Linux, das ich getestet habe, wird kein Stapelüberlauffehler angezeigt. Sie erhalten einen Segmentierungsfehler, da ein Überlaufen des Stapels zu einem Zugriff auf nicht besessene Segmente führt. Ich bin nicht sicher, ob es überhaupt einen Stapelüberlauffehler in Linux gibt, da ein unendlicher rekursiver Funktionsaufruf auch einen Segmentierungsfehler ausgibt.
Shahbaz
3
~0uist eine ziemlich große Zahl in C.
Vortico
63

Nicht rekursiver Stapelüberlauf in C

Nicht übereinstimmende Konventionen aufrufen.

typedef void __stdcall (* ptr) (int);

void __cdecl hello (int x) { }

void main () {
  ptr goodbye = (ptr)&hello;
  while (1) 
    goodbye(0);
}

Kompilieren mit gcc -O0.

__cdeclFunktionen erwarten, dass der Aufrufer den Stapel __stdcallaufräumt , und erwarten , dass der Angerufene dies tut. Durch Aufrufen des typecast-Funktionszeigers wird die Bereinigung also nie durchgeführt. mainDer Parameter wird bei jedem Aufruf auf den Stapel verschoben, aber es wird kein Popup ausgeführt und schließlich der Stapelfüllungen.

Jason C
quelle
2
schöne Art , den Stapel von Spam: P
masterX244
62

JavaScript

window.toString = String.toLocaleString;
+this;
Ryan Cavanaugh
quelle
5
Dieser wird unterschätzt.
Ry
1
Was macht +thisdas?
NobleUplift
8
Unary + ruft die abstrakte Operation ToNumber auf. Das verwendet die ToPrimitive-Operation mit einem Hinweistyp von Nummer. ToPrimitive für ein Objekt verwendet die interne Methode [[DefaultValue]], die bei Übergabe eines Zahlenhinweises zuerst überprüft, ob die valueOf () -Methode vorhanden ist, und ein Grundelement zurückgibt. window.valueOf () gibt ein Objekt zurück, stattdessen gibt [[DefaultValue]] das Ergebnis des Aufrufs von toString () zurück. Das erste, was window.toString macht (jetzt ist es toLocaleString), ist toString on 'this' aufzurufen. Wiederholen.
Ryan Cavanaugh
3
Wenn Chrome den Fehler "Zu viel Rekursion" ausgibt, funktioniert dies in Chrome, oder? Der Stapel läuft über, und auf diese Weise liefert Chrome eine Stapelüberlauf-Ausnahme.
David Conrad
4
Sie sollten verwenden +{toString:"".toLocaleString}:-)
Bergi
62

Ich war frustriert darüber, dass Java 7 und Java 8 in meiner vorherigen Antwort gegen meinen bösen Code immun sind . Also entschied ich, dass ein Patch dafür notwendig war.

Erfolg! Ich habe einen printStackTrace()Wurf gemacht StackOverflowError. printStackTrace()wird häufig zum Debuggen und Protokollieren verwendet, und niemand vermutet, dass dies gefährlich sein könnte. Es ist nicht schwer zu erkennen, dass dieser Code missbraucht werden kann, um einige schwerwiegende Sicherheitsprobleme zu verursachen:

public class StillMoreEvilThanMyPreviousOne {
    public static void main(String[] args) {
        try {
            evilMethod();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void evilMethod() throws Exception {
        throw new EvilException();
    }

    public static class EvilException extends Exception {
        @Override
        public Throwable getCause() {
            return new EvilException();
        }
    }
}

Einige Leute denken vielleicht, dass dies eine offensichtliche Rekursion ist. Es ist nicht. Der EvilExceptionKonstruktor ruft die getCause()Methode nicht auf, sodass eine Ausnahme tatsächlich sicher ausgelöst werden kann. Das Aufrufen der getCause()Methode führt auch nicht zu einer StackOverflowError. Die Rekursion befindet sich innerhalb des normalerweise unerwarteten printStackTrace()Verhaltens von JDK und in der Bibliothek von Drittanbietern zum Debuggen und Protokollieren, die zum Untersuchen der Ausnahme verwendet werden. Ferner ist es wahrscheinlich, dass der Ort, an dem die Ausnahme ausgelöst wird, sehr weit von dem Ort entfernt ist, an dem sie behandelt wird.

Wie auch immer, hier ist ein Code, der ein wirft StackOverflowErrorund schließlich keine rekursiven Methodenaufrufe enthält. Das StackOverflowErrorpassiert außerhalb der mainMethode in JDKs UncaughtExceptionHandler:

public class StillMoreEvilThanMyPreviousOneVersion2 {
    public static void main(String[] args) {
        evilMethod();
    }

    private static void evilMethod() {
        throw new EvilException();
    }

    public static class EvilException extends RuntimeException {
        @Override
        public Throwable getCause() {
            return new EvilException();
        }
    }
}
Exception: java.lang.StackOverflowError thrown from the UncaughtExceptionHandler in thread "main"
Victor Stafusa
quelle
2
: D und jetzt mit einer Cross-Java-Version Stackoverflow-kompatiblen Methode. Böser Bastard! : D
Kiwy
9
Ich bin geneigt, die Rekursion in diesem Fall als offensichtlich zu betrachten.
Taemyr
1
@BlacklightShining Das Aufrufen der getCause()Methode führt nicht zu StackOverflowError. Es beruht auf der Tatsache, dass es einen JDK-Code gibt, der die getCause()Methode rekursiv aufruft .
Victor Stafusa
2
Ich dachte, Sie könnten dies vereinfachen, indem Sie den Text von getCauseauf "nur" ändern , return this;aber anscheinend ist Java dafür zu schlau. Es stellt fest, dass es sich um ein " CIRCULAR REFERENCE" handelt.
David Conrad
1
Ich habe keinen bekommen, StackOverflowErrorweil das OpenBSD 5.5-Paket von jdk-1.7.0.21p2v0 einen Fehler hat. Es wirft nicht StackOverflowError. Es trifft SIGSEGVund entleert den Kern.
Kernigh
57

Linux x86 NASM-Assembly

section .data
    helloStr:     db 'Hello world!',10 ; Define our string
    helloStrLen:  equ $-helloStr       ; Define the length of our string

section .text
    global _start

    doExit:
        mov eax,1 ; Exit is syscall 1
        mov ebx,0 ; Exit status for success
        int 80h   ; Execute syscall

    printHello:
        mov eax,4           ; Write syscall is No. 4
        mov ebx,1           ; File descriptor 1, stdout
        mov ecx,helloStr    ; Our hello string
        mov edx,helloStrLen ; The length of our hello string
        int 80h             ; execute the syscall

    _start:
        call printHello ; Print "Hello World!" once
        call doExit     ; Exit afterwards

Spoiler:

Wenn wir vergessen haben, von printHello zurückzukehren, springen wir gleich wieder in _start.

Michael Ehrenreich
quelle
78
In der Montage ist nichts offensichtlich.
11684
21
@ 11684: Ich finde das Gegenteil richtig: In der Assembly ist alles offensichtlich, weil niemand Abstraktionen verwenden kann, um zu verbergen, was der Code wirklich tut.
Mason Wheeler
3
Dies ist brillant für seine Einfachheit und Eleganz.
Haneefmubarak
11
@ MasonWheeler: Ich ziehe es vor zu sagen, dass alles sichtbar statt offensichtlich ist ... Um den Unterschied zwischen sichtbar und offensichtlich zu erkennen, verweise ich gerne auf underhanded.xcott.com
Olivier Dulac
2
Ich erinnere mich an meine häufigen Fehler des Vergessensret
Ruslan
48

C ++ zur Kompilierungszeit

template <unsigned N>
struct S : S<N-1> {};

template <>
struct S<0> {};

template
struct S<-1>;
$ g ++ -c test.cc -ftemplate-depth = 40000
g ++: Interner Compilerfehler: Segmentierungsfehler (Programm cc1plus)
Bitte reichen Sie einen vollständigen Fehlerbericht ein.
gegebenenfalls mit vorverarbeiteter Quelle.
Anweisungen finden Sie unter.

Es gibt keine Rekursion dieser Quelldatei, keine der Klassen hat sich selbst als Basisklasse, auch nicht indirekt. (In C ++, in einer Schablonenklasse wie dieser, S<1>und S<2>sind völlig unterschiedliche Klassen.) Der Segmentierungsfehler ist auf einen Stapelüberlauf nach einer Rekursion im Compiler zurückzuführen.

hvd
quelle
7
Ich gebe zu, ich hätte dies in Ihrem Metaprogramm eine offensichtliche Rekursion genannt.
2
GCC erkennt Rekursion und stoppt diese Seite ordnungsgemäß (4.8 und höher scheinen in Ordnung zu sein)
Alec Teal
2
template <typename T> auto foo(T t) -> decltype(foo(t)); decltype(foo(0)) x;ist etwas kürzer.
Casey
2
@hvd Es scheint einen Fehler in GCC auszunutzen. clang fängt die fehlerhafte Verwendung ab - von der Sie vermutlich bereits wissen -, aber mein GCC gibt fast 2 Megabyte an Fehlermeldungen aus.
Casey
4
+1 für Meta-Stack-Überlauf : p
Aschratt
45

Bash (Gefahrenalarm)

while true
do 
  mkdir x
  cd x
done

Streng genommen führt dies nicht direkt zu einem Stapelüberlauf, sondern zu einer Situation , die möglicherweise als " anhaltend stapelüberlauferzeugend " bezeichnet wird: Wenn Sie dies ausführen, bis Ihre Festplatte voll ist, und das Durcheinander mit "rm -rf" beseitigen möchten x ", dieser wird getroffen.

Dies ist jedoch nicht auf allen Systemen der Fall. Einige sind robuster als andere.

Große Gefahr WARNUNG:

Einige Systeme handhaben dies sehr schlecht und es kann schwierig sein, die Daten zu bereinigen (da "rm -rf" selbst auf ein Wiederherstellungsproblem stößt). Möglicherweise müssen Sie ein ähnliches Skript für die Bereinigung schreiben.

Versuchen Sie dies besser in einer Scratch-VM, wenn Sie sich nicht sicher sind.

PS: Gleiches gilt natürlich auch, wenn dies in einem Batch-Skript programmiert oder durchgeführt wird.
PPS: Es kann interessant sein, einen Kommentar von Ihnen zu erhalten, wie sich Ihr bestimmtes System verhält ...

blabla999
quelle
wie ich schrieb: auf vielen systemen versucht rm -rf herunterzufahren und wird von einem getroffen (heutzutage vielleicht nicht mehr mit 64bit adressraum - aber auf kleineren maschinen mit einem kleineren stapel vielleicht). Natürlich kann es auch "rm" -Implementierungen geben, die es anders machen ...
blabla999
2
Scheint mir sowas while cd x; do :; done; cd ..; while rmdir x; cd ..; done;zu kümmern.
Blacklight Shining
Sie haben vollkommen recht (das habe ich mit "ähnlichem Skript zum Aufräumen" gemeint). Sobald Ihre Festplatte (oder Ihr Kontingent) jedoch voll ist, kann es sogar schwierig sein, sich danach anzumelden, da einige Systeme mit dieser Situation schlecht umgegangen sind. Sie müssen sich als root anmelden und das Skript ausführen (was auf den heutigen PCs einfach ist, in der Antike jedoch oft schwierig war, wenn Computer von mehr als einem Benutzer verwendet wurden).
Blabla999
lustig, das bekommt mehr Stimmen als die Smalltalk-Magie unten (hätte das nie gedacht!)
blabla999
jemand versucht dies unter Windows?
Sarge Borsch
43

Java

Eine schöne von Java Puzzlers . Was wird gedruckt?

public class Reluctant {
    private Reluctant internalInstance = new Reluctant();

    public Reluctant() throws Exception {
        throw new Exception("I'm not coming out");
    }

    public static void main(String[] args) {
        try {
            Reluctant b = new Reluctant();
            System.out.println("Surprise!");
        } catch (Exception ex) {
            System.out.println("I told you so");
        }
    }
}

Es schlägt tatsächlich mit einem StackOverflowError fehl.

Die Ausnahme im Konstruktor ist nur ein roter Hering. Das sagt das Buch dazu:

Wenn Sie einen Konstruktor aufrufen, werden die Initialisierer der Instanzvariablen vor dem Rumpf des Konstruktors ausgeführt . In diesem Fall internalInstanceruft der Initialisierer für die Variable den Konstruktor rekursiv auf. Dieser Konstruktor wiederum initialisiert sein eigenes internalInstanceFeld, indem er den Reluctant-Konstruktor erneut usw. unendlich aufruft. Diese rekursiven Aufrufe führen dazu, dass a, StackOverflowErrorbevor der Konstruktorkörper jemals die Möglichkeit erhält, ausgeführt zu werden. Da StackOverflowErrores sich um einen Untertyp von Erroranstatt handelt Exception, wird er von der catch-Klausel in mainnicht erfasst.

ntoskrnl
quelle
41

Latex

\end\end

Der Eingabestapel läuft über, weil er sich \endwiederholt in einer Endlosschleife erweitert, wie hier erläutert .

TeX schlägt mit a TeX capacity exceeded, sorry [input stack size=5000]oder ähnlich fehl .

bwDraco
quelle
1
Ich habe auf ein TeX / LaTeX-Problem gewartet. Diese Dinge sind irritierender als die meisten - normalerweise habe ich vergessen, dass das, was ich tue, als Programmierung gilt, als ich es plötzlich geschafft habe, etwas zu schreiben, das unendlich rekursiv ist.
Ernir
1
"TeX-Kapazität überschritten, sorry" TeX ist so höflich, wenn es fehlschlägt.
Alex A.
40

BF

Wird irgendwann der Stack überlaufen, hängt nur davon ab, wie lange der Interpreter den Stack macht ...

+[>+]
Timtech
quelle
5
Dieser Code erinnert mich an das Spiel des Lebens.
Adam Arold
10
Genau genommen gibt es im Brainfuck keinen Stack.
Fejesjoco
6
@fejesjoco Tape, wenn du möchtest.
Timtech
32

C #, zur Kompilierungszeit

Es gibt verschiedene Möglichkeiten, den Microsoft C # -Compiler dazu zu bringen, seinen Stapel zu sprengen. Jedes Mal, wenn Sie einen "Ausdruck ist zu komplex zum Kompilieren" -Fehler vom C # -Compiler sehen, der mit ziemlicher Sicherheit darauf zurückzuführen ist, dass der Stack durchgebrannt ist.

Der Parser ist ein rekursiver Abstieg, sodass alle ausreichend tief verschachtelten Sprachstrukturen den Stapel sprengen:

 class C { class C { class C { ....

Der Ausdrucksparser ist ziemlich klug, wenn es darum geht, Rekursionen auf der Seite zu eliminieren, auf der häufig Rekursionen auftreten. Gewöhnlich:

x = 1 + 1 + 1 + 1 + .... + 1;

die einen enorm tiefen Analysebaum baut, wird den Stapel nicht sprengen. Aber wenn Sie die Rekursion auf der anderen Seite erzwingen:

x = 1 + (1 + (1 + (1 + ....+ (1 + 1))))))))))))))))))))))))))))))))))))))))))...;

dann kann der Stapel geblasen werden.

Diese haben die unelegante Eigenschaft, dass das Programm sehr groß ist. Es ist auch möglich, den Semantikanalysator mit einem kleinen Programm in unbegrenzte Rekursionen zu versetzen, da er nicht intelligent genug ist, um bestimmte ungerade Zyklen im Typensystem zu beseitigen. (Roslyn könnte dies verbessern.)

public interface IN<in U> {}
public interface IC<X> : IN<IN<IC<IC<X>>>> {}
...
IC<double> bar = whatever;
IN<IC<string>> foo = bar;  // Is this assignment legal? 

Ich beschreibe hier, warum diese Analyse in eine unendliche Rekursion geht:

http://blogs.msdn.com/b/ericlippert/archive/2008/05/07/covariance-and-contravariance-part-twelve-to-infinity-but-not-beyond.aspx

und für viele weitere interessante Beispiele sollten Sie dieses Papier lesen:

http://research.microsoft.com/en-us/um/people/akenn/generics/FOOL2007.pdf

Eric Lippert
quelle
2
Die genaue Fehlermeldung lautet fatal error CS1647: An expression is too long or complex to compile near (code). Die Dokumentation für diese Fehlermeldung ist hier und genau so, wie Sie sagen: "Es gab einen Stapelüberlauf im Compiler, der Ihren Code verarbeitet."
bwDraco
32

Im Internet (von Milliarden Menschen pro Tag genutzt)

Redirects, HTTP status code: 301

Zum Beispiel auf der Dell Support-Website (keine Beleidigung, sorry Dell):

Wenn Sie das Support-TAG von der URL entfernen, werden unendlich viele Weiterleitungen ausgeführt . In der folgenden URL steht ###### für ein beliebiges Support-TAG.

http://www.dell.com/support/drivers/uk/en/ukdhs1/ServiceTag/#####?s=BSD&~ck=mn

Ich glaube, das entspricht einem Stapelüberlauf.

CodeSetter
quelle
5
nice way :) firefox sagt, dass es umleitet, so dass die Anfrage nicht aufgelöst werden kann, die das Stackoverflow-Äquivalent ist :)
masterX244
3
Beachten Sie, dass die Weiterleitungen nicht unendlich sind. Wenn Sie auf "Erneut versuchen" klicken /Errors/, werden der URL einige weitere hinzugefügt, und die Weiterleitungen werden nach dem Empfang von HTTP 400 Bad Request beendet. Dies führt jedoch möglicherweise zu einem besseren Stapelüberlauf als eine unendliche Umleitung.
Nandhp
@nandhp, ich stimme zu, aber wenn Sie denken, ein Browser der dritten Welt (nicht modern, IE usw.), haben sie keine Ahnung von dieser Situation.
CodeSetter
2
Hier ist , wie Wget antwortet hier: pastebin.com/pPRktM1m
bwDraco
1
http://www.dell.com/support/drivers/uk/en/ukdhs1/ServiceTag/Errors/Errors/Errors/Errors/Errors/Errors/Errors/Errors/Errors/Errors/Errors/Errors/Errors/Errors/Errors/Errors/Errors/Errors/Errors/Errors/Errors/Errors/Errors/Errors/Errors/Errors/Errors/Errors/Errors/Errors/Errors/...
Vortico
31

PHP

Ein Stapelüberlauf, der nur mit Schleifenelementen durchgeführt wird.

$a = array(&$a);
while (1) {
    foreach ($a as &$tmp) {
        $tmp = array($tmp, &$a);
    }
}

Erklärung (Hover um den Spoiler zu sehen):

Das Programm schlägt fehl, wenn der Interpreter versucht, das $tmpArray zu entfernen (wenn er es $tmphier neu zuordnet ). Nur weil das Array zu tief ist (selbstreferenzierend) und der Garbage Collector dann in einer Rekursion endet.

Bwoebi
quelle
16
PHP's GC kann keine selbstreferenziellen Strukturen erkennen? Ja wirklich?! Autsch. Das ist böse
Peter C
1
Ja, aber es ist nicht perfekt. Es gibt einen Grund, warum while (1) { $a = array(&$a); }oder etwas ähnliches nur die Speichergrenze erreicht ...
bwoebi
Ja, ich denke, es ist der gleiche Grund, warum PHP auch in Bezug auf objektorientierte Entwicklung nicht richtig funktioniert. (Abstraktion, Vererbung usw.)
Pythonian29033
1
@ Pythonian29033 Pflege zu erarbeiten?
Vvondra
30

Java

Ich habe genau das Gegenteil getan - ein Programm, das offensichtlich einen Stapelüberlauffehler auslösen sollte, dies jedoch nicht.

public class Evil {
    public static void main(String[] args) {
        recurse();
    }

    private static void recurse() {
        try {
            recurse();
        } finally {
            recurse();
        }
    }
}

Hinweis: Das Programm wird in O (2 n ) ausgeführt und n ist die Größe des Stapels (normalerweise 1024).

Von Java Puzzlers # 45:

Nehmen wir an, unsere Maschine kann 10 10 Aufrufe pro Sekunde ausführen und 10 10 Ausnahmen pro Sekunde generieren , was nach aktuellen Standards recht großzügig ist. Unter diesen Voraussetzungen endet das Programm in etwa 1,7 × 10 291 Jahren. Aus diesem Grund wird die Lebensdauer unserer Sonne auf 10 bis 10 Jahre geschätzt. Es ist also sicher, dass niemand von uns anwesend sein wird, wenn dieses Programm endet. Obwohl es keine Endlosschleife ist, könnte es genauso gut sein.

ntoskrnl
quelle
3
Offensichtliche Rekursion ... nicht sehr interessant.
Kami
5
@Kami Hast du es versucht? Haben Sie tatsächlich einen StackOverflowError erhalten? Es scheint offensichtlich, aber es ist nicht so.
ntoskrnl
Nur weil eine Ausnahme abgefangen wird, heißt das nicht, dass sie nie geworfen wurde. Dieses Programm
löst
1
@CodesInChaos Okay, also habe ich das noch einmal überprüft und die richtige Version verwendet finallyanstatt catch, und die Laufzeit ist O (2 ^ n). Antwort aktualisiert.
ntoskrnl
@ntorkrnl nice one + 1'd; Übrigens hat der Compiler bereits Stackoverflow (Compiler läuft auch innerhalb der VM)
masterX244
30

C #

Erster Beitrag, also bitte schont mich.

class Program
{
    static void Main()
    {
        new System.Diagnostics.StackTrace().GetFrame(0).GetMethod().Invoke(null, null);
    }
}

Dadurch wird einfach ein Stack-Trace erstellt, der oberste Frame (der unser letzter Aufruf ist Main()) wird abgerufen, die Methode abgerufen und aufgerufen.

BenM
quelle
nette Art, sich selbst zu beschwören, was nicht so offensichtlich ist, ohne es zu erklären; +1 gegeben
masterX244
Sie sollten diesen Code mit "Was könnte möglicherweise schief gehen?" Kommentieren: P
Spaceman
26

Java

  • Tritt in Java 5 printStackTrace()in eine Endlosschleife ein.
  • In Java 6 printStackTrace()wirft StackOverflowError.
  • In Java 7 und 8 wurde es behoben.

Das Verrückte ist, dass es in Java 5 und 6 nicht vom Benutzercode kommt, sondern im JDK-Code. Niemand vernünftiger Verdächtiger, printStackTrace()dessen Ausführung gefährlich sein kann.

public class Bad {
    public static void main(String[] args) {
        try {
            evilMethod();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void evilMethod() throws Exception {
        Exception a = new Exception();
        Exception b = new Exception(a);
        a.initCause(b);
        throw a;
    }
}
Victor Stafusa
quelle
7
Ja; stackoverflows out of nothing sind der beste freund für codetrolling und starke kopfschmerzen bei der jagd em
masterX244
2
Wenn sich jemand wundert, verursacht der entsprechende Code in C # eine Endlosschleife. Die InnerExceptionEigenschaft ist jedoch schreibgeschützt und wird im Konstruktor festgelegt. Daher ist eine Reflektion erforderlich, um dies zu bewirken.
Athari
17

JavaScript: Nicht rekursive, iterative Funktionsmutation

var f = function() {
    console.log(arguments.length);
};

while (true) {
    f = f.bind(null, 1);
    f();
}

Hier gibt es überhaupt keine Rekursion. fwird wiederholt mit immer mehr Argumenten versehen, bis der Stack in einem einzigen Funktionsaufruf überlaufen kann . Der console.logTeil ist optional, wenn Sie sehen möchten, wie viele Argumente dafür erforderlich sind. Es sorgt auch dafür, dass clevere JS-Motoren dies nicht wegoptimieren.

Code-Golf-Version in CoffeeScript, 28 Zeichen:

f=->
do f=f.bind f,1 while 1
Justin Morgan
quelle
14

C # mit einem epischen Fehler

using System.Xml.Serialization;

[XmlRoot]
public class P
{
    public P X { get { return new P(); } set { } }
    static void Main()
    {
        new XmlSerializer(typeof(P)).Serialize(System.Console.Out, new P());
    }
}

Die Art und Weise, wie es fehlschlägt, ist episch und hat mich völlig umgehauen:

Bildbeschreibung hier eingeben

Es ist nur ein Bild aus einer scheinbar unendlichen Reihe seltsamer Bilder.

Das muss das Seltsamste sein, was es je gab. Kann jemand erklären? Anscheinend werden diese weißen Blöcke durch die immer größere Anzahl von Leerzeichen angezeigt, die zum Einrücken verwendet werden. Es passiert auf einem Win7 Enterprise x64 mit .NET 4.5.

Ich habe das Ende noch nicht gesehen. Wenn Sie ersetzen System.Console.Outmit System.IO.Stream.Null, stirbt es ziemlich schnell.

Die Erklärung ist ziemlich einfach. Ich erstelle eine Klasse mit einer einzelnen Eigenschaft und sie gibt immer eine neue Instanz ihres enthaltenden Typs zurück. Es ist also eine unendlich tiefe Objekthierarchie. Jetzt brauchen wir etwas, das versucht, das durchzulesen. Dort verwende ich die XmlSerializer, die genau das macht. Und anscheinend verwendet es eine Rekursion.

fejesjoco
quelle
Ja; Serialisierung ftw: P; aber noch lustiger ist es, wenn ydie eigentümlichkeit völlig außerhalb ihres codes liegt, wie snakeyaml die eigenschaften erhält und eine klasse sich auf eine weise zurückgibt, die endlos wiederkehrt
masterX244
1
Der Überlauf passiert zwar außerhalb meines Codes, aber der wahre Grund, warum ich das gepostet habe, ist der unerwartete Nebeneffekt in der Konsole :-)
fejesjoco
Ich denke, die weißen Bits müssen entweder mit .Net 4.5 zu tun haben oder wie Ihre Umgebung eingerichtet ist, denn mit .Net 4 (auch wenn Sie durch cmd laufen) bekomme ich nur Leerzeichen, keine weißen Blöcke. Ich vermute, dass entweder die Win7 Enterprise-Version von cmd oder der .Net 4.5-Konsolenemulator eine bestimmte Zeichenkombination als "Ändern des Konsolenfarbhintergrunds" behandeln.
Pharap
Ich kann es mit .NET 4.5 auf Win7 x64 Professional mit Aero nicht reproduzieren
Ray
Kann auch nicht reproduzieren. .NET 4.5, Windows 8.1 Pro.
bwDraco
13

Bash

_(){ _;};_

Zwar mögen viele erkennen, dass die Rekursion offensichtlich ist , aber es scheint hübsch. Nein?

Bei der Ausführung sehen Sie garantiert:

Segmentation fault (core dumped)
devnull
quelle
5
Dieser ist schöner und schlimmer:_(){_|_;};_
RSFalcon7
1
@ RSFalcon7 Gabelbombenalarm! (Braucht es nicht auch ein Leerzeichen nach dem {, um syntaktisch korrekt zu sein?)
Blacklight Shining
3
Versuchen Sie dies auch:(){:|:;}:
Tarek Eldeeb
14
Sieht aus wie ein gruseliges, mutiertes Emoticon.
Tim Seguine
8
Bash-Emoticons: Fresser von Gesichtern, Killer von Stapeln.
NobleUplift
12

Haskell

(traurig, aber wahr, bis zumindest ghc-7.6, obwohl mit O1oder mehr das Problem wegoptimiert wird)

main = print $ sum [1 .. 100000000]
hörte auf, sich gegen den Uhrzeigersinn zu drehen
quelle
Sollte es nicht automatisch (in Summe) eine Tail-Call-Optimierung durchführen?
Blabla999
2
@ blabla999: Tail-Calls sind in Haskell nicht so relevant, es ist meistens die Ansammlung von Thunk aufgrund verdeckter Faulheit, die solche Probleme verursacht. In diesem Fall ist das Problem, dass sumin Bezug auf implementiert wird foldl, die Tail Calls verwendet, aber da der Akku nicht streng ausgewertet wird, wird lediglich ein Stapel von Thunks erzeugt, der so groß ist wie die ursprüngliche Liste. Das Problem verschwindet beim Umschalten auf foldl' (+), das streng auswertet und somit eine WHN in seinem Tail Call zurückgibt. Oder, wie gesagt, wenn Sie die Optimierungen von GHC einschalten!
hörte
aah - interessant, wenn also niemand auf den thunk warten würde (dh den print weglassen), würde der müllsammler sie wegsammeln (von vorne nach hinten)?
Blabla999
1
Übrigens ist nichts davon wirklich durch den Haskell-Standard spezifiziert: Es ist lediglich erforderlich, dass die Bewertung nicht streng ist , dh eine nicht abschließende Berechnung wird nicht für immer blockiert, wenn das Ergebnis nicht vollständig benötigt wird. Wie lange es wirklich blockiert, hängt von der Implementierung ab. In Standard-Lazy-GHC blockiert es überhaupt nicht, bis Sie das Ergebnis anfordern.
hörte
2
haskell ist cool.
Blabla999
12

Smalltalk

Dies erzeugt eine neue Methode im laufenden Betrieb, die
  eine neue Methode im laufenden Betrieb erzeugt, die
    eine neue Methode im laufenden Betrieb erzeugt, die
      ...
    ...
  ...
und dann zu ihr überträgt.

Ein zusätzliches Plus an Würze ergibt sich aus der gleichzeitigen Belastung von Stapelspeicher UND Heapspeicher, indem sowohl ein längerer als auch ein längerer Methodenname und eine große Zahl als Empfänger erstellt werden, wenn wir das Loch runterfallen ... (aber die Rekursion trifft uns zuerst ).

kompiliere in Integer:

downTheRabbitHole
    |name deeperName nextLevel|

    nextLevel := self * 2.
    name := thisContext selector.
    deeperName := (name , '_') asSymbol.
    Class withoutUpdatingChangesDo:[
        nextLevel class 
            compile:deeperName , (thisContext method source copyFrom:name size+1).
    ].
    Transcript show:self; showCR:' - and down the rabbit hole...'.
    "/ self halt. "/ enable for debugging
    nextLevel perform:deeperName.

Dann springen Sie, indem Sie auswerten "2 downTheRabbitHole"...
... nach einer Weile werden Sie in einem Debugger enden, der eine RecursionException anzeigt.

Dann musst du das ganze Durcheinander beseitigen (sowohl SmallInteger als auch LargeInteger haben jetzt eine Menge Wunderland-Code):

{SmallInteger . LargeInteger } do:[:eachInfectedClass |
    (eachInfectedClass methodDictionary keys 
        select:[:nm| nm startsWith:'downTheRabbitHole_'])
            do:[:each| eachInfectedClass removeSelector:each]

Oder verbringen Sie einige Zeit im Browser und entfernen Sie das Wunderland von Alice.

Hier sind einige aus dem Kopf der Spur:

2 - and down the rabbit hole...
4 - and down the rabbit hole...
8 - and down the rabbit hole...
16 - and down the rabbit hole...
[...]
576460752303423488 - and down the rabbit hole...
1152921504606846976 - and down the rabbit hole...
2305843009213693952 - and down the rabbit hole...
[...]
1267650600228229401496703205376 - and down the rabbit hole...
2535301200456458802993406410752 - and down the rabbit hole...
5070602400912917605986812821504 - and down the rabbit hole...
[...]
162259276829213363391578010288128 - and down the rabbit hole...
324518553658426726783156020576256 - and down the rabbit hole...
[...]
and so on...

PS: Die Datei "withoutUpdatingChangesFile:" wurde hinzugefügt, um zu vermeiden, dass die persistente Änderungsprotokolldatei von Smalltalk anschließend bereinigt werden muss.

PPS: Danke für die Herausforderung: Über etwas Neues und Innovatives nachzudenken hat Spaß gemacht!

PPPS: Ich stelle fest, dass einige Smalltalk-Dialekte / -Versionen überlaufende Stack-Frames auf den Heap kopieren. Daher kann es stattdessen zu Speichermangel kommen.

blabla999
quelle
2
LOL +1 für diese schwarze Magie in ihrer reinen Form
masterX244
Wenn ich etwas Zeit finde, kann ich mich "verbessern", indem ich eine anonyme Methode einer anonymen Klasse verwende, um das dunkelste Kaninchenloch aller Zeiten zu machen ...
blabla999
12

C #

Wirklich groß struct, keine Rekursion, reines C #, kein unsicherer Code.

public struct Wyern
{
    double a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z;
}
public struct Godzilla
{
    Wyern a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z;
}
public struct Cyclops
{
    Godzilla a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z;
}
public struct Titan
{
    Cyclops a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z;
}
class Program
{
    static void Main(string[] args)
    {
        // An unhandled exception of type 'System.StackOverflowException' occurred in ConsoleApplication1.exe
        var A=new Titan();
        // 26×26×26×26×8 = 3655808 bytes            
        Console.WriteLine("Size={0}", Marshal.SizeOf(A));
    }
}

Als Kicker stürzt das Debug-Fenster ab {Cannot evaluate expression because the current thread is in a stack overflow state.}


Und die generische Version (danke für den Vorschlag NPSF3000)

public struct Wyern<T>
    where T: struct
{
    T a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z;        
}


class Program
{
    static void Main(string[] args)
    {
        // An unhandled exception of type 'System.StackOverflowException' occurred in ConsoleApplication1.exe
        var A=new Wyern<Wyern<Wyern<Wyern<int>>>>();
    }
}
ja72
quelle
Benötigt mehr generische Code-Methoden: P
NPSF3000
Das sieht nach Rekursion aus, ist aber mit verschachtelten Typargumenten möglich.
ja72
1
Ich habe Ihre C # struct-Antwort nicht gesehen, bevor ich meine gepostet habe. Ich habe immer noch einen etwas anderen Ansatz, also lassen wir sie vielleicht koexistieren.
Thomas Weller
11

C #

Fehlerhafte Implementierung des ==Operators overriden :

public class MyClass
{
    public int A { get; set; }

    public static bool operator ==(MyClass obj1, MyClass obj2)
    {
        if (obj1 == null)
        {
            return obj2 == null;
        }
        else
        {
            return obj1.Equals(obj2);
        }
    }

    public static bool operator !=(MyClass obj1, MyClass obj2)
    {
        return !(obj1 == obj2);
    }

    public override bool Equals(object obj)
    {
        MyClass other = obj as MyClass;
        if (other == null)
        {
            return false;
        }
        else
        {
            return A == other.A;
        }
    }
}

Man könnte sagen, es liegt auf der Hand, dass operator==sich der ==Operator selbst aufruft , aber normalerweise denken Sie nicht ==so.

Sebastian Negraszus
quelle
11

Antwort mit SnakeYAML starten

class A
{

    public static void main(String[] a)
    {
         new org.yaml.snakeyaml.Yaml().dump(new java.awt.Point());
    }
}

Edit: es ungolfed

Der Leser muss herausfinden, wie das funktioniert: P (Tipp: stackoverflow.com)

Übrigens: Die Rekursion wird dynamisch von SnakeYAML erstellt (Sie werden feststellen, ob Sie wissen, wie es die Felder erkennt, die es serialisiert, und dann hineinschaut) Point den Quellcode ).

Bearbeiten: erzählen, wie das funktioniert:

SnakeYAML sucht nach einem Paar von getXXXund setXXXmthod mit demselben Namen für XXXund return type des Getters ist derselbe wie parameter of setter; und überraschenderweise hat die PointKlasse ein Point getLocation()und void setLocation(Point P)das kehrt zurück; SnakeYAML merkt es nicht und wiederholt diese Eigenart und StackOverflows. Entdeckt, dass man, wenn man mit ihnen in einem arbeitet HashMapund auf stackoverflow.com danach fragt.

masterX244
quelle
10

C #

falsch implementierter Property Getter

class C
{
   public int P { get { return P; } }
}

static void Main()
{
   int p = new C().P;
}
Alberto
quelle
14
MEINER BESCHEIDENEN MEINUNG NACH. Dies ist ein klassisches Beispiel für eine offensichtliche Rekursion ... (und daher nicht gültig)
Ole Albers
2
Nun, es ist nur dann offensichtlich, wenn Sie es einmal getan haben und herausgefunden haben, dass C # Getter nicht so funktionieren, wie Sie es vielleicht gedacht haben. Immerhin handelt es sich bei diesem Code um eine Membervariablendeklaration. Warum sollte er also keine tatsächliche Membervariable erstellen?
meustrus
2
Dies ist nicht mehr als eine verschlungene Vorgehensweisestatic void Main() { Main(); }
Jodrell
5
@Jodrell Du würdest nicht versehentlich rekursiv schreiben Main(). Es ist jedoch recht einfach, eine rekursive Eigenschaft versehentlich zu schreiben und dann vom Stapelüberlauf verwechselt zu werden.
Svick
1
Dies ist nützlich, wenn Sie C # abholen.
2.