├── LICENSE ├── README.md ├── arm ├── Makefile └── tiresias.s ├── by └── hello_world.by └── x86 ├── Makefile └── tiresias.asm /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017, Battelle Memorial Institute 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## tiresias 2 | : arm and x86 are turing-complete without data fetches // domas, @xoreaxeaxeax 3 | 4 | Tiresias is a brief proof of concept that ARM and x86 (and most other Von 5 | Neumann architectures) are Turing-complete without actually reading data. 6 | 7 | The project began as a thought experiment on circumventions to an arbitrary 8 | 'execute-only' memory protection. Looking at x86 for succinctness, the 9 | (somewhat uninspiring) observation is that a data fetch: 10 | 11 | ``` 12 | data: 0xdeadc0de 13 | mov eax, [data] 14 | ``` 15 | 16 | can be simulated by an instruction fetch instead: 17 | 18 | ``` 19 | mov eax, 0xdeadc0de 20 | ``` 21 | 22 | A data write, then, simply modifies the immediate used in the instruction. 23 | Memory can then be modeled as an array of 'fetch cells': 24 | 25 | ``` 26 | cell_0: 27 | mov eax, 0xdeadc0de 28 | jmp esi 29 | cell_1: 30 | mov eax, 0xfeedface 31 | jmp esi 32 | cell_2: 33 | mov eax, 0xcafed00d 34 | jmp esi 35 | ``` 36 | 37 | To read a memory cell, without a data fetch, we then: 38 | 39 | ``` 40 | mov esi, mret 41 | jmp cell_2 ; load cell 2 42 | mret: 43 | ``` 44 | 45 | And writing a data cell is simply modifying the immediate used: 46 | 47 | ``` 48 | mov [cell_1+1], 0xc0ffee ; set cell 1 49 | ``` 50 | 51 | Of course, for a proof of concept, we should actually _compute_ something, 52 | without reading data. As is typical in this situation, the BrainF#$! language 53 | is an ideal candidate for implementation - our fetch cells can be easily adapted 54 | to fit the BF memory model. Reads from the BF memory space are performed 55 | through a jmp to the BF data cell, which loads an immediate, and jmps back; 56 | writes to the BF memory space are executed as self modifying code, overwriting 57 | the immediate value loaded by the data cell. To satisfy our 'no data fetch' 58 | requirement, we implement a BrainF#$! interpreter without a stack. The I/O BF 59 | instructions (. and ,), which use an int 0x80, will, at some point, use data 60 | reads of course, but this is merely a result of the Linux implementation of I/O. 61 | 62 | The result is functioning Turing-machines on [ARM](arm/tiresias.s) and 63 | [x86](x86/tiresias.asm) capable of execution without ever touching the data 64 | read pipeline. Practical applications are nonexistent. 65 | 66 | ``` 67 | # build: 68 | $ make 69 | 70 | # verify there are no ARM data fetches: 71 | $ objdump -d tiresias | grep -e ldr -e ldm 72 | 73 | # or for x86: 74 | $ objdump -d tiresias | grep '),' 75 | $ objdump -d tiresias | grep -v mov | grep ',(' 76 | 77 | # execute a program without reading data: 78 | $ cat hello_world.by | ./tiresias 79 | Hello World! 80 | ``` 81 | 82 | ### Acknowledgements 83 | 84 | This work is inspired by the blog post 'x86 is Turing-complete with no 85 | registers'. 86 | http://mainisusuallyafunction.blogspot.com/2014/02/x86-is-turing-complete-with-no-registers.html 87 | 88 | ### Author 89 | 90 | Tiresias is a proof-of-concept from Christopher Domas 91 | ([@xoreaxeaxeax](https://twitter.com/xoreaxeaxeax)). 92 | -------------------------------------------------------------------------------- /arm/Makefile: -------------------------------------------------------------------------------- 1 | tiresias: tiresias.o 2 | arm-linux-gnueabi-ld $< -o $@ 3 | 4 | tiresias.o: tiresias.s 5 | arm-linux-gnueabi-as $< -o $@ 6 | 7 | clean: 8 | rm tiresias tiresias.o 9 | -------------------------------------------------------------------------------- /arm/tiresias.s: -------------------------------------------------------------------------------- 1 | @ 2 | @ tiresias 3 | @ arm is turing-complete without data fetches 4 | @ domas, @xoreaxeaxeax 5 | @ 6 | 7 | .arch armv6t2 8 | .fpu softvfp 9 | 10 | .macro mov32, reg, val 11 | movw \reg, #:lower16:\val 12 | movt \reg, #:upper16:\val 13 | .endm 14 | 15 | .macro simcall target 16 | mov32 r9, 1f 17 | b \target 18 | 1: 19 | .endm 20 | 21 | .macro simfetch from, cell 22 | mov r0, \cell 23 | lsl r0, #3 24 | mov32 r1, \from 25 | add r0, r1 26 | mov32 r9, 1f 27 | bx r0 28 | 1: 29 | .endm 30 | 31 | .macro simwrite to, cell 32 | mov r4, \cell 33 | lsl r4, #3 34 | mov32 r1, \to 35 | add r4, r1 36 | strb r0, [r4] 37 | .endm 38 | 39 | .text 40 | .align 2 41 | 42 | .global _start 43 | _start: 44 | mov r6, #0 @ ip 45 | mov r8, #0 @ dp 46 | mov r3, #0 47 | .read_program: 48 | simcall read_byte 49 | simwrite program, r3 50 | add r3, #1 51 | cmp r0, #'#' 52 | bne .read_program 53 | 54 | .execute: 55 | simfetch program, r6 56 | 57 | cmp r0, #0 58 | beq .exit 59 | cmp r0, #'>' 60 | beq .increment_dp 61 | cmp r0, #'<' 62 | beq .decrement_dp 63 | cmp r0, #'+' 64 | beq .increment_data 65 | cmp r0, #'-' 66 | beq .decrement_data 67 | cmp r0, #'.' 68 | beq .output 69 | cmp r0, #',' 70 | beq .input 71 | cmp r0, #'[' 72 | beq .forward 73 | cmp r0, #']' 74 | beq .backward 75 | b .done 76 | 77 | .increment_dp: 78 | add r8, #1 79 | b .done 80 | 81 | .decrement_dp: 82 | sub r8, #1 83 | b .done 84 | 85 | .increment_data: 86 | simfetch data, r8 87 | add r0, #1 88 | simwrite data, r8 89 | b .done 90 | 91 | .decrement_data: 92 | simfetch data, r8 93 | sub r0, #1 94 | simwrite data, r8 95 | b .done 96 | 97 | .output: 98 | simfetch data, r8 99 | simcall write_byte 100 | b .done 101 | 102 | .input: 103 | simcall read_byte 104 | simwrite data, r8 105 | b .done 106 | 107 | .forward: 108 | simfetch data, r8 109 | cmp r0, #0 110 | bne .done 111 | mov r4, #1 112 | .forward.seek: 113 | add r6, #1 114 | simfetch program, r6 115 | cmp r0, #']' 116 | beq .forward.seek.dec 117 | cmp r0, #'[' 118 | beq .forward.seek.inc 119 | b .forward.seek 120 | .forward.seek.inc: 121 | add r4, #1 122 | b .forward.seek 123 | .forward.seek.dec: 124 | sub r4, #1 125 | cmp r4, #0 126 | beq .done 127 | b .forward.seek 128 | 129 | .backward: 130 | simfetch data, r8 131 | cmp r0, #0 132 | beq .done 133 | mov r4, #1 134 | .backward.seek: 135 | sub r6, #1 136 | simfetch program, r6 137 | cmp r0, #'[' 138 | beq .backward.seek.dec 139 | cmp r0, #']' 140 | beq .backward.seek.inc 141 | b .backward.seek 142 | .backward.seek.inc: 143 | add r4, #1 144 | b .backward.seek 145 | .backward.seek.dec: 146 | sub r4, #1 147 | cmp r4, #0 148 | beq .done 149 | b .backward.seek 150 | 151 | .done: 152 | add r6, #1 153 | b .execute 154 | 155 | .exit: 156 | mov r0, #0 157 | mov r7, #1 158 | svc 0 159 | 160 | .section .data 161 | .align 2 162 | 163 | read_byte: 164 | mov r0, #0 165 | mov32 r1, io 166 | mov r2, #1 167 | mov r7, #3 168 | svc 0 169 | io: mov r0, #0 170 | bx r9 171 | 172 | write_byte: 173 | strb r0, io 174 | mov r0, #1 175 | mov32 r1, io 176 | mov r2, #1 177 | mov r7, #4 178 | svc 0 179 | bx r9 180 | 181 | data: 182 | .rept 30000 183 | .long 0xe3a00000 @ mov r0, xx 184 | .long 0xe12fff19 @ bx r9 185 | .endr 186 | 187 | program: 188 | .rept 30000 189 | .long 0xe3a00000 @ mov r0, xx 190 | .long 0xe12fff19 @ bx r9 191 | .endr 192 | -------------------------------------------------------------------------------- /by/hello_world.by: -------------------------------------------------------------------------------- 1 | ++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.# 2 | -------------------------------------------------------------------------------- /x86/Makefile: -------------------------------------------------------------------------------- 1 | tiresias: tiresias.o 2 | ld -melf_i386 $< -o $@ 3 | 4 | tiresias.o: tiresias.asm 5 | nasm -felf $< -o $@ 6 | 7 | clean: 8 | rm tiresias tiresias.o 9 | -------------------------------------------------------------------------------- /x86/tiresias.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; tiresias 3 | ; x86 is turing-complete without data fetches 4 | ; domas, @xoreaxeaxeax 5 | ; 6 | 7 | USE32 8 | 9 | global _start 10 | 11 | section .text 12 | 13 | %macro simcall 1 14 | mov esi, %%retsim 15 | jmp %1 16 | %%retsim: 17 | %endmacro 18 | 19 | %macro simfetch 2 20 | mov edi, %2 21 | shl edi, 3 22 | add edi, %1 23 | mov esi, %%retsim 24 | jmp edi 25 | %%retsim: 26 | %endmacro 27 | 28 | %macro simwrite 2 29 | mov edi, %2 30 | shl edi, 3 31 | add edi, %1+1 32 | mov [edi], eax 33 | %%retsim: 34 | %endmacro 35 | 36 | _start: 37 | mov ebp, 0 38 | .read_program: 39 | simcall read_byte 40 | simwrite program, ebp 41 | inc ebp 42 | cmp al, '#' 43 | jnz .read_program 44 | 45 | .execute: 46 | simcall fetch_ip 47 | simfetch program, eax 48 | 49 | cmp al, 0 50 | je .exit 51 | cmp al, '>' 52 | je .increment_dp 53 | cmp al, '<' 54 | je .decrement_dp 55 | cmp al, '+' 56 | je .increment_data 57 | cmp al, '-' 58 | je .decrement_data 59 | cmp al, '.' 60 | je .output 61 | cmp al, ',' 62 | je .input 63 | cmp al, '[' 64 | je .forward 65 | cmp al, ']' 66 | je .backward 67 | jmp .done 68 | 69 | .increment_dp: 70 | simcall fetch_dp 71 | inc eax 72 | mov [dp], eax 73 | jmp .done 74 | 75 | .decrement_dp: 76 | simcall fetch_dp 77 | dec eax 78 | mov [dp], eax 79 | jmp .done 80 | 81 | .increment_data: 82 | simcall fetch_dp 83 | mov edx, eax 84 | simfetch data, edx 85 | inc eax 86 | simwrite data, edx 87 | jmp .done 88 | 89 | .decrement_data: 90 | simcall fetch_dp 91 | mov edx, eax 92 | simfetch data, edx 93 | dec eax 94 | simwrite data, edx 95 | jmp .done 96 | 97 | .output: 98 | simcall fetch_dp 99 | simfetch data, eax 100 | simcall write_byte 101 | jmp .done 102 | 103 | .input: 104 | simcall read_byte 105 | mov edx, eax 106 | simcall fetch_dp 107 | mov ecx, eax 108 | mov eax, edx 109 | simwrite data, ecx 110 | jmp .done 111 | 112 | .forward: 113 | simcall fetch_dp 114 | simfetch data, eax 115 | cmp al, 0 116 | jne .done 117 | mov ecx, 1 118 | .forward.seek: 119 | simcall fetch_ip 120 | inc eax 121 | mov [ip], eax 122 | simfetch program, eax 123 | cmp al, ']' 124 | je .forward.seek.dec 125 | cmp al, '[' 126 | je .forward.seek.inc 127 | jmp .forward.seek 128 | .forward.seek.inc: 129 | inc ecx 130 | jmp .forward.seek 131 | .forward.seek.dec: 132 | dec ecx 133 | cmp ecx, 0 134 | je .done 135 | jmp .forward.seek 136 | 137 | .backward: 138 | simcall fetch_dp 139 | simfetch data, eax 140 | cmp al, 0 141 | je .done 142 | mov ecx, 1 143 | .backward.seek: 144 | simcall fetch_ip 145 | dec eax 146 | mov [ip], eax 147 | simfetch program, eax 148 | cmp al, '[' 149 | je .backward.seek.dec 150 | cmp al, ']' 151 | je .backward.seek.inc 152 | jmp .backward.seek 153 | .backward.seek.inc: 154 | inc ecx 155 | jmp .backward.seek 156 | .backward.seek.dec: 157 | dec ecx 158 | cmp ecx, 0 159 | je .done 160 | jmp .backward.seek 161 | 162 | .done: 163 | simcall fetch_ip 164 | inc eax 165 | mov [ip], eax 166 | jmp .execute 167 | 168 | .exit: 169 | mov eax, 1 170 | mov ebx, 0 171 | int 0x80 172 | 173 | section .data 174 | 175 | read_byte: 176 | mov ebx, 0 177 | mov ecx, io 178 | mov edx, 1 179 | mov eax, 3 180 | int 0x80 181 | db 0xb8 182 | io: dd 0 183 | jmp esi 184 | 185 | write_byte: 186 | mov ebx, 1 187 | mov [io], eax 188 | mov ecx, io 189 | mov edx, 1 190 | mov eax, 4 191 | int 0x80 192 | jmp esi 193 | 194 | fetch_ip: db 0xb8 ; mov eax, xxxxxxxx 195 | ip: dd 0 196 | jmp esi 197 | fetch_dp: db 0xb8 ; mov eax, xxxxxxxx 198 | dp: dd 0 199 | jmp esi 200 | data: times 30000 db 0xb8, 0, 0, 0, 0, 0xff, 0xe6, 0x90 ; mov eax, xxxxxxxx, jmp esi, nop 201 | program: times 30000 db 0xb8, 0, 0, 0, 0, 0xff, 0xe6, 0x90 ; mov eax, xxxxxxxx, jmp esi, nop 202 | --------------------------------------------------------------------------------