Maneira mais estranha de produzir um estouro de pilha [fechado]

146

Como programador, você certamente conhece o erro de um estouro de pilha devido a uma recursão óbvia. Mas certamente existem muitas maneiras estranhas e incomuns de obter seu idioma favorito para cuspir esse erro.

Objetivos.

  1. Deve causar um estouro de pilha claramente visível na saída de erro.
  2. Não é permitido usar uma recursão óbvia.

Exemplos de programas inválidos:

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

As formas mais criativas são as melhores, pois este é um . Ou seja, evite respostas óbvias chatas como esta:

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

Embora eu tenha aceitado uma resposta agora, adicionar mais respostas ainda está bem :)

masterX244
fonte
14
Costumo produzir navegando para stackoverflow.com, embora soubesse consultar 'estouro de pilha' no meu mecanismo de pesquisa preferido.
OJFord 17/02
21
Use o Internet Explorer. Uma maneira de pegar um :)
asgoth
64
A maneira mais estranha de produzir um estouro de pilha é postar um concurso de popularidade no codegolf.stackexchange.com, solicitando que as pessoas publiquem a maneira mais estranha de produzir um estouro de pilha. Os respondentes, ao testar suas soluções para a pergunta, produzirão um estouro de pilha. Eu ainda não testei, então não posso ter certeza de que funciona (e é por isso que não o publiquei como resposta).
Tim Seguine
3
Sou parcial com esse método: joelonsoftware.com/items/2008/09/09/15.html
robert
11
Dirija um Toyota (Ei, espere um minuto, meu carro é um Toyota ...) #
ossifrage melindroso

Respostas:

224

Pitão

import sys
sys.setrecursionlimit(1)

Isso fará com que o intérprete falhe imediatamente:

$ 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
$ 

Em vez de usar a recursão, apenas encolhe a pilha e transborda imediatamente.

marinus
fonte
12
Bonito, mas não exatamente o que a pergunta original estava buscando na minha opinião.
Nit
12
@ Não vejo o problema. E esta solução é insatisfatória?
SimonT
17
@ masterX244 Sim, o ponto principal da questão é "não faça da maneira usual".
Plutor 18/02
24
@Plutor: você normalmente cria um StackOverFlow de propósito?
Kiwy
15
Isso foi apenas um pouco inteligente na primeira vez que foi publicado .
primo
189

Pitão

import webbrowser
webbrowser.open("http://stackoverflow.com/")
O médico
fonte
7
Fantástico. Agradável e elegante.
James Webster
103
Muito literal. Muito resposta.
Tim Seguine
47
Bem, isso mostra um estouro de pilha, mas para produzir um, isso só funciona se Jeff Atwood ou Joel Spolsky estiver executando.
Caracol mecânico
10
@TimSeguine Wow.
Sean Allred
9
Não é uma resposta, pois não está na saída de erro.
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;
}

Funciona substituindo o endereço de retorno. Portanto, gretorna ao ponto mainantes de chamarf . Funcionará para qualquer plataforma em que os endereços de retorno estejam na pilha, mas pode exigir ajustes.

Obviamente, escrever fora de uma matriz é um comportamento indefinido e você não tem garantia de que ela causará um estouro de pilha em vez de, por exemplo, pintar seu bigode de azul. Detalhes de sinalizadores de plataforma, compilador e compilação podem fazer uma grande diferença.

Ugoren
fonte
1
Isso não produziria um segfault?
11684
4
+1 para a manipulação de pilha estranha e absolutamente nenhuma recursão!
RSFalcon7
6
@ 11684, é um comportamento indefinido, de modo que geralmente pode falhar. No Linux de 32 bits (que eu testei), ele grava fora da matriz, substituindo o endereço de retorno e não trava até a pilha estourar.
21414 ugoren
46
"Pinte seu bigode de azul" -> Essa frase me pegou.
Aneesh Dogra 17/02
2
Uau. Isso é fantasticamente ofuscado.
MirroredFate
108

JavaScript / DOM

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

    title = 'Kill me!';
}

Se você quiser matar o seu navegador, tente isso no console.

Visão
fonte
53
Tenho certeza que você merece mais +1 votos, mas, infelizmente, as pessoas tentaram isso antes de votar .... lol
Reactgular
5
Bem, isso foi eficaz. Eu não conseguia nem abrir meu gerenciador de tarefas para matá-lo.
primo
1
Use o Chrome, guia "Fechar". Problema resolvido.
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 21/02
1
Droga, meu Firefox foi enforcado
Farhad
91

Java

Vi algo assim em algum lugar por aqui:

Edit: Encontrado onde o vi: resposta de Joe K para o programa mais curto que lança o erro StackOverflow

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

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

Isso pode confundir alguns iniciantes em Java. Ele simplesmente oculta a chamada recursiva. val + thistorna-se val + this.toString()porqueval é uma String.

Veja aqui: http://ideone.com/Z0sXiD

Justin
fonte
30
Na verdade, sim new StringBuilder().append(val).append("").append(this).toString(), e o último anexo chama String.valueOf (...), que por sua vez chama toString. Isso faz com que seu rastreamento de pilha seja um pouco variado (três métodos nele).
Pa Elo Ebermann 17/02
2
@ PaŭloEbermann Sim, isso está correto. No entanto, é muito mais fácil dizer que se torna "" + this.toString().
23714 Justin
2
Eu acho que isso + "" +pode ajudar as pessoas, pois parece inútil à primeira vista. String val;e return val + this;pode ser marginalmente sneakier
Cruncher
@ Cruncher não é de todo. Se você é um programador Java, você sabe que o caminho mais fácil de concatenar um int para uma seqüência durante a ""construção -String é com o+ ""
Justin
5
Em uma aplicação real do mundo que eu preferiria evitar infinitas recursão e estouro de pilha erros
jon_darkstar
77

C

Muito fácil:

int main()
{
    int large[10000000] = {0};
    return 0;
}
Shahbaz
fonte
10
+1 para não óbvio! Apesar é dependente muito sistema (um ulimit -s unlimitedna concha resolve este em Linux)
RSFalcon7
4
@ RSFalcon7, obrigado pelo +1, mas para mim isso foi realmente o mais óbvio !!
Shahbaz 17/02
14
@ Cruncher: Não resulta em recursão. O problema dado foi explodir a pilha. Em muitos sistemas operacionais, a pilha é de tamanho fixo e muito menor que dez milhões de ints; portanto, isso acaba com a pilha.
Eric Lippert
2
@ HannoBinder, No Linux onde eu testei, você não recebe um erro de estouro de pilha. Você recebe uma falha de segmentação, pois o transbordamento da pilha resulta no acesso a segmentos não proprietários. Não tenho certeza se o erro de estouro de pilha ainda existe no Linux, pois uma chamada de função recursiva infinita também gera uma falha de segmentação.
Shahbaz
3
~0ué um número muito grande em C.
Vortico
63

Estouro de pilha não recursivo em C

Incompatibilidade de convenção de chamada.

typedef void __stdcall (* ptr) (int);

void __cdecl hello (int x) { }

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

Compile com gcc -O0.

__cdeclAs funções esperam que o chamador limpe a pilha e __stdcallespera que o receptor faça isso; portanto, chamando pelo ponteiro da função typecast, a limpeza nunca é feita - maincoloca o parâmetro na pilha para cada chamada, mas nada aparece e, finalmente, o pilha preenche.

Jason C
fonte
2
boa maneira de spam a pilha: P
masterX244
62

Javascript

window.toString = String.toLocaleString;
+this;
Ryan Cavanaugh
fonte
5
Este é subestimado.
Ry-
1
O que +thisfaz?
NobleUplift
8
Unary + chama a operação abstrata ToNumber. Isso usa a operação ToPrimitive com um tipo de dica de número. ToPrimitive em um objeto usa o método interno [[DefaultValue]], que quando passado uma dica de número, primeiro verifica se o método valueOf () existe e retorna uma primitiva. window.valueOf () retorna um objeto, então [[DefaultValue]] retorna o resultado da chamada paraString (). A primeira coisa que window.toString faz (agora que é toLocaleString) é invocar toString em 'this'. Repetir.
22814 Ryan Cavanaugh
3
Se o Chrome emitir um erro, 'Demasiada recursão', funcionará no Chrome, certo? A pilha está cheia e é assim que o Chrome oferece uma exceção de pilha cheia.
David Conrad
4
Você deve usar +{toString:"".toLocaleString}:-)
Bergi
62

Fiquei frustrado pelo fato de o java 7 e o java 8 serem imunes ao meu código maligno na minha resposta anterior . Então eu decidi que era necessário um patch para isso.

Sucesso! Eu fiz printStackTrace()jogar um StackOverflowError. printStackTrace()é comumente usado para depuração e registro em log e ninguém suspeita razoavelmente que isso possa ser perigoso. Não é difícil ver que esse código pode ser abusado para criar alguns problemas sérios de segurança:

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

Algumas pessoas podem pensar que esta é uma recursão óbvia. Não é. O EvilExceptionconstrutor não chama o getCause()método, para que a exceção possa realmente ser lançada com segurança, afinal. Chamar o getCause()método também não resultará em um StackOverflowError. A recursão está dentro do printStackTrace()comportamento normalmente insuspeitado do JDK e em qualquer biblioteca de terceiros para depuração e criação de log usada para inspecionar a exceção. Além disso, é provável que o local onde a exceção é lançada esteja muito longe do local em que é manipulado.

Enfim, aqui está um código que lança um StackOverflowErrore não contém nenhuma chamada de método recursivo, afinal. O que StackOverflowErroracontece fora do mainmétodo, nos JDK's 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
fonte
2
: D e agora com um método compatível com stackoverflow da versão cross java. Bastardo do mal! : D
Kiwy 17/02
9
Estou inclinado a considerar óbvia a recursão neste caso.
Taemyr 17/02
1
@BlacklightShining Chamar o getCause()método não resulta em StackOverflowError. Ele se baseia no fato de que há um código do JDK que está chamando o getCause()método recursivamente .
Victor Stafusa 17/02
2
Eu pensei que você poderia simplificar isso, alterando o corpo de getCausepara apenas, return this;mas aparentemente Java é inteligente demais para isso. Ele percebe que é um " CIRCULAR REFERENCE".
David Conrad
1
Não recebi um StackOverflowErrorporque o pacote OpenBSD 5.5 do jdk-1.7.0.21p2v0 tem um erro. Não joga StackOverflowError. Ele atinge SIGSEGVe despeja o núcleo.
kernigh
57

Montagem NASM do Linux x86

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:

Esquecendo de retornar de printHello, então pulamos direto para _start novamente.

Michael Ehrenreich
fonte
78
Na montagem, nada é óbvio.
11684
21
@ 11684: Acho o oposto verdadeiro: na montagem, tudo é óbvio, porque ninguém pode usar abstrações para ocultar o que o código está realmente fazendo.
Mason Wheeler
3
Isto é brilhante por sua simplicidade e elegância.
21414 haneefmubarak
11
@ MasonWheeler: Prefiro dizer que tudo é visível em vez de óbvio ... Para uma boa maneira de ver a diferença entre visível e óbvio, adoro me referir a underhanded.xcott.com
Olivier Dulac
2
Lembro-me dos meus erros frequentes de esquecerret
Ruslan
48

C ++ em tempo de compilação

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

template <>
struct S<0> {};

template
struct S<-1>;
$ g ++ -c test.cc -ftemplate-depth = 40000
g ++: erro interno do compilador: falha de segmentação (programa cc1plus)
Envie um relatório completo de erros,
com fonte pré-processada, se apropriado.
Veja para instruções.

Não há recursão nesse arquivo de origem, nenhuma das classes tem ela própria como classe base, nem indiretamente. (Em C ++, em uma classe de modelo como essa S<1>e S<2>são classes completamente distintas.) A falha de segmentação ocorre devido ao estouro de pilha após recursão no compilador.

hvd
fonte
7
Confesso que chamaria isso de recursão óbvia no seu metaprograma.
2
GCC detecta recursão e pára graciosamente este lado (4.8 e acima parecem estar bem)
Alec Teal
2
template <typename T> auto foo(T t) -> decltype(foo(t)); decltype(foo(0)) x;é um pouco mais curto.
Casey
2
@hvd Parece estar explorando um bug no GCC. O clang captura o uso incorreto - do qual suponho que você já esteja ciente -, mas faz meu GCC transmitir quase 2 megabytes de mensagens de erro.
Casey
4
+1 para estouro de meta stack : p
Aschratt
45

Bash (Alerta de Perigo)

while true
do 
  mkdir x
  cd x
done

Estritamente falando, isso não empilhará diretamente o estouro, mas gera o que pode ser rotulado como " uma situação persistente de geração de excesso de pilha ": quando você executa isso até o disco ficar cheio e deseja remover a bagunça com "rm -rf x ", esse é atingido.

Porém, isso não acontece em todos os sistemas. Alguns são mais robustos que outros.

Grande perigo AVISO:

alguns sistemas lidam com isso muito mal e você pode ter muita dificuldade em limpar (porque "rm -rf" pode ter um problema de recusa). Pode ser necessário escrever um script semelhante para a limpeza.

É melhor tentar isso em uma VM temporária, se não tiver certeza.

PS: o mesmo se aplica, é claro, se programado ou feito em um script em lote.
PPS: pode ser interessante receber um comentário de você, como seu sistema específico se comporta ...

blabla999
fonte
como escrevi: em muitos sistemas, o rm -rf tenta ser desativado e é atingido por um (talvez não mais hoje em dia, com espaço de endereço de 64 bits - mas em máquinas menores com uma pilha menor). Obviamente, também pode haver implementações "rm", que o fazem de maneira diferente ...
blabla999
2
Parece-me que algo como while cd x; do :; done; cd ..; while rmdir x; cd ..; done;deveria cuidar disso.
Blacklight Shining
Você está absolutamente certo (foi isso que eu quis dizer com "script semelhante para limpeza"). No entanto, quando seu disco (ou cota) estiver cheio, você poderá ter dificuldades para fazer o login posteriormente, pois alguns sistemas costumavam lidar mal com essa situação. Você precisará entrar como root e fazer o script (o que é fácil para os PCs de hoje, mas costumava ser difícil nos tempos antigos, quando os computadores eram usados ​​por mais de um usuário).
blabla999
engraçado, isso fica mais votos do que a magia Smalltalk abaixo (nunca pensei que!)
blabla999
alguém tentou isso no Windows?
Sarge Borsch
43

Java

Um bom dos Java Puzzlers . O que é impresso?

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

Na verdade, ele falha com um StackOverflowError.

A exceção no construtor é apenas um arenque vermelho. Isto é o que o livro tem a dizer sobre isso:

Quando você invoca um construtor, os inicializadores da variável de instância são executados antes do corpo do construtor . Nesse caso, o inicializador para a variável internalInstancechama o construtor recursivamente. Esse construtor, por sua vez, inicializa seu próprio internalInstancecampo invocando o construtor Relutante novamente e assim por diante, ad infinitum. Essas invocações recursivas causam um StackOverflowErrorantes que o corpo do construtor tenha a chance de executar. Como StackOverflowErroré um subtipo, em Errorvez de Exception, a cláusula maincatch não a captura.

ntoskrnl
fonte
41

Látex

\end\end

A pilha de entrada transborda porque \endse expande repetidamente em um loop infinito, conforme explicado aqui .

O TeX falha com um TeX capacity exceeded, sorry [input stack size=5000]ou similar.

bwDraco
fonte
1
Eu estava esperando um problema de TeX / LaTeX. Essas coisas são mais chocantes do que a maioria - geralmente, esqueci que o que estou fazendo conta como programação quando, de repente, consegui escrever algo que é infinitamente recursivo.
Ernir
1
Veja também codegolf.stackexchange.com/questions/9359/…
bwDraco 2/14
1
"Capacidade do TeX excedida, desculpe" O TeX é tão educado quando falha.
Alex A.
40

BF

Eventualmente, ela excederá a pilha, depende apenas de quanto tempo o intérprete faz a pilha ...

+[>+]
Timtech
fonte
5
Esse código me lembra o jogo da vida.
Adam Arold
10
A rigor, não há pilha no cérebro.
fejesjoco
6
@fejesjoco Tape, se desejar.
Timtech
32

C #, em tempo de compilação

Existem várias maneiras de fazer com que o compilador Microsoft C # expanda sua pilha; sempre que você vir um erro "a expressão é muito complexa para compilar" no compilador C #, quase certamente porque a pilha explodiu.

O analisador é descendente recursivo, portanto, qualquer estrutura de linguagem suficientemente profundamente aninhada explodirá a pilha:

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

O analisador de expressão é bastante inteligente em eliminar recursões no lado comumente recorrente. Usualmente:

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

que constrói uma árvore de análise imensamente profunda, não explodirá a pilha. Mas se você forçar a recursão a acontecer do outro lado:

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

então a pilha pode ser explodida.

Eles têm a propriedade deselegante de que o programa é muito grande. Também é possível fazer o analisador semântico entrar em recursões ilimitadas com um programa pequeno, porque não é inteligente o suficiente para remover certos ciclos ímpares no sistema de tipos. (Roslyn pode melhorar isso.)

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? 

Descrevo por que essa análise entra em uma recursão infinita aqui:

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

e para muitos outros exemplos interessantes, você deve ler este artigo:

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

Eric Lippert
fonte
2
A mensagem de erro exata é fatal error CS1647: An expression is too long or complex to compile near (code). A documentação para esta mensagem de erro está aqui e é exatamente como você diz: "Houve um estouro de pilha no compilador que processava seu código".
bwDraco
32

Na Internet (usada por bilhões de pessoas / dia)

Redirects, HTTP status code: 301

Por exemplo, no site de suporte da Dell (sem ofensa, desculpe Dell):

Se você remover o TAG de suporte do URL, ele será redirecionado infinitamente . No URL a seguir, ###### é qualquer TAG de suporte.

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

Eu acredito que é equivalente a um estouro de pilha.

codeSetter
fonte
5
boa maneira :) firefox diz que ele redireciona para que o pedido não pode ser resolvido, que é a rquivalent stackoverflow :)
masterX244
3
Observe que os redirecionamentos não são infinitos - se você clicar em "Tentar novamente", ele adiciona mais alguns /Errors/ao URL e pára depois de receber HTTP 400 Bad Request. Mas isso pode gerar um estouro de pilha melhor do que um redirecionamento infinito.
Nandhp
@ nandhp, eu concordo, mas se você pensa em um navegador do terceiro mundo (não moderno, IE etc.), eles não têm idéia dessa situação.
codSetter
2
Aqui está como o Wget responde aqui: 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 23/02
31

PHP

Um stackoverflow feito apenas com elementos de loop.

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

Explicação (passe o mouse para ver o spoiler):

O programa irá falhar quando o intérprete tentar coletar a $tmpmatriz de lixo (quando for reatribuir $tmpaqui). Só porque a matriz é muito profunda (auto-referência) e, em seguida, o coletor de lixo termina em uma recursão.

bwoebi
fonte
16
O GC do PHP não pode detectar estruturas auto-referenciais? Realmente?! Ai. Isso é mau.
Peter C
1
Sim, mas não é perfeito. Há alguma razão while (1) { $a = array(&$a); }ou algo similar apenas atinge o limite de memória ...
bwoebi
Sim, acho que é a mesma razão pela qual o PHP também não está funcionando corretamente em relação ao desenvolvimento orientado a objetos; (abstração, herança etc.) #
pythonian29033
1
@ pythonian29033 gostaria de elaborar?
precisa saber é o seguinte
30

Java

Fiz exatamente o oposto - um programa que obviamente deveria gerar um erro de estouro de pilha, mas não o faz.

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

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

Dica: o programa é executado em O (2 n tempo), e n é o tamanho da pilha (geralmente 1024).

De Java Puzzlers # 45:

Vamos supor que nossa máquina possa executar 10 10 chamadas por segundo e gerar 10 10 exceções por segundo, o que é bastante generoso pelos padrões atuais. Sob essas premissas, o programa será encerrado em cerca de 1,7 × 10 291 anos. Para colocar isso em perspectiva, a vida útil do nosso sol é estimada em 10 a 10 anos, por isso é uma aposta segura que nenhum de nós estará por perto para ver esse programa terminar. Embora não seja um loop infinito, pode muito bem ser.

ntoskrnl
fonte
3
Recursão óbvia ... não muito interessante.
197 Kami
5
@Kami Você já tentou? Você realmente recebeu um StackOverflowError? Parece óbvio, mas não é.
Ntskrnl
Só porque uma exceção é capturada não significa que nunca foi lançada. Este programa lança uma exceção de estouro de pilha após o tempo O (n)
CodesInChaos
1
@CodesInChaos Ok, então verifiquei isso duas vezes, e a versão correta usa finallye não catch, e o tempo de execução é O (2 ^ n). Resposta atualizada.
Ntskrnl 17/02
@ntorkrnl 1 pessoa marcou com +1; btw já conseguiu o compilador para stackoverflow (o compilador é executado dentro da VM, também fyi)
masterX244
30

C #

Primeiro post, por favor, vá com calma comigo.

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

Isso simplesmente cria um rastreamento de pilha, pega o quadro superior (que será nossa última chamada Main()), obtém o método e o invoca.

BenM
fonte
boa maneira de auto-invocação, o que não é tão óbvio sem explicar; Marcou com +1
masterX244
Você deve comentar esse código com "o que poderia dar errado?": P
Spaceman
26

Java

  • No Java 5, printStackTrace() insere um loop infinito.
  • No Java 6, printStackTrace()lançaStackOverflowError .
  • Nos Java 7 e 8, foi corrigido.

O mais louco é que, no Java 5 e 6, ele não vem do código do usuário, acontece no código do JDK. Ninguém suspeita razoável que printStackTrace()possa ser perigoso de executar.

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
fonte
7
sim; stackoverflows do nada são o melhor amigo para codetrolling e uma séria dor de cabeça na caça em
masterX244
2
Se alguém se perguntar, código equivalente em C # causa um loop infinito. Mas a InnerExceptionpropriedade é somente leitura e é definida no construtor; portanto, é necessária reflexão para causar isso.
Athari
17

JavaScript: mutação de função iterativa não recursiva

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

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

Não há recursão aqui. fserá repetidamente repetido com mais e mais argumentos até que possa estourar a pilha em uma única chamada de função. A console.logpeça é opcional caso você queira ver quantos argumentos são necessários para isso. Ele também garante que mecanismos JS inteligentes não otimizarão isso.

Versão Code-golf no CoffeeScript, 28 caracteres:

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

C # com uma falha épica

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

O jeito que ele falha é épico, me surpreendeu completamente:

enter image description here

É apenas um quadro de uma série aparentemente infinita de imagens estranhas.

Isso deve ser a coisa mais estranha de todas. Alguém pode explicar? Aparentemente, a quantidade cada vez maior de espaços usados ​​para indentação faz com que esses blocos brancos apareçam. Isso acontece em um Win7 Enterprise x64 com o .NET 4.5.

Eu ainda não vi o fim disso ainda. Se você substituir System.Console.Outcom System.IO.Stream.Null, morre muito rápido.

A explicação é bem simples. Eu faço uma classe que possui uma única propriedade e ela sempre retorna uma nova instância do seu tipo de contenção. Portanto, é uma hierarquia de objetos infinitamente profunda. Agora precisamos de algo que tente ler isso. É aí que eu uso o XmlSerializer, o que faz exatamente isso. E, aparentemente, ele usa recursão.

fejesjoco
fonte
sim; ftw de serialização: P; mas mais engraçado é que quando ythe peculiaridade é completamente fora do seu código como como snakeyaml recebe os atributos e uma classe retorna-se de uma forma que endlesly recurses
masterX244
1
Bem, o transbordamento acontecer fora do meu código, mas a verdadeira razão eu postei isso é por causa do efeito colateral inesperado no :-) consola
fejesjoco
Eu acho que os bits brancos devem ter a ver com o .Net 4.5 ou como o seu ambiente está configurado, porque usando o .Net 4 (mesmo quando executado pelo cmd) eu só recebo espaços, sem blocos brancos. Meu palpite é que a versão Win7 Enterprise do cmd ou o emulador de console .Net 4.5 trata uma certa combinação de caracteres como 'alterar o plano de fundo da cor do console'.
Pharap
Eu não posso reproduzi-lo com o .NET 4.5 no Win7 x64 Professional com o Aero ativado #
18714 Ray
Também não pode se reproduzir. .NET 4.5, Windows 8.1 Pro.
bwDraco
13

bater

_(){ _;};_

Enquanto muitos podem reconhecer que a recursão é óbvia , mas parece bonita. Não?

Após a execução, você tem a garantia de ver:

Segmentation fault (core dumped)
devnull
fonte
5
este é mais bonita e pior:_(){_|_;};_
RSFalcon7
1
@ Alerta de Forkbomb RSFalcon7! (Também, não é preciso um espaço após o {ser sintaticamente correto?)
Blacklight Brilhante
3
Tente também:(){:|:;}:
Tarek Eldeeb 17/02
14
Parece um emoticon assustador e mutante.
Tim Seguine
8
Emoticons Bash: comedor de rostos, matador de pilhas.
NobleUplift
12

Haskell

(triste, mas verdadeiro até pelo menos ghc-7.6, embora com O1ou mais otimize o problema)

main = print $ sum [1 .. 100000000]
deixou de girar contra-relógio
fonte
não deveria fazer uma otimização de chamada de cauda automaticamente (em suma)?
blabla999
2
@ blabla999: chamadas de cauda não são tão relevantes em Haskell, é principalmente um acúmulo de thunk devido à preguiça secreta que está causando esses problemas. Nesse caso, o problema é que sumé implementado em termos de foldl, que usa chamadas de cauda, ​​mas porque não avalia o acumulador estritamente apenas produz uma pilha de thunks do tamanho da lista original. O problema desaparece ao mudar para foldl' (+), que avalia estritamente e, portanto, retorna um WHN em sua chamada final. Ou, como eu disse, se você ativar as otimizações do GHC!
deixou de girar contra-
aah - interessante, então, se ninguém esperaria pelo thunk (isto é, deixando de fora a impressão), o coletor de lixo os recolheria (da frente para trás)?
blabla999
1
BTW, nada disso é realmente especificado pelo padrão Haskell: é apenas necessário que a avaliação não seja rigorosa , ou seja, um cálculo não-determinante não será bloqueado para sempre se o resultado não for totalmente necessário. Quanto tempo realmente bloqueia depende da implementação; no GHC preguiçoso padrão, ele não bloqueia até que você solicite o resultado.
deixou de girar contra-
2
haskell é legal.
blabla999
12

Conversa fiada

Isso cria um novo método em tempo real, que
  cria um novo método em tempo real, que
    cria um novo método em tempo real, que
      ...
    ...
  ..
e depois transfere para ele.

Um pouco mais de tempero vem do estresse da memória da pilha E da memória da pilha ao mesmo tempo, criando um nome de método cada vez mais longo e um grande número como receptor, quando caímos no buraco ... (mas a recursão nos atinge primeiro )

compilar em número inteiro:

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.

depois pule, avaliando "2 downTheRabbitHole"...
... depois de um tempo, você terminará em um depurador, mostrando uma RecursionException.

Então você precisa limpar toda a bagunça (o SmallInteger e o LargeInteger agora têm muito código do país das maravilhas):

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

ou então, passe algum tempo no navegador, removendo o país das maravilhas de alice.

Aqui estão alguns dos principais traços:

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: o "withoutUpdatingChangesFile:" foi adicionado para evitar a necessidade de limpar o arquivo de log de alterações persistente do Smalltalk posteriormente.

PPS: obrigado pelo desafio: pensar em algo novo e inovador foi divertido!

PPPS: Eu gosto de observar que algumas versões / dialetos do Smalltalk copiam quadros de pilha transbordantes para o heap - portanto, eles podem ter uma situação de falta de memória.

blabla999
fonte
2
LOL +1 para que a magia negra em sua forma pura
masterX244
Se eu encontrar algum tempo, eu pode "melhorar" usando um método anônimo de uma classe anônima, para fazer o buraco de coelho mais escura sempre ...
blabla999
12

C #

Realmente grande struct, sem recursão, C # puro, código não seguro.

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

como um kicker ele trava as janelas de depuração afirmando que {Cannot evaluate expression because the current thread is in a stack overflow state.}


E a versão genérica (obrigado pela sugestão 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
fonte
Precisa de mais códigos genéricos methinks: P
NPSF3000 20/02
A aparência seria recursiva, mas é possível com argumentos de tipo aninhado.
ja72
1
Não vi sua resposta em C # struct antes de postar a minha. Eu ainda tenho uma abordagem um pouco diferente, então talvez possamos deixá-los coexistir.
Thomas Weller
11

C #

Implementação defeituosa do ==operador substituído :

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

Pode-se dizer que é óbvio que se operator==chama usando o ==operador, mas você geralmente não pensa assim ==, por isso é fácil cair nessa armadilha.

Sebastian Negraszus
fonte
11

Iniciando a resposta usando o SnakeYAML

class A
{

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

Editar: ungolfed it

Cabe ao leitor descobrir como isso funciona: P (dica: stackoverflow.com)

A propósito: a recursão é feita dinamicamente pelo SnakeYAML (você notará se souber como ele detecta os campos que serializa e depois olhará Point código-fonte)

Edit: dizendo como isso funciona:

O SnakeYAML procura um par de getXXXe setXXXmthod com o mesmo nome XXXe o tipo de retorno do getter é o mesmo que o parâmetro do setter; e surpreendentemente a Pointclasse tem um Point getLocation()e void setLocation(Point P)que se retorna; O SnakeYAML não percebe e se repete nessa peculiaridade e StackOverflows. Descobriu aquele ao trabalhar com eles dentro de um HashMape perguntando sobre stackoverflow.com.

masterX244
fonte
10

C #

getter de propriedade implementado incorretamente

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

static void Main()
{
   int p = new C().P;
}
Alberto
fonte
14
NA MINHA HUMILDE OPINIÃO. Este é um exemplo clássico de uma recursão óbvio ... (e assim não é válido)
Ole Albers
2
Bem, só é óbvio uma vez que você fez isso uma vez e descobriu que os getters de C # não funcionam da maneira que você pensou que eles fizeram. Afinal, esse código é uma declaração de variável de membro, por que não criar uma variável de membro real?
meustrus
2
Isso não passa de uma maneira complicada de fazerstatic void Main() { Main(); }
Jodrell
5
@Jodrell Você não escreveria recursivamente Main()acidentalmente. Mas é bastante simples escrever uma propriedade recursiva acidentalmente e depois ficar confuso com o estouro da pilha.
svick
1
Isso seria útil para alguém pegar C #.
2rs2ts