fundo
Já temos um desafio em jogar o SIGSEGV , por que não um desafio em jogar o SIGILL?
O que é o SIGILL?
SIGILL é o sinal para uma instrução ilegal no processador, o que acontece muito raramente. A ação padrão depois de receber o SIGILL está finalizando o programa e gravando um dump principal. O ID do sinal do SIGILL é 4. Você encontra o SIGILL muito raramente, e eu não tenho absolutamente nenhuma idéia de como gerá-lo no seu código, exceto via sudo kill -s 4 <pid>
.
Regras
Você terá raiz nos seus programas, mas se não quiser, por qualquer motivo, também poderá usar um usuário normal. Estou em um computador Linux com código de idioma alemão e não sei o texto em inglês que é exibido após a captura do SIGILL, mas acho que é algo como 'Instrução ilegal'. O programa mais curto que lança SIGILL vence.
raise(SIGILL)
?Illegal instruction (core dumped)
.Respostas:
Assembler do PDP-11 (UNIX sexta edição), 1 byte
A instrução 9 não é válida no PDP-11 (em octal, seria
000011
, o que não aparece na lista de instruções (PDF)). O montador PDP-11 que acompanha o UNIX Sexta Edição aparentemente reproduz tudo o que não entende diretamente no arquivo; nesse caso, 9 é um número e, portanto, gera uma instrução literal 9. Também possui a propriedade ímpar (incomum nas linguagens assembly atualmente) que os arquivos começam a ser executados desde o início, portanto, não precisamos de nenhuma declaração para fazer o programa. trabalhos.Você pode testar o programa usando este emulador , embora tenha que lutar um pouco com ele para inserir o programa.
Veja como as coisas terminam quando você descobre como usar o sistema de arquivos, o editor, o terminal e coisas semelhantes que você pensou que já sabia usar:
Confirmei com a documentação que este é um
SIGILL
sinal genuíno (e até tinha o mesmo número de sinal, 4, todo o caminho naquela época!)fonte
a.out
possui vários bytes, na verdade (a9
instrução é compilada em dois bytes, e o assembler também adiciona um cabeçalho e rodapé para tornar o programa executável). Por isso escrevi o programa em linguagem assembly, não em código de máquina. O programa em linguagem assembly possui apenas um byte e é compilado em um programa com mais bytes em; esse é um problema de código-golfe (minimize o tamanho da fonte), não um problema de codificação de tamanho (minimize o tamanho do executável); portanto, é importante o tamanho de 1 byte da fonte.C (x86_64, tcc ), 7 bytes
Inspirado por esta resposta .
Experimente online!
Como funciona
A montagem gerada se parece com isso.
Observe que o TCC não coloca a "função" definida em um segmento de dados .
Após a compilação, _start apontará para main como de costume. Quando o programa resultante é executado, ele espera o código em main e localiza o inteiro little-endian (!) De 32 bits 6 , codificado como 0x06 0x00 0x00 0x00 . O primeiro byte - 0x06 - é um código de operação inválido, portanto o programa termina com o SIGILL .
C (x86_64, gcc ), 13 bytes
Experimente online!
Como funciona
Sem o modificador const , o assembly gerado se parece com isso.
O vinculador do GCC trata a última linha como uma dica de que o objeto gerado não requer uma pilha executável. Como main é explicitamente colocado em uma seção de dados , o código de operação que ele contém não é executável; portanto, o programa termina com SIGSEGV (falha de segmentação).
Remover a segunda ou a última linha fará com que o executável gerado funcione como pretendido. A última linha pode ser ignorada com o sinalizador do compilador
-zexecstack
( Experimente online! ), Mas isso custa 12 bytes .Uma alternativa mais curta é declarar main com o modificador const , resultando na seguinte montagem.
Isso funciona sem nenhum sinalizador de compilador. Observe que
main=6;
escreveria a "função" definida nos dados , mas o modificador const fará com que o GCC a escreva em rodata , que (pelo menos na minha plataforma) tem permissão para conter código.fonte
main
é um 6 e tenta chamá-lo (o que eu acho que faria desistir e tentar a instrução)?main
não ser uma função, mas somente se você ativar os avisos (faça-Wall
ou o-pedantic
fará)..rodata
seção dentro do segmento de texto do executável, e espero que este seja o caso em praticamente qualquer plataforma. (O carregador de programas do kernel se preocupa apenas com segmentos, não seções).06
é apenas uma instrução inválida em x86-64. No modo de 32 bits, éPUSH ES
, portanto, esta resposta funciona apenas com compiladores padrão-m64
. Consulte ref.x86asm.net/coder.html#x06 . A única sequência de bytes que está garantido para decodificar como uma instrução ilegal em todas as futuras CPUs x86 é o 2 byte UD2 :0F 0B
. Qualquer outra coisa pode ser algum prefixo futuro ou codificação de instruções. Ainda assim, com o voto positivo de uma maneira interessante de fazer com que um compilador C cole ummain
rótulo em alguns bytes!Rápido, 5 bytes
Acesse o índice 0 de uma matriz vazia. Isso chama
fatalError()
, que imprime uma mensagem de erro e trava com um SIGILL. Você pode tentar aqui .fonte
fatalError()
falha intencionalmente ao executarud2
. Por que eles escolheram fazer isso eu não sei, mas talvez eles achem que a mensagem de erro "Instrução ilegal" fazia sentido porque o programa fez algo ilegal.nil!
, mas o compilador não conseguiu inferir o tipo de resultado. (Além disso, oi JAL!)GNU C, 25 bytes
O GNU C (um dialeto específico de C com extensões) contém uma instrução para travar o programa intencionalmente. A implementação exata varia de versão para versão, mas geralmente os desenvolvedores tentam implementar a falha o mais barato possível, o que normalmente envolve o uso de uma instrução ilegal.
A versão específica que eu costumava testar é
gcc (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0
; no entanto, esse programa causa um SIGILL em uma ampla variedade de plataformas e, portanto, é bastante portátil. Além disso, ele faz isso realmente executando uma instrução ilegal. Aqui está o código do assembly que o acima compila com as configurações de otimização padrão:ud2
é uma instrução que a Intel garante sempre permanecerá indefinida.fonte
main(){asm("ud2");}
00 00 0f 0b
é a linguagem de máquina paraud2
...00
bytes; eles não fazem parte do código de máquina do UD2. BTW, como eu comentei na resposta de Dennis , há instruções ilegais de um byte no x86-64 por enquanto, mas não é garantido que elas permaneçam assim.00 00
decodifica o mesmo em x86-64 (asadd [rax], al
).00 00 0f 0b
usualmente SIGSEGV antes da SIGILL, a menos que você tenha um ponteiro gravávelrax
.C (x86_64), 11, 30, 34 ou 34 + 15 = 49 bytes
Enviei algumas soluções que usam funções de biblioteca para executar de
SIGILL
várias maneiras, mas é indiscutível que isso é trapaça, pois a função de biblioteca resolve o problema. Aqui está uma série de soluções que não usam funções de biblioteca e fazem suposições variadas sobre onde o sistema operacional está disposto a permitir que você execute código não executável. (As constantes aqui são escolhidas para x86_64, mas você pode alterá-las para obter soluções funcionais para a maioria dos outros processadores que possuem instruções ilegais.)06
é o byte de código de máquina com o número mais baixo que não corresponde a uma instrução definida em um processador x86_64. Então, tudo o que precisamos fazer é executá-lo. (Como alternativa,2F
também é indefinido e corresponde a um único caractere ASCII imprimível.) Não é garantido que nenhum deles seja sempre indefinido, mas não está definido atualmente.O primeiro programa aqui é executado a
2F
partir do segmento de dados somente leitura. A maioria dos vinculadores não é capaz de produzir um salto de trabalho.text
para.rodata
(ou o equivalente do sistema operacional), pois não é algo que jamais seria útil em um programa segmentado corretamente; Ainda não encontrei um sistema operacional no qual isso funcione. Você também teria que permitir o fato de que muitos compiladores desejam que a cadeia em questão seja uma cadeia ampla, o que exigiria um adicionalL
; Estou assumindo que qualquer sistema operacional em que isso funcione tenha uma visão bastante desatualizada das coisas e, portanto, esteja criando um padrão pré-C94 por padrão. É possível que não haja nenhum lugar onde este programa funcione, mas também é possível que haja algum lugar em que esse programa funcione, e, portanto, estou listando-o nesta coleção de respostas potenciais mais duvidosas para menos duvidosas. (Depois de postar esta resposta, Dennis também mencionou a possibilidademain[]={6}
no bate-papo, que tem o mesmo tamanho e que não apresenta problemas com a largura dos caracteres, e até sugeriu o potencial paramain=6
; não posso razoavelmente reivindicar essas respostas como meu, como eu não pensei neles.)O segundo programa aqui é executado a
06
partir do segmento de dados de leitura e gravação. Na maioria dos sistemas operacionais, isso causa uma falha de segmentação, porque os segmentos de dados graváveis são considerados uma falha de projeto ruim que torna as explorações mais prováveis. Porém, nem sempre foi esse o caso, por isso provavelmente funciona em uma versão suficientemente antiga do Linux, mas não posso testá-lo facilmente.O terceiro programa é executado a
06
partir da pilha. Novamente, isso causa uma falha de segmentação atualmente, porque a pilha é normalmente classificada como não gravável por motivos de segurança. A documentação do vinculador que eu vi implica fortemente que costumava ser legal executar a partir da pilha (ao contrário dos dois casos anteriores, isso é útil ocasionalmente), portanto, embora eu não possa testá-lo, tenho certeza de que há alguns versão do Linux (e provavelmente outros sistemas operacionais) em que isso funciona.Finalmente, se você der
-Wl,-z,execstack
(penalidade de 15 bytes) agcc
(se estiver usando o GNUld
como parte do back-end), ele desativará explicitamente a proteção da pilha executável, permitindo que o terceiro programa funcione e emita um sinal de operação ilegal conforme o esperado. Eu tenho testado e verificado esta versão 49-byte para o trabalho. (Dennis menciona no bate-papo que essa opção aparentemente funcionamain=6
, o que daria uma pontuação de 6 + 15. Estou surpreso que isso funcione, já que os 6 não estão descaradamente na pilha; a opção de link aparentemente faz mais do que o nome sugere.)fonte
const main=6;
funciona, assim como várias variações. Esse vinculador (que eu suspeito que também seja seu vinculador) é capaz de gerar um salto de.text
para.rodata
; o problema que você estavaconst
enfrentando é que, sem , você está pulando no segmento de dados graváveis (.data
), que não é executável no hardware moderno. Teria funcionado no x86 mais antigo, onde o hardware de proteção de memória não podia marcar as páginas como legíveis, mas não executáveis.main
é necessário que seja uma função (§5.1.2.2.1) - não sei por que o gcc considera que a declaraçãomain
como um objeto de dados merece apenas um aviso e apenas-pedantic
na linha de comando. Alguém no início dos anos 90 talvez pensasse que ninguém faria isso por acidente, mas não é uma coisa útil a se fazer de propósito, exceto nesse tipo de jogo.main[]="/"
pular para o segmento de dados somente leitura, porque os literais de seqüência de caracteres entram em rodata. Você foi pego pela diferença entrechar *foo = "..."
echar foo[] = "..."
.char *foo = "..."
é um açúcar sintático paraconst char __inaccessible1[] = "..."; char *foo = (char *)&__inaccessible1[0];
, portanto, a string literal entra em rodata efoo
é uma variável global gravável separada que aponta para ela. Comchar foo[] = "..."
, no entanto, toda a matriz entra no segmento de dados graváveis.GNU como (x86_64), 3 bytes
$ xxd sigill.S
$ as --64 sigill.S -o sigill.o; ld -S sigill.o -o sigill
$ ./sigill
$ objdump -d sigill
fonte
asm-link
) para programas de brinquedo de arquivo único criaria um executável a partir dessa fonte da mesma maneira, já que old
padrão é o ponto de entrada para o início do segmento de texto ou algo parecido. Eu só não pensar em ir para o tamanho fonte asm em um presente: PBash no Raspbian no QEMU, 4 (1?) Bytes
Não é o meu trabalho. Eu apenas relato o trabalho de outro. Não estou nem em posição de testar a alegação. Como uma parte crucial desse desafio parece estar em um ambiente em que esse sinal será gerado e capturado, não incluo o tamanho do QEMU, Raspbian ou bash.
Em 27 de fevereiro de 2013 às 20:49, o usuário emlhalac relatou " Obtendo 'instruções ilegais' ao tentar chroot " nos fóruns do Raspberry Pi.
produzindo
Eu imagino que comandos muito mais curtos produzirão essa saída, por exemplo
tr
,.EDIT: Com base no comentário do @ fluffy , reduziu o limite inferior conjecturado no comprimento da entrada para "1?".
fonte
[
comando venceria. :)x86 arquivo COM do MS-DOS, 2 bytes
EDIT: Como indicado nos comentários, o próprio DOS não interceptará a exceção da CPU e simplesmente travará (não apenas o aplicativo, todo o sistema operacional). A execução em um sistema operacional baseado em NT de 32 bits, como o Windows XP, realmente dispara um sinal de instrução ilegal.
A partir da documentação :
O que é bastante auto-explicativo. Salve como um arquivo .com e
execute qualquer emulador doDOS. Os emuladores do DOS simplesmente falham. Execute no Windows XP, Vista ou 7 de 32 bits.fonte
#UD
armadilha. (Além disso, eu decidi realmente testá-lo, e ele apareceu para jogar meu emulador DOS em um loop infinito.)C (Windows de 32 bits), 34 bytes
Isso funciona apenas se compilar sem otimizações (caso contrário, o código ilegal na
f
função é "otimizado").A desmontagem da
main
função fica assim:Podemos ver que ele usa uma
push
instrução com um valor literal0b0f
(little-endian, para que seus bytes sejam trocados). Acall
instrução envia um endereço de retorno (da...
instrução), que está situado na pilha perto do parâmetro da função. Usando um[-1]
deslocamento, a função substitui o endereço de retorno para que aponte 9 bytes anteriormente, onde0f 0b
estão os bytes .Esses bytes causam uma exceção de "instrução indefinida", conforme projetado.
fonte
Java,
504324 bytesEste é um
java.util.function.Consumer<Runtime>
1 cujo comando é roubado da resposta do fluffy . Funciona porque você deve chamá-lo comowhateverNameYouGiveIt.accept(Runtime.getRuntime())
!Observe que isso criará um novo processo e fará com que ele jogue um SIGILL em vez de lançar um SIGILL.
1 - Tecnicamente, também pode ser um
java.util.function.Function<Runtime, Process>
porqueRuntime#exec(String)
retorna ajava.lang.Process
que pode ser usado para controlar o processo que você acabou de criar executando um comando shell.Para fazer algo mais impressionante em uma linguagem tão detalhada, aqui está um bônus de
7260 e48 bytes:Este é outro
Consumer<Runtime>
que passa por TODOS os processos (inclusive ele próprio), fazendo com que cada um deles jogue um SIGILL. Melhor suporte para um acidente violento.E outro bônus (a
Consumer<ANYTHING_GOES>
), que pelo menos finge lançar um SIGILL em 20 bytes:fonte
Perl, 9 bytes
Simplesmente chama a função de biblioteca apropriada para sinalizar um processo e obtém o programa para sinalizar a si próprio
SIGILL
. Nenhuma instrução ilegal real está envolvida aqui, mas produz o resultado apropriado. (Eu acho que isso torna o desafio razoavelmente barato, mas se algo for permitido, essa é a brecha que você usaria ...)fonte
+
. :)+
. Depois de jogar golfe por um tempo, eles+
ocasionalmente se exibem. Eventualmente, eles escreveram programas suficientes onde precisavam evitar espaços em branco por algum motivo ou outro que+
se tornasse hábito. (Ele também analisa a menos ambígua, porque funciona em torno provocando o caso especial no analisador de parênteses.)Linguagem de montagem unificada (UAL) do ARM, 3 bytes
Por exemplo:
Após a execução
nop
, o processador interpreta a.ARM.attributes
seção como código e encontra uma instrução ilegal em algum lugar:Testado em um Raspberry Pi 3.
fonte
Microsoft C (Visual Studio 2005 em diante), 16 bytes
Não posso testar isso facilmente, mas de acordo com a documentação, ele deve produzir uma instrução ilegal, intencionalmente, tentando executar uma instrução somente do kernel de um programa no modo de usuário. (Observe que, como a instrução ilegal trava o programa, não precisamos tentar retornar
main
, o que significa que essamain
função no estilo K&R é válida. O Visual Studio nunca tendo migrado do C89 normalmente é uma coisa ruim, mas veio útil aqui.)fonte
Ruby, 13 bytes
Eu acho que é seguro assumir que estamos executando isso a partir de um shell * nix. Os literais de backtick executam o comando shell fornecido.
$$
é o processo Ruby em execução e#
é para interpolação de strings.Sem chamar o shell diretamente:
Ruby, 17 bytes
fonte
Qualquer shell (sh, bash, csh, etc.), qualquer POSIX (10 bytes)
Resposta trivial, mas eu não tinha visto ninguém postá-lo.
Apenas envia o SIGILL para o processo atual. Exemplo de saída no OSX:
fonte
kill -4 1
se a questão não é específico sobre qual programa lança a SIGILLYou will have root in your programs
- bata um byte na sua resposta e remova a pergunta um pouco ao mesmo tempo: D. Bônus: Você começa a matarinit
init
na verdade , é imune a sinais que não solicitou especificamente para receber, mesmo com root. Talvez você possa solucionar isso usando um sistema operacional POSIX diferente.kill -4 2
então: DCódigo de máquina ELF + x86, 45 bytes
Este deve ser o menor programa executável em uma máquina Unix que lança o SIGILL (devido ao Linux não reconhecer o executável, se for menor).
Compile com
nasm -f bin -o a.out tiny_sigill.asm
, testado em uma máquina virtual x64.Binário real de 45 bytes:
Lista de montagem (veja a fonte abaixo):
Isenção de responsabilidade: codifique o seguinte tutorial sobre como escrever o menor programa de montagem para retornar um número, mas usando o opcode ud2 em vez de mov: http://www.muppetlabs.com/~breadbox/software/tiny/teensy.html
fonte
AutoIt , 93 bytes
Usando montagem embutida flatassembler:
Quando executado no modo interativo SciTE, ele falha imediatamente. O depurador do Windows deve aparecer por uma fração de segundo. A saída do console será algo como isto:
Onde
-1073741795
está o código de erro indefinido lançado pela WinAPI. Este pode ser qualquer número negativo.Similar usando meu próprio LASM de assembler :
fonte
NASM, 25 bytes
Não sei como isso funciona, apenas no meu computador especificamente (Linux x86_64).
Compile e execute como:
fonte
ja 0
ud2
Conjunto hexagonal TI-83, 2 bytes
Executar como
Asm(prgmI)
. Executa o opcode 0xed77 ilegal. Eu conto cada par de dígitos hexadecimais como um byte.fonte
Python, 32 bytes
fonte
import os;os.kill(os.getpid(),4)
x86 .COM, 1 byte
ARPL
causa#UD
no modo de 16 bitsfonte
Shell Linux, 9 bytes
Envia
SIGILL
para o processo com PID 0. Não sei qual processo possui o PID 0, mas ele sempre existe.Experimente online!
fonte
man kill
:0 All processes in the current process group are signaled.
GNU C,
241918 bytes-4 graças a Dennis
-1 graças a ceilingcat
Experimente online! Isso pressupõe ASCII e x86_64. Ele tenta executar o código da máquina
27
, o que é ilegal.shortC ,
1054 bytesEquivalente ao código GNU C acima. Experimente online!
fonte
L"\6"
também é ilegal, assumindo x86_64.L
não é necessário.'
é 39 = 0x27 , não 0x39 .MachineCode em x86_64,
21 bytesExperimente online!
Simplesmente chama a instrução x86_64
0x07
(tetocat sugeriu 0x07 em vez de 0x27)fonte