Antecedentes
Ya tenemos un desafío sobre lanzar SIGSEGV , entonces, ¿por qué no un desafío sobre lanzar SIGILL?
¿Qué es SIGILL?
SIGILL es la señal de una instrucción ilegal en el procesador, que ocurre muy raramente. La acción predeterminada después de recibir SIGILL es terminar el programa y escribir un volcado de memoria. La identificación de la señal de SIGILL es 4. Te encuentras con SIGILL muy raramente, y no tengo ni idea de cómo generarlo en tu código, excepto a través de sudo kill -s 4 <pid>
.
Reglas
Tendrá root en sus programas, pero si no lo desea por algún motivo, también puede usar un usuario normal. Estoy en una computadora Linux con configuración regional alemana y no sé el texto en inglés que se muestra después de capturar SIGILL, pero creo que es algo así como 'Instrucción ilegal'. El programa más corto que arroja SIGILL gana.
raise(SIGILL)
?Illegal instruction (core dumped)
.Respuestas:
Ensamblador PDP-11 (Sexta edición de UNIX), 1 byte
La Instrucción 9 no es una instrucción válida en el PDP-11 (en octal, lo sería
000011
, lo que no aparece en la lista de instrucciones (PDF)). El ensamblador PDP-11 que se entrega con UNIX Sixth Edition aparentemente hace eco de todo lo que no entiende en el archivo directamente; en este caso, 9 es un número, por lo que genera una instrucción literal 9. También tiene la propiedad impar (inusual en los lenguajes de ensamblaje hoy en día) de que los archivos comienzan a ejecutarse desde el principio, por lo que no necesitamos ninguna declaración para hacer el programa trabajo.Puede probar el programa usando este emulador , aunque tendrá que luchar un poco con él para ingresar el programa.
Así es como terminan las cosas una vez que has descubierto cómo usar el sistema de archivos, el editor, el terminal y cosas similares que creías que ya sabías cómo usar:
He confirmado con la documentación que esta es una
SIGILL
señal genuina (¡e incluso tenía el mismo número de señal, 4, en todo momento!)fuente
a.out
tiene múltiples bytes, de hecho (la9
instrucción se compila en dos bytes, y el ensamblador también agrega un encabezado y pie de página para hacer que el programa sea ejecutable). Es por eso que escribí el programa en lenguaje ensamblador, no en código máquina. El programa en lenguaje ensamblador tiene solo un byte y se compila en un programa con más bytes; Este es un problema de código de golf (minimizar el tamaño de la fuente), no un problema de codificación de tamaño (minimizar el tamaño del archivo ejecutable), por lo que es el tamaño de 1 byte de la fuente lo que importa.C (x86_64, tcc ), 7 bytes
Inspirado por esta respuesta .
Pruébalo en línea!
Cómo funciona
El conjunto generado se ve así.
Tenga en cuenta que TCC no coloca la "función" definida en un segmento de datos .
Después de la compilación, _start apuntará a main como de costumbre. Cuando se ejecuta el programa resultante, espera código en main y encuentra el entero 6 de 32 bits little-endian (!) , Que está codificado como 0x06 0x00 0x00 0x00 . El primer byte, 0x06 , es un código de operación no válido, por lo que el programa termina con SIGILL .
C (x86_64, gcc ), 13 bytes
Pruébalo en línea!
Cómo funciona
Sin el modificador const , el conjunto generado se ve así.
El enlazador de GCC trata la última línea como una pista de que el objeto generado no requiere una pila ejecutable. Dado que main se coloca explícitamente en una sección de datos , el código de operación que contiene no es ejecutable, por lo que el programa termina SIGSEGV (error de segmentación).
Al eliminar la segunda o la última línea, el ejecutable generado funcionará según lo previsto. La última línea podría ignorarse con el indicador del compilador
-zexecstack
(¡ Pruébelo en línea! ), Pero esto cuesta 12 bytes .Una alternativa más corta es declarar main con el modificador const , lo que resulta en el siguiente ensamblaje.
Esto funciona sin ningún indicador del compilador. Tenga en cuenta que
main=6;
escribiría la "función" definida en los datos , pero el modificador constante hace que GCC la escriba en rodata , que (al menos en mi plataforma) puede contener código.fuente
main
es un 6 e intenta llamarlo (lo que supongo que haría que se rindiera e intentara la instrucción)?main
no ser una función, pero solo si activa las advertencias (-Wall
o-pedantic
lo hará)..rodata
sección dentro del segmento de texto del ejecutable, y espero que este sea el caso en casi cualquier plataforma. (El cargador de programas del núcleo solo se preocupa por los segmentos, no por las secciones).06
solo es una instrucción no válida en x86-64. En el modo de 32 bits, esPUSH ES
, por lo que esta respuesta solo funciona con compiladores predeterminados-m64
. Ver ref.x86asm.net/coder.html#x06 . La única secuencia de bytes que está garantizado para decodificar como una instrucción ilegal en todas las CPU x86 futuros es el octeto 2 UD2 :0F 0B
. Cualquier otra cosa podría ser algún prefijo futuro o codificación de instrucciones. Aún así, ¡voté por una forma genial de hacer que un compilador de C pegue unamain
etiqueta en algunos bytes!Rápido, 5 bytes
Índice de acceso 0 de una matriz vacía. Esto llama
fatalError()
, que imprime un mensaje de error y se bloquea con un SIGILL. Puedes probarlo aquí .fuente
fatalError()
se bloquea intencionalmente al correrud2
. No sé por qué decidieron hacer eso, pero tal vez pensaron que el mensaje de error "Instrucción ilegal" tenía sentido porque el programa hizo algo ilegal.nil!
, pero el compilador no pudo inferir el tipo de resultado. (También, hola JAL!)GNU C, 25 bytes
GNU C (un dialecto específico de C con extensiones) contiene una instrucción para bloquear el programa intencionalmente. La implementación exacta varía de una versión a otra, pero a menudo los desarrolladores intentan implementar el bloqueo lo más barato posible, lo que normalmente implica el uso de una instrucción ilegal.
La versión específica que solía probar es
gcc (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0
; sin embargo, este programa causa una SIGILL en una gama bastante amplia de plataformas y, por lo tanto, es bastante portátil. Además, lo hace mediante la ejecución de una instrucción ilegal. Aquí está el código de ensamblaje en el que se compila lo anterior con la configuración de optimización predeterminada:ud2
es una instrucción que Intel garantiza que siempre permanecerá indefinida.fuente
main(){asm("ud2");}
00 00 0f 0b
es el lenguaje de máquina paraud2
...00
bytes; no son parte del código de máquina para UD2. Por cierto, como comenté en la respuesta de Dennis , hay instrucciones ilegales de un byte en x86-64 por ahora, pero no se garantiza que permanezcan así.00 00
decodifica lo mismo en x86-64 (comoadd [rax], al
).00 00 0f 0b
generalmente SIGSEGV antes de SIGILL, a menos que tenga un puntero grabablerax
.C (x86_64), 11, 30, 34 o 34 + 15 = 49 bytes
He presentado un par de soluciones que usan funciones de biblioteca para lanzar a
SIGILL
través de varios medios, pero podría decirse que es trampa, ya que la función de biblioteca resuelve el problema. Aquí hay una gama de soluciones que no utilizan funciones de biblioteca y hacen suposiciones variables sobre dónde el sistema operativo está dispuesto a permitirle ejecutar código no ejecutable. (Las constantes aquí se eligen para x86_64, pero puede cambiarlas para obtener soluciones de trabajo para la mayoría de los otros procesadores que tienen instrucciones ilegales).06
es el byte de código máquina con el número más bajo que no corresponde a una instrucción definida en un procesador x86_64. Entonces, todo lo que tenemos que hacer es ejecutarlo. (Alternativamente,2F
también está indefinido y corresponde a un único carácter ASCII imprimible). Ninguno de estos está garantizado que siempre esté indefinido, pero no están definidos a partir de hoy.El primer programa aquí se ejecuta
2F
desde el segmento de datos de solo lectura. La mayoría de los enlazadores no son capaces de producir un salto trabajando desde.text
a.rodata
(o el equivalente en su sistema operativo) ya que no es algo que nunca sería útil en un programa correctamente segmentada; Todavía no he encontrado un sistema operativo en el que esto funcione. También debería tener en cuenta el hecho de que muchos compiladores quieren que la cadena en cuestión sea una cadena ancha, lo que requeriría un adicionalL
; Supongo que cualquier sistema operativo en el que funcione tiene una visión bastante desactualizada de las cosas y, por lo tanto, se está construyendo para un estándar anterior a C94 por defecto. Es posible que no haya ningún lugar donde funcione este programa, pero también es posible que haya algún lugar donde funcione este programa, y por lo tanto lo estoy enumerando en esta colección de posibles respuestas más dudosas a menos dudosas. (Después de publicar esta respuesta, Dennis también mencionó la posibilidadmain[]={6}
en el chat, que tiene la misma longitud y que no tiene problemas con el ancho de los caracteres, e incluso insinuó el potencial paramain=6
; No puedo reclamar razonablemente estas respuestas como mía, ya que no pensé en ellos yo mismo).El segundo programa aquí se ejecuta
06
desde el segmento de datos de lectura-escritura. En la mayoría de los sistemas operativos, esto causará una falla de segmentación, ya que los segmentos de datos grabables se consideran un defecto de diseño incorrecto que hace que las vulnerabilidades sean probables. Sin embargo, este no siempre ha sido el caso, por lo que probablemente funcione en una versión suficientemente antigua de Linux, pero no puedo probarlo fácilmente.El tercer programa se ejecuta
06
desde la pila. Nuevamente, esto causa una falla de segmentación hoy en día, porque la pila normalmente se clasifica como no escribible por razones de seguridad. La documentación del enlazador que he visto implica en gran medida que solía ser legal ejecutarlo desde la pila (a diferencia de los dos casos anteriores, hacerlo ocasionalmente es útil), así que aunque no puedo probarlo, estoy bastante seguro de que hay algunos versión de Linux (y probablemente otros sistemas operativos) en la que esto funciona.Finalmente, si le da
-Wl,-z,execstack
(penalización de 15 bytes) agcc
(si usa GNUld
como parte del backend), desactivará explícitamente la protección de la pila ejecutable, permitiendo que el tercer programa funcione y dé una señal de operación ilegal como se esperaba. Yo he probado y verificado esta versión 49 bytes para trabajar. (Dennis menciona en el chat que aparentemente esta opción funcionamain=6
, lo que daría un puntaje de 6 + 15. Estoy bastante sorprendido de que esto funcione, dado que el 6 descaradamente no está en la pila; la opción de enlace aparentemente hace más de su nombre lo sugiere.)fuente
const main=6;
funciona, al igual que varias variaciones. Este enlazador (que sospecho es también su enlazador) es capaz de generar un salto de.text
a.rodata
; El problema que tenía es que, sinconst
eso, está entrando en el segmento de datos de escritura (.data
), que no es ejecutable en el hardware moderno. Habría funcionado en versiones anteriores de x86, donde el hardware de protección de memoria no podía marcar las páginas como legibles pero no ejecutables.main
se requiere que sea una función (§5.1.2.2.1): no sé por qué gcc considera que declararmain
como un objeto de datos solo merece una advertencia, y solo con-pedantic
la línea de comando. Alguien a principios de la década de 1990 tal vez pensó que nadie lo haría por accidente, pero no es que sea algo útil a propósito, excepto para este tipo de juego.main[]="/"
a leer de nuevo, parece que esperaba saltar al segmento de datos de solo lectura, porque los literales de cadena van en rodata. Te ha sorprendido la diferencia entrechar *foo = "..."
ychar foo[] = "..."
.char *foo = "..."
es azúcar sintáctico paraconst char __inaccessible1[] = "..."; char *foo = (char *)&__inaccessible1[0];
, por lo que el literal de cadena va en rodata, yfoo
es una variable global independiente y grabable que apunta a él. Sinchar foo[] = "..."
embargo, con toda la matriz va en el segmento de datos grabables.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
fuente
asm-link
) para programas de juguete de un solo archivo compilaría un ejecutable desde esta fuente de la misma manera, ya queld
el punto de entrada predeterminado es el inicio del segmento de texto o algo así. Simplemente no pensé en buscar el tamaño de fuente asm en este: PBash en Raspbian en QEMU, 4 (1?) Bytes
No es mi trabajo Simplemente informo el trabajo de otro. Ni siquiera estoy en condiciones de probar el reclamo. Dado que una parte crucial de este desafío parece ser encontrar un entorno en el que se eleve y capture esta señal, no incluyo el tamaño de QEMU, Raspbian o bash.
El 27 de febrero de 2013, 8:49 pm, el usuario emlhalac informó " Obteniendo 'instrucciones ilegales' al intentar chroot " en el foro Raspberry Pi.
productor
Imagino comandos mucho más cortos producirán esta salida, por ejemplo,
tr
.EDITAR: Basado en el comentario de @ fluffy , redujo el límite inferior conjeturado en la longitud de entrada a "1?".
fuente
[
comando ganaría. :)Archivo COM x86 MS-DOS, 2 bytes
EDITAR: Como se señaló en los comentarios, el DOS en sí no atrapará la excepción de la CPU y simplemente se bloqueará (no solo la aplicación, todo el sistema operativo). Ejecutar en un sistema operativo basado en NT de 32 bits como Windows XP, de hecho, activará una señal de instrucción ilegal.
De la documentación :
Lo cual se explica por sí mismo. Guardar como un archivo .com y
ejecutar en cualquier emulador deDOS Los emuladores de DOS simplemente se bloquean. Ejecutar en Windows XP, Vista o 7 de 32 bits.fuente
#UD
trampa. (Además, decidí probarlo realmente, y parecía arrojar mi emulador de DOS a un bucle infinito).C (Windows de 32 bits), 34 bytes
Esto solo funciona si se compila sin optimizaciones (de lo contrario, el código ilegal en la
f
función está "optimizado").El desmontaje de la
main
función se ve así:Podemos ver que usa una
push
instrucción con un valor literal0b0f
(little-endian, por lo que sus bytes se intercambian). Lacall
instrucción empuja una dirección de retorno (de la...
instrucción), que se encuentra en la pila cerca del parámetro de la función. Al usar un[-1]
desplazamiento, la función anula la dirección de retorno, por lo que señala 9 bytes antes, donde están los bytes0f 0b
.Estos bytes provocan una excepción de "instrucción indefinida", como se diseñó.
fuente
Java,
504324 bytesEste es un
java.util.function.Consumer<Runtime>
1 cuyo comando es robado de la respuesta de fluffy . Funciona porque debes llamarlo comowhateverNameYouGiveIt.accept(Runtime.getRuntime())
!Tenga en cuenta que esto creará un nuevo proceso y hará que arroje un SIGILL en lugar de arrojar un SIGILL.
1 - Técnicamente, también puede ser un
java.util.function.Function<Runtime, Process>
porqueRuntime#exec(String)
devuelve unjava.lang.Process
que puede usarse para controlar el proceso que acaba de crear ejecutando un comando de shell.En aras de hacer algo más impresionante en un lenguaje tan detallado, aquí hay un bono
726048 bytes:Este es otro
Consumer<Runtime>
que pasa por TODOS los procesos (incluido él mismo), haciendo que cada uno de ellos arroje un SIGILL. Mejor prepararse para un choque violento.Y otra bonificación (a
Consumer<ANYTHING_GOES>
), que al menos pretende lanzar un SIGILL en 20 bytes:fuente
Perl, 9 bytes
Simplemente llama a la función de biblioteca apropiada para señalar un proceso y hace que el programa se señale a sí mismo
SIGILL
. Aquí no hay instrucciones ilegales reales, pero produce el resultado apropiado. (Creo que esto hace que el desafío sea bastante barato, pero si algo está permitido, esta es la laguna que usarías ...)fuente
+
. :)+
. Después de jugar al golf por un tiempo, lo hacen de+
vez en cuando para presumir. Eventualmente, han escrito suficientes programas donde necesitan evitar espacios en blanco por alguna razón u otra que se+
convierta en hábito. (También analiza menos ambigua, ya que funciona en torno a desencadenar el caso especial en el programa de análisis de paréntesis.)Lenguaje de ensamblador unificado ARM (UAL), 3 bytes
Por ejemplo:
Después de ejecutar
nop
, el procesador interpreta la.ARM.attributes
sección como código y encuentra una instrucción ilegal en algún lugar:Probado en una Raspberry Pi 3.
fuente
Microsoft C (Visual Studio 2005 en adelante), 16 bytes
No puedo probar esto fácilmente, pero de acuerdo con la documentación , debería producir una instrucción ilegal al intentar ejecutar intencionalmente una instrucción solo del núcleo desde un programa en modo de usuario. (Tenga en cuenta que debido a que la instrucción ilegal bloquea el programa, no tenemos que intentar regresar
main
, lo que significa que estamain
función de estilo K&R es válida. Visual Studio nunca haber pasado de C89 normalmente es algo malo, pero entró útil aquí.)fuente
Rubí, 13 bytes
Supongo que es seguro asumir que estamos ejecutando esto desde un shell * nix. Los literales de retroceso ejecutan el comando de shell dado.
$$
es el proceso de Ruby en ejecución y#
es para la interpolación de cadenas.Sin llamar al shell directamente:
Rubí, 17 bytes
fuente
Cualquier shell (sh, bash, csh, etc.), cualquier POSIX (10 bytes)
Respuesta trivial pero no había visto a nadie publicarlo.
Solo envía SIGILL al proceso actual. Ejemplo de salida en OSX:
fuente
kill -4 1
si la pregunta no es específica sobre qué programa arroja el SIGILLYou will have root in your programs
- elimine un byte de su respuesta y consulte la pregunta ligeramente al mismo tiempo: D. BONIFICACIÓN: Tienes que matarinit
init
realidad es inmune a las señales que no ha solicitado específicamente recibir, incluso con root. Sin embargo, quizás podría solucionar esto utilizando un sistema operativo POSIX diferente.kill -4 2
entonces: DCódigo de máquina ELF + x86, 45 bytes
Este debería ser el programa ejecutable más pequeño en una máquina Unix que arroja SIGILL (debido a que Linux no reconoce el ejecutable si se hace más pequeño).
Compilar con
nasm -f bin -o a.out tiny_sigill.asm
, probado en una máquina virtual x64.Binario real de 45 bytes:
Listado de ensamblaje (ver fuente a continuación):
Descargo de responsabilidad: código del siguiente tutorial sobre cómo escribir el programa de ensamblaje más pequeño para devolver un número, pero usando opcode ud2 en lugar de mov: http://www.muppetlabs.com/~breadbox/software/tiny/teensy.html
fuente
AutoIt , 93 bytes
Usando el ensamblaje en línea del ensamblador plano
Cuando se ejecuta en modo interactivo SciTE, se bloqueará inmediatamente. El depurador de Windows debería aparecer por una fracción de segundo. La salida de la consola será algo como esto:
¿Dónde
-1073741795
está el código de error indefinido lanzado por WinAPI? Este puede ser cualquier número negativo.Similar usando mi propio ensamblador LASM :
fuente
NASM, 25 bytes
No sé cómo funciona esto, solo que lo hace específicamente en mi computadora (Linux x86_64).
Compilar y ejecutar como:
fuente
ja 0
ud2
Conjunto hexagonal TI-83, 2 bytes
Corre como
Asm(prgmI)
. Ejecuta el código de operación ilegal 0xed77. Cuento cada par de dígitos hexadecimales como un byte.fuente
Python, 32 bytes
fuente
import os;os.kill(os.getpid(),4)
x86 .COM, 1 byte
ARPL
causas#UD
en modo de 16 bitsfuente
Shell de Linux, 9 bytes
Envía
SIGILL
al proceso con PID 0. No sé qué proceso tiene PID 0, pero siempre existe.Pruébalo en línea!
fuente
man kill
:0 All processes in the current process group are signaled.
GNU C,
241918 bytes-4 gracias a Dennis
-1 gracias a ceilingcat
Pruébalo en línea! Esto supone ASCII y x86_64. Intenta ejecutar el código de la máquina
27
, que ... es ilegal.shortC ,
1054 bytesEquivalente al código GNU C anterior. Pruébalo en línea!
fuente
L"\6"
también es ilegal, suponiendo x86_64.L
no es necesario.'
es 39 = 0x27 , no 0x39 .MachineCode en x86_64,
21 bytesPruébalo en línea!
Simplemente llama a la instrucción x86_64
0x07
(ceilingcat sugiere 0x07 en lugar de 0x27)fuente