├── minskys_turing_machine.py ├── README.md ├── test_minsky.py └── minskys_turing_machine_lib.py /minskys_turing_machine.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | from minskys_turing_machine_lib import UTM 3 | 4 | parser = argparse.ArgumentParser(description='A Universal Turing Machine as described in Minsky, Computation: Finite and infinite machines, 1967, Chapter 7.') 5 | parser.add_argument('--machine_condition', type=str, default="001", help="The internal state and the currently read symbol. The default starts in state 00 and the head is scanning a 1.") 6 | parser.add_argument('--machine_description', type=str, default="X0000001X0010110X0100011X0110100", help="The program of the Turing machine to be simulated.") 7 | parser.add_argument('--machine_tape', type=str, default="1111YBAAXAAAAAAAXAABAAAAS", help="The simulated Turing machine's initial tape. The default is an exploit that achieves arbitrary code execution.") 8 | parser.add_argument('--infinity_buffer_size', type=int, default=2, help='Number of zeros to the left and right of the UTM tape,') 9 | parser.add_argument('--t_buffer_size', type=int, default=6, help='Number of zeros to the right of the simulated machine\'s tape,') 10 | parser.add_argument('--max_steps', type=int, default=float("inf"), help='Maximum number of steps to execute before terminating the program.') 11 | parser.add_argument('--verbosity', type=int, default=1, help='Degree of vebosity, -1 to 4.') 12 | parser.add_argument('--print_start_step', type=int, default=0, help='The step at which printing of output commences.') 13 | parser.add_argument('--explicit_quintuples', action='store_true', help='In Minsky\'s original machine the most common quintuples, of the form (qi, sj, qi, sj, dij) are implicit. We can require explicit ones instead, though.') 14 | parser.add_argument('--dangerous_quintuples', action='store_true', help='If explicit quintuples are enabled, we can add the specific set of quintuples that make exploitation possible. The results will be the same is when disabling explicit quintuples, though.') 15 | args = parser.parse_args() 16 | 17 | utm = UTM(args.machine_description, args.machine_tape, args.machine_condition, infinity_buffer_size=args.infinity_buffer_size, t_buffer_size=args.t_buffer_size, max_steps=args.max_steps, verbosity=args.verbosity, print_start_step=args.print_start_step, implicit_quintuples=(not args.explicit_quintuples), dangerous_quintuples=args.dangerous_quintuples) 18 | utm.execute() 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Arbitrary Code Execution in the Universal Turing Machine 2 | 3 | This is an implementation of the Universal Turing Machine as presented in Minsky, Computation: Finite and infinite machines, 1967, Chapter 7. 4 | 5 | The default input to the simulated machine is an exploit that achieves arbitrary code execution. 6 | 7 | 8 | Run the program with 9 | 10 | ```console 11 | $ python3.7 minskys_turing_machine.py 12 | State 19 reading S writing B shifting left resulting in: 00111MYBAAXAAAAAAAXAABAAAAB1000Y01SX0000001X0010110X0100011X0110100Y00 Step 380 13 | A 14 | State 19 reading S writing B shifting left resulting in: 0011M0Y00BX0000000X001000011000Y00SX0000001X0010110X0100011X0110100Y00 Step 721 15 | A 16 | State 19 reading S writing B shifting left resulting in: 001M00Y00BX0000000X001000011000Y00SX0000001X0010110X0100011X0110100Y00 Step 1055 17 | A 18 | State 19 reading S writing B shifting left resulting in: 00M000Y00BX0000000X001000011000Y00SX0000001X0010110X0100011X0110100Y00 Step 1393 19 | A 20 | State 18 reading S writing A shifting left resulting in: 0M0000Y00AX0000000X001000011000Y00SX0000001X0010110X0100011X0110100Y00 Step 1735 21 | A 22 | State 18 reading S writing A shifting left resulting in: M00000Y00AX0000000X001000011000Y00SX0000001X0010110X0100011X0110100Y00 Step 1909 23 | A 24 | Ran out of tape! 25 | 26 | ``` 27 | 28 | Help is provided with 29 | 30 | ```console 31 | $ python3.7 minskys_turing_machine.py -h 32 | usage: minskys_turing_machine.py [-h] [--machine_condition MACHINE_CONDITION] 33 | [--machine_description MACHINE_DESCRIPTION] 34 | [--machine_tape MACHINE_TAPE] 35 | [--verbosity VERBOSITY] 36 | 37 | A Universal Turing Machine as described in Minsky, Computation: Finite and 38 | infinite machines, 1967, Chapter 7. 39 | 40 | optional arguments: 41 | -h, --help show this help message and exit 42 | --machine_condition MACHINE_CONDITION 43 | The internal state and the currently read symbol. The 44 | default starts in state 00 and the head is scanning a 45 | 1. 46 | --machine_description MACHINE_DESCRIPTION 47 | The program of the Turing machine to be simulated. 48 | --machine_tape MACHINE_TAPE 49 | The simulated Turing machine's initial tape. The 50 | default is an exploit that achieves arbitrary code 51 | execution. 52 | --verbosity VERBOSITY 53 | Degree of vebosity, 1-4. 54 | 55 | ``` 56 | 57 | The vulnerability has been assigned [CVE-2021-32471](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-32471) and is presented in detail in [Pontus Johnson](https://www.kth.se/profile/pontusj)'s paper [Intrinsic Propensity for Vulnerability in Computers? Arbitrary Code Execution in the Universal Turing Machine](https://arxiv.org/abs/2105.02124) from 2021. There is also [a talk on YouTube](https://youtu.be/pujUhiX9Mvk) on the vulnerability and its exploitation. 58 | 59 | A visual simulation of the exploit is available at https://intrinsic-propensity.github.io. 60 | 61 | For an alternative re-implementation of the vulnerable Minsky Turing machine, check out [Andreas Rozek's specification](https://github.com/rozek/Universal-Turing-Machine) for [Martín Ugarte's Turing machine simulator](https://turingmachinesimulator.com). 62 | -------------------------------------------------------------------------------- /test_minsky.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | from minskys_turing_machine_lib import UTM 3 | 4 | # Verbosity 5 | parser = argparse.ArgumentParser(description='A Universal Turing Machine as described in Minsky, Computation: Finite and infinite machines, 1967, Chapter 7.') 6 | parser.add_argument('--verbosity', type=int, default=-1, help='Degree of vebosity, -1 to 4.') 7 | 8 | args = parser.parse_args() 9 | 10 | any_failure = False 11 | 12 | def check_results(utm): 13 | if (utm.tape.tape[:4] == ['0','0','0','0']): 14 | if (args.verbosity >= 0): 15 | print("Success") 16 | else: 17 | global any_failure 18 | any_failure = True 19 | print("Failure") 20 | print("Step " + str(utm.step)) 21 | print("".join(utm.tape.tape)) 22 | 23 | # The original program cannot be manipulated by the attacker, so the exploit must be able to handle all original programs. Because the first quintuple is all that the machine ever needs to come into contact with, we need not concern ourselves with larger programs; if it works for any single quintuple, then it will work for all programs. 24 | print("Exploring all possible initial quintuples.") 25 | for quintuple_1_machine_state in ['00', '01', '10', '11']: 26 | for quintuple_1_scanned_symbol in ['0', '1']: 27 | for quintuple_1_target_state in ['00', '01', '10', '11']: 28 | for quintuple_1_printed_symbol in ['0', '1']: 29 | # We do not explore the option of shifting the machine head right, as this will bring it to its end rather than onto the user input. Arguably, programs that do not consider the user input may be ignored as a degenerated special case. 30 | for quintuple_1_direction in ['0']: 31 | machine_description="X" + quintuple_1_machine_state + quintuple_1_scanned_symbol + quintuple_1_target_state + quintuple_1_printed_symbol + quintuple_1_direction 32 | # For any valid program, the first machine state and scanned symbol must point to a quintuple in the original program. 33 | initial_utm_state = quintuple_1_machine_state 34 | initial_utm_symbol = quintuple_1_scanned_symbol 35 | utm_condition = initial_utm_state + initial_utm_symbol 36 | # This can be anything that the malicious code is to operate on. 37 | initial_fake_input = "1111" 38 | # This can be any malicious code of the attacker's choosing. 39 | malicious_code = "XAAAAAAAXAABAAAA" 40 | # It happens that the symbol printed by the first quintuple will be interpreted as the first symbol in the next state to look for. This condition is satisfied by providing a matching fake state, thus tricking the machine into executing a crafted quintuple in the user data. The second symbol in the state needs to be an 'A'. 41 | if quintuple_1_printed_symbol == '0': 42 | initial_fake_state = 'AA' 43 | else: 44 | initial_fake_state = 'BA' 45 | # The initial fake symbol needs to be an 'A'. 46 | for initial_fake_symbol in ['A']: 47 | initial_fake_condition = initial_fake_state + initial_fake_symbol 48 | machine_tape= initial_fake_input + "Y" + initial_fake_condition + malicious_code + "S" 49 | utm = UTM(machine_description, machine_tape, utm_condition, verbosity=args.verbosity) 50 | steps = utm.execute() 51 | if args.verbosity >= 0: 52 | print("--machine_tape=\"" + machine_tape + "\" --machine_condition=\"" + utm_condition + "\" --machine_description=\"" + machine_description + "\"") 53 | check_results(utm) 54 | 55 | # The size of the buffer at the start of T's tape (between M and Y) can assume any value above 2 56 | print("Exploring various sizes of the buffer at the start of T's tape.") 57 | machine_description = "X0000001X0010110X0100011X0110100" 58 | machine_tape = "1111YBAAXAAAAAAAXAABAAAAS" 59 | machine_condition = "001" 60 | for t_buffer_size in [3, 4, 8, 16, 32]: 61 | utm = UTM(machine_description, machine_tape, machine_condition, t_buffer_size=t_buffer_size, verbosity=args.verbosity) 62 | steps = utm.execute() 63 | check_results(utm) 64 | 65 | # The size of the state variable can assume any value above 1 as long as t_buffer tags along 66 | print("Exploring various sizes of the state variable.") 67 | for state_size in [1, 2, 3, 4, 8]: 68 | t_buffer_size = state_size + 1 69 | sb = state_size - 1 70 | machine_description = "X" + "0"*sb + "00" + "0"*sb + "001X" + "0"*sb + "01" + "0"*sb + "110X" + "0"*sb +"10" + "0"*sb + "011X" + "0"*sb + "11" + "0"*sb + "100" 71 | machine_tape = "1111YB" + "A"*sb + "AX" + "A"*sb + "AA" + "A"*sb + "AAAX" + "A"*sb + "AB" + "A"*sb + "AAAS" 72 | machine_condition = "0"*sb + "01" 73 | utm = UTM(machine_description, machine_tape, machine_condition, t_buffer_size=t_buffer_size, verbosity=args.verbosity) 74 | steps = utm.execute() 75 | check_results(utm) 76 | 77 | 78 | 79 | 80 | print() 81 | if (any_failure): 82 | print("At least one test failed") 83 | else: 84 | print("All tests succeeded") 85 | print() 86 | 87 | -------------------------------------------------------------------------------- /minskys_turing_machine_lib.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import json 3 | 4 | # The Universal Turing Machine 5 | class UTM: 6 | 7 | def __init__(self, simulated_machine_description, simulated_machine_tape, simulated_machine_condition, infinity_buffer_size=2, t_buffer_size=3, implicit_quintuples=True, dangerous_quintuples=False, max_steps=float("inf"), verbosity=1, print_start_step=0): 8 | self.max_steps = max_steps 9 | self.verbosity = verbosity 10 | self.print_start_step = print_start_step 11 | self.trace = [] 12 | 13 | # Defining the UTM as presented in Minsky, Computation: Finite and infinite machines, 1967, Chapter 7. 14 | # Defining the UTMs states. 15 | self.states = [] 16 | self.states.append(None) 17 | self.states.append(State(1, "R", implicit_quintuples=implicit_quintuples)) 18 | self.states.append(State(2, "R", implicit_quintuples=implicit_quintuples)) 19 | self.states.append(State(3, "L", implicit_quintuples=implicit_quintuples)) 20 | self.states.append(State(4, "R", implicit_quintuples=implicit_quintuples)) 21 | self.states.append(State(5, "R", implicit_quintuples=implicit_quintuples)) 22 | self.states.append(State(6, "L", implicit_quintuples=implicit_quintuples)) 23 | self.states.append(State(7, "R", implicit_quintuples=implicit_quintuples)) 24 | self.states.append(State(8, "L", implicit_quintuples=implicit_quintuples)) 25 | self.states.append(State(9, "L", implicit_quintuples=implicit_quintuples)) 26 | self.states.append(State(10, "R", implicit_quintuples=implicit_quintuples)) 27 | self.states.append(State(11, "R", implicit_quintuples=implicit_quintuples)) 28 | self.states.append(State(12, "R", implicit_quintuples=implicit_quintuples)) 29 | self.states.append(State(13, "L", implicit_quintuples=implicit_quintuples)) 30 | self.states.append(State(14, "L", implicit_quintuples=implicit_quintuples)) 31 | self.states.append(State(15, "R", implicit_quintuples=implicit_quintuples)) 32 | self.states.append(State(16, "R", implicit_quintuples=implicit_quintuples)) 33 | self.states.append(State(17, "L", implicit_quintuples=implicit_quintuples)) 34 | self.states.append(State(18, "R", implicit_quintuples=implicit_quintuples)) 35 | self.states.append(State(19, "R", implicit_quintuples=implicit_quintuples)) 36 | self.states.append(State(20, "R", implicit_quintuples=implicit_quintuples)) 37 | self.states.append(State(21, "L", implicit_quintuples=implicit_quintuples)) 38 | self.states.append(State(22, "L", implicit_quintuples=implicit_quintuples)) 39 | self.states.append(State(23, "L", implicit_quintuples=implicit_quintuples)) 40 | 41 | # Defining the UTMs operations. 42 | self.states[1].define_operation('0', 'A', self.states[3], 3) 43 | self.states[1].define_operation('1', 'B', self.states[4], 3) 44 | self.states[2].define_operation('A', '0', self.states[1], 3) 45 | self.states[2].define_operation('B', '1', self.states[5], 3) 46 | self.states[2].define_operation('X', 'X', self.states[7], 2) 47 | self.states[3].define_operation('Y', 'Y', self.states[2], 3) 48 | self.states[4].define_operation('X', 'X', self.states[6], 3) 49 | self.states[4].define_operation('Y', 'Y', self.states[0], 3) 50 | self.states[5].define_operation('0', 'A', self.states[4], 3) 51 | self.states[5].define_operation('1', 'B', self.states[3], 3) 52 | self.states[6].define_operation('0', 'A', self.states[6], 3) 53 | self.states[6].define_operation('1', 'B', self.states[6], 3) 54 | self.states[6].define_operation('Y', 'Y', self.states[2], 3) 55 | self.states[7].define_operation('0', 'A', self.states[9], 3) 56 | self.states[7].define_operation('1', 'B', self.states[8], 3) 57 | self.states[8].define_operation('Y', 'Y', self.states[10], 3) 58 | self.states[9].define_operation('Y', 'Y', self.states[11], 3) 59 | self.states[10].define_operation('0', 'B', self.states[12], 3) 60 | self.states[10].define_operation('1', 'B', self.states[12], 3) 61 | self.states[10].define_operation('X', 'X', self.states[13], 2) 62 | self.states[11].define_operation('0', 'A', self.states[12], 3) 63 | self.states[11].define_operation('1', 'A', self.states[12], 3) 64 | self.states[11].define_operation('X', 'X', self.states[14], 2) 65 | self.states[12].define_operation('X', 'X', self.states[7], 3) 66 | self.states[13].define_operation('M', 'B', self.states[15], 3) 67 | self.states[14].define_operation('M', 'A', self.states[15], 3) 68 | self.states[15].define_operation('A', '0', self.states[15], 3) 69 | self.states[15].define_operation('B', '1', self.states[15], 3) 70 | self.states[15].define_operation('X', 'X', self.states[16], 3) 71 | self.states[16].define_operation('0', '0', self.states[17], 3) 72 | self.states[16].define_operation('1', '1', self.states[17], 3) 73 | self.states[17].define_operation('0', 'S', self.states[22], 2) 74 | self.states[17].define_operation('1', 'S', self.states[23], 2) 75 | self.states[17].define_operation('A', '0', self.states[17], 3) 76 | self.states[17].define_operation('B', '1', self.states[17], 3) 77 | self.states[18].define_operation('S', 'A', self.states[6], 1) 78 | self.states[19].define_operation('S', 'B', self.states[6], 1) 79 | self.states[20].define_operation('0', 'M', self.states[18], 3) 80 | self.states[20].define_operation('1', 'M', self.states[19], 3) 81 | self.states[21].define_operation('0', 'M', self.states[18], 3) 82 | self.states[21].define_operation('1', 'M', self.states[19], 3) 83 | self.states[22].define_operation('A', '0', self.states[21], 3) 84 | self.states[22].define_operation('B', '0', self.states[20], 3) 85 | self.states[23].define_operation('A', '1', self.states[21], 3) 86 | self.states[23].define_operation('B', '1', self.states[20], 3) 87 | 88 | # Explicit quintuples 89 | self.states[1].define_operation('A', 'A', self.states[1], 4) 90 | self.states[1].define_operation('B', 'B', self.states[1], 4) 91 | self.states[1].define_operation('X', 'X', self.states[1], 4) 92 | self.states[2].define_operation('0', '0', self.states[2], 4) 93 | self.states[2].define_operation('1', '1', self.states[2], 4) 94 | self.states[3].define_operation('0', '0', self.states[3], 4) 95 | self.states[3].define_operation('1', '1', self.states[3], 4) 96 | self.states[3].define_operation('A', 'A', self.states[3], 4) 97 | self.states[3].define_operation('B', 'B', self.states[3], 4) 98 | self.states[3].define_operation('X', 'X', self.states[3], 4) 99 | self.states[4].define_operation('0', '0', self.states[4], 4) 100 | self.states[4].define_operation('1', '1', self.states[4], 4) 101 | self.states[5].define_operation('A', 'A', self.states[5], 4) 102 | self.states[5].define_operation('B', 'B', self.states[5], 4) 103 | self.states[5].define_operation('X', 'X', self.states[5], 4) 104 | self.states[6].define_operation('A', 'A', self.states[6], 4) 105 | self.states[6].define_operation('B', 'B', self.states[6], 4) 106 | self.states[6].define_operation('X', 'X', self.states[6], 4) 107 | self.states[7].define_operation('A', 'A', self.states[7], 4) 108 | self.states[7].define_operation('B', 'B', self.states[7], 4) 109 | self.states[7].define_operation('X', 'X', self.states[7], 4) 110 | self.states[8].define_operation('0', '0', self.states[8], 4) 111 | self.states[8].define_operation('1', '1', self.states[8], 4) 112 | self.states[8].define_operation('A', 'A', self.states[8], 4) 113 | self.states[8].define_operation('B', 'B', self.states[8], 4) 114 | self.states[8].define_operation('X', 'X', self.states[8], 4) 115 | self.states[9].define_operation('0', '0', self.states[9], 4) 116 | self.states[9].define_operation('1', '1', self.states[9], 4) 117 | self.states[9].define_operation('A', 'A', self.states[9], 4) 118 | self.states[9].define_operation('B', 'B', self.states[9], 4) 119 | self.states[9].define_operation('X', 'X', self.states[9], 4) 120 | self.states[10].define_operation('A', 'A', self.states[10], 4) 121 | self.states[10].define_operation('B', 'B', self.states[10], 4) 122 | self.states[11].define_operation('A', 'A', self.states[11], 4) 123 | self.states[11].define_operation('B', 'B', self.states[11], 4) 124 | self.states[12].define_operation('0', '0', self.states[12], 4) 125 | self.states[12].define_operation('1', '1', self.states[12], 4) 126 | self.states[13].define_operation('0', '0', self.states[13], 4) 127 | self.states[13].define_operation('1', '1', self.states[13], 4) 128 | self.states[13].define_operation('A', 'A', self.states[13], 4) 129 | self.states[13].define_operation('B', 'B', self.states[13], 4) 130 | self.states[13].define_operation('Y', 'Y', self.states[13], 4) 131 | self.states[14].define_operation('0', '0', self.states[14], 4) 132 | self.states[14].define_operation('1', '1', self.states[14], 4) 133 | self.states[14].define_operation('A', 'A', self.states[14], 4) 134 | self.states[14].define_operation('B', 'B', self.states[14], 4) 135 | self.states[14].define_operation('Y', 'Y', self.states[14], 4) 136 | self.states[15].define_operation('0', '0', self.states[15], 4) 137 | self.states[15].define_operation('1', '1', self.states[15], 4) 138 | self.states[15].define_operation('Y', 'Y', self.states[15], 4) 139 | self.states[16].define_operation('A', 'A', self.states[16], 4) 140 | self.states[16].define_operation('B', 'B', self.states[16], 4) 141 | self.states[16].define_operation('X', 'X', self.states[16], 4) 142 | self.states[16].define_operation('Y', 'Y', self.states[16], 4) 143 | self.states[17].define_operation('X', 'X', self.states[17], 4) 144 | self.states[17].define_operation('Y', 'Y', self.states[17], 4) 145 | self.states[18].define_operation('0', '0', self.states[18], 4) 146 | self.states[18].define_operation('1', '1', self.states[18], 4) 147 | self.states[18].define_operation('Y', 'Y', self.states[18], 4) 148 | self.states[19].define_operation('0', '0', self.states[19], 4) 149 | self.states[19].define_operation('1', '1', self.states[19], 4) 150 | self.states[19].define_operation('Y', 'Y', self.states[19], 4) 151 | self.states[22].define_operation('0', '0', self.states[22], 4) 152 | self.states[22].define_operation('1', '1', self.states[22], 4) 153 | self.states[22].define_operation('Y', 'Y', self.states[22], 4) 154 | self.states[23].define_operation('0', '0', self.states[23], 4) 155 | self.states[23].define_operation('1', '1', self.states[23], 4) 156 | self.states[23].define_operation('Y', 'Y', self.states[23], 4) 157 | 158 | # Dangerous quintuples. These are accepted implicitly in Minsky's machine, but if disabled will all mitigate exploitation. 159 | if dangerous_quintuples: 160 | self.states[7].define_operation('Y', 'Y', self.states[7], 4) 161 | self.states[8].define_operation('S', 'S', self.states[8], 4) 162 | self.states[9].define_operation('S', 'S', self.states[9], 4) 163 | self.states[10].define_operation('S', 'S', self.states[10], 4) 164 | self.states[11].define_operation('S', 'S', self.states[11], 4) 165 | self.states[12].define_operation('S', 'S', self.states[12], 4) 166 | self.states[13].define_operation('S', 'S', self.states[13], 4) 167 | self.states[14].define_operation('X', 'X', self.states[14], 4) 168 | self.states[14].define_operation('S', 'S', self.states[14], 4) 169 | self.states[16].define_operation('S', 'S', self.states[16], 4) 170 | self.states[17].define_operation('S', 'S', self.states[17], 4) 171 | self.states[18].define_operation('A', 'A', self.states[18], 4) 172 | self.states[18].define_operation('B', 'B', self.states[18], 4) 173 | self.states[18].define_operation('X', 'X', self.states[18], 4) 174 | self.states[19].define_operation('A', 'A', self.states[19], 4) 175 | self.states[19].define_operation('B', 'B', self.states[19], 4) 176 | self.states[19].define_operation('X', 'X', self.states[19], 4) 177 | self.states[20].define_operation('A', 'A', self.states[20], 4) 178 | self.states[20].define_operation('B', 'B', self.states[20], 4) 179 | self.states[20].define_operation('S', 'S', self.states[20], 4) 180 | self.states[20].define_operation('X', 'X', self.states[20], 4) 181 | self.states[20].define_operation('Y', 'Y', self.states[20], 4) 182 | self.states[21].define_operation('A', 'A', self.states[21], 4) 183 | self.states[21].define_operation('S', 'S', self.states[21], 4) 184 | self.states[21].define_operation('B', 'B', self.states[21], 4) 185 | self.states[21].define_operation('X', 'X', self.states[21], 4) 186 | self.states[21].define_operation('Y', 'Y', self.states[21], 4) 187 | 188 | 189 | self.step = 0 190 | # This is the initial state 191 | self.state = self.states[6] 192 | # The tape is composed of a buffer to represent the infinite number of zeros of the UTM, the simulated machine's tape. it's internal machine condition, and the actual program describing that simulated machine. 193 | self.tape = Tape(list("0"*infinity_buffer_size + simulated_machine_tape + "M" + "0"*t_buffer_size + "Y" + simulated_machine_condition + simulated_machine_description + "Y" + "0"*infinity_buffer_size), infinity_buffer_size + len(simulated_machine_tape) + 1 + t_buffer_size + 1 + len(simulated_machine_condition)) 194 | 195 | # Run the UTM 196 | def execute(self): 197 | self.print_all(ignore_verbosity=True) 198 | while self.step < self.max_steps: 199 | self.step += 1 200 | symbol = self.tape.scan() 201 | self.print_before_operation() 202 | operation = self.state.get_operation(symbol) 203 | try: 204 | self.state = operation.execute(self.tape) 205 | except EndOfTapeException: 206 | if self.verbosity >= 0: 207 | print('Reached the end of the infinite tape. Increase buffer size to proceed further.') 208 | break 209 | break 210 | except HaltException: 211 | if self.verbosity >= 0: 212 | print("Found halting state. Halting.") 213 | break 214 | break 215 | self.print_after_operation() 216 | self.append_trace(operation) 217 | self.print_all(ignore_verbosity=True) 218 | json_trace = json.dumps(self.trace) 219 | with open("trace.json", "w") as outfile: 220 | outfile.write(json_trace) 221 | return self.step 222 | 223 | def print_all(self, ignore_verbosity=False): 224 | self.print_before_operation(ignore_verbosity) 225 | self.print_after_operation(ignore_verbosity) 226 | 227 | # Printing the output 228 | def print_before_operation(self, ignore_verbosity=False): 229 | if self.step >= self.print_start_step: 230 | if self.verbosity >= 0: 231 | operation = self.state.get_operation(self.tape.scan()) 232 | self.current_verbosity = operation.verbosity 233 | if self.current_verbosity <= self.verbosity or ignore_verbosity: 234 | print("State " + self.state.id_string(), end = ' ') 235 | print("reading " + self.tape.scan(), end = ' ') 236 | print("writing " + operation.print_symbol, end = ' ') 237 | if operation.target_state: 238 | if operation.target_state.direction == "R": 239 | print("shifting right", end = ' ') 240 | if operation.target_state.direction == "L": 241 | print("shifting left ", end = ' ') 242 | else: 243 | print("stopping ", end = ' ') 244 | 245 | 246 | def print_after_operation(self, ignore_verbosity=False): 247 | if self.step >= self.print_start_step: 248 | if self.verbosity >= 0: 249 | if self.current_verbosity <= self.verbosity or ignore_verbosity: 250 | print("resulting in: " + "".join(self.tape.tape), end = ' ') 251 | print("Step " + str(self.step)) 252 | print(" "*59 + " "*self.tape.head_location + "A") 253 | 254 | def append_trace(self, operation): 255 | self.trace.append({ 256 | "state" : self.state.id, 257 | "head" : self.tape.head_location, 258 | "reading" : self.tape.scan(), 259 | "writing" : operation.print_symbol, 260 | "direction" : operation.target_state.direction, 261 | "tape" : "".join(self.tape.tape), 262 | "step" : self.step 263 | }) 264 | 265 | 266 | # The tape of the UTM 267 | class Tape: 268 | def __init__(self, initial_tape, head_location): 269 | self.tape = initial_tape 270 | self.head_location = head_location 271 | 272 | def scan(self): 273 | return self.tape[self.head_location] 274 | 275 | def shift_left(self): 276 | self.head_location -= 1 277 | if self.head_location < 0: 278 | raise EndOfTapeException() 279 | 280 | def shift_right(self): 281 | self.head_location += 1 282 | if self.head_location >= len(self.tape): 283 | raise EndOfTapeException() 284 | 285 | def write(self, print_symbol): 286 | self.tape[self.head_location] = print_symbol 287 | 288 | 289 | class EndOfTapeException(Exception): 290 | pass 291 | 292 | 293 | class HaltException(Exception): 294 | pass 295 | 296 | 297 | # An operation of the UTM 298 | class Operation: 299 | 300 | def __init__(self, print_symbol, target_state, verbosity): 301 | self.print_symbol = print_symbol 302 | self.target_state = target_state 303 | self.verbosity = verbosity 304 | 305 | def execute(self, tape): 306 | tape.write(self.print_symbol) 307 | r = False 308 | if self.target_state: 309 | if self.target_state.direction == "R": 310 | tape.shift_right() 311 | if self.target_state.direction == "L": 312 | tape.shift_left() 313 | return self.target_state 314 | else: 315 | raise HaltException() 316 | 317 | # A state of the UTM 318 | class State: 319 | 320 | def __init__(self, id_num, direction, implicit_quintuples=True): 321 | self.id = id_num 322 | self.direction = direction 323 | self.operations = dict() 324 | self.implicit_quintuples = implicit_quintuples 325 | 326 | def id_string(self): 327 | if self.id < 10: 328 | return "0" + str(self.id) 329 | else: 330 | return str(self.id) 331 | 332 | def define_operation(self, scan_symbol, print_symbol, target_state, verbosity): 333 | self.operations[scan_symbol] = Operation(print_symbol, target_state, verbosity) 334 | 335 | def get_operation(self, scan_symbol): 336 | try: 337 | return self.operations[scan_symbol] 338 | except KeyError: 339 | if self.implicit_quintuples: 340 | # As specified on page 123 of the Minsky book, the most common quintuples, of the form (qi, sj, qi, sj, dij) are implicit. 341 | return Operation(scan_symbol, self, 4) 342 | else: 343 | # In order to make exploitation harder, we may restrict input to valid symbols. 344 | raise HaltException() 345 | --------------------------------------------------------------------------------