|
| 1 | +(* |
| 2 | + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. |
| 3 | + * SPDX-License-Identifier: Apache-2.0 OR ISC OR MIT-0 |
| 4 | + *) |
| 5 | + |
| 6 | +(****************************************************************************** |
| 7 | + An example that shows how to describe big numbers in a specification. |
| 8 | +******************************************************************************) |
| 9 | + |
| 10 | +needs "arm/proofs/base.ml";; |
| 11 | + |
| 12 | +(* Let's prove that the following program |
| 13 | +
|
| 14 | + 0: a9400c02 ldp x2, x3, [x0] |
| 15 | + 4: a9401424 ldp x4, x5, [x1] |
| 16 | + 8: eb04005f cmp x2, x4 |
| 17 | + c: 540000a1 b.ne 20 <bb_false> // b.any |
| 18 | + 10: eb05007f cmp x3, x5 |
| 19 | + 14: 54000061 b.ne 20 <bb_false> // b.any |
| 20 | + 18: d2800020 mov x0, #0x1 |
| 21 | + 1c: d65f03c0 ret |
| 22 | +
|
| 23 | +0000000000000020 <bb_false>: |
| 24 | + 20: aa1f03e0 mov x0, xzr |
| 25 | + 24: d65f03c0 ret |
| 26 | +
|
| 27 | + .. returns 1 to x0 if a pair of 16-byte integers at buffer x0 and x1 |
| 28 | + are equal, 0 otherwise. |
| 29 | + Since this example uses 128 bit integers, we will use 'bignum_from_memory' |
| 30 | + which will state that reading a memory buffer of a specified word number will |
| 31 | + return some large natural number. |
| 32 | +*) |
| 33 | +let bignum_mc = define_assert_from_elf "bignum_mc" "arm/tutorial/bignum.o" [ |
| 34 | + 0xa9400c02; (* arm_LDP X2 X3 X0 (Immediate_Offset (iword (&0))) *) |
| 35 | + 0xa9401424; (* arm_LDP X4 X5 X1 (Immediate_Offset (iword (&0))) *) |
| 36 | + 0xeb04005f; (* arm_CMP X2 X4 *) |
| 37 | + 0x540000a1; (* arm_BNE (word 20) *) |
| 38 | + 0xeb05007f; (* arm_CMP X3 X5 *) |
| 39 | + 0x54000061; (* arm_BNE (word 12) *) |
| 40 | + 0xd2800020; (* arm_MOV X0 (rvalue (word 1)) *) |
| 41 | + 0xd65f03c0; (* arm_RET X30 *) |
| 42 | + 0xaa1f03e0; (* arm_MOV X0 XZR *) |
| 43 | + 0xd65f03c0 (* arm_RET X30 *) |
| 44 | +];; |
| 45 | + |
| 46 | +(* |
| 47 | +You can get the above OCaml list data structure from |
| 48 | +`print_literal_from_elf "<.o file>"` or |
| 49 | +`save_literal_from_elf "<out.txt>" "<.o file>"`. |
| 50 | +*) |
| 51 | + |
| 52 | +(* ARM_MK_EXEC_RULE decodes the byte sequence into conjunction of |
| 53 | + equalities between the bytes and instructions. *) |
| 54 | +let EXEC = ARM_MK_EXEC_RULE bignum_mc;; |
| 55 | + |
| 56 | +let BIGNUM_SPEC = prove( |
| 57 | + `forall pc retpc loc0 loc1 a b. |
| 58 | + ensures arm |
| 59 | + // Precondition |
| 60 | + (\s. aligned_bytes_loaded s (word pc) bignum_mc /\ |
| 61 | + read PC s = word pc /\ |
| 62 | + read X30 s = word retpc /\ |
| 63 | + read X0 s = word loc0 /\ |
| 64 | + read X1 s = word loc1 /\ |
| 65 | + // Read 2 words (=128bits) at loc0. It is equivalent to num a. |
| 66 | + // Alternatively, this kind of condition can be written using |
| 67 | + // bignum_of_wordlist which takes a list of 64-bit words. |
| 68 | + bignum_from_memory (word loc0,2) s = a /\ |
| 69 | + // Read 2 words (=128bits) at loc1. It is equivalent to num b. |
| 70 | + bignum_from_memory (word loc1,2) s = b |
| 71 | + ) |
| 72 | + // Postcondition |
| 73 | + (\s. read PC s = word retpc /\ |
| 74 | + read X0 s = word (if a = b then 1 else 0)) |
| 75 | + // Registers (and memory locations) that may change after execution |
| 76 | + (MAYCHANGE [PC;X0;X2;X3;X4;X5] ,, MAYCHANGE SOME_FLAGS)`, |
| 77 | + |
| 78 | + REPEAT STRIP_TAC THEN |
| 79 | + (* Convert 'bignum_from_memory' into 'memory :> bytes (..)'. |
| 80 | + Also, expand SOME_FLAGS *) |
| 81 | + REWRITE_TAC[BIGNUM_FROM_MEMORY_BYTES;SOME_FLAGS] THEN |
| 82 | + (* Start symbolic execution with state 's0' *) |
| 83 | + ENSURES_INIT_TAC "s0" THEN |
| 84 | + (* Split the memory :> bytes .. into a pair of memory :> bytes64. |
| 85 | + This is necessary to successfully encode the symbolic result of ldps. *) |
| 86 | + BIGNUM_DIGITIZE_TAC "a_" `read (memory :> bytes (word loc0,8 * 2)) s0` THEN |
| 87 | + BIGNUM_DIGITIZE_TAC "b_" `read (memory :> bytes (word loc1,8 * 2)) s0` THEN |
| 88 | + |
| 89 | + (* Symbolically run two ldp instructions *) |
| 90 | + ARM_STEPS_TAC EXEC (1--2) THEN |
| 91 | + (* Until first 'bne' *) |
| 92 | + ARM_STEPS_TAC EXEC (3--4) THEN |
| 93 | + |
| 94 | + (* Recognize the if condition and create two subgoals . *) |
| 95 | + FIRST_X_ASSUM MP_TAC THEN |
| 96 | + COND_CASES_TAC THENL [ |
| 97 | + (* The low 64 bits of a and b are different. *) |
| 98 | + STRIP_TAC THEN |
| 99 | + ARM_STEPS_TAC EXEC (5--6) THEN |
| 100 | + (* Returned; Finalize symbolic execution. *) |
| 101 | + ENSURES_FINAL_STATE_TAC THEN ASM_REWRITE_TAC[] THEN |
| 102 | + (* From `~(val (word_sub a_0 b_0) = 0)` and `val a_0 + 2 EXP 64 * val a_1 = a`, |
| 103 | + and `val b_0 + 2 EXP 64 * val b_1 = b`, |
| 104 | + prove `~(a = b)`. *) |
| 105 | + SUBGOAL_THEN `~(a:num = b)` (fun th -> REWRITE_TAC[th]) THEN |
| 106 | + MAP_EVERY EXPAND_TAC ["a";"b"] THEN |
| 107 | + (* VAL_WORD_SUB_EQ_0: |- !x y. val (word_sub x y) = 0 <=> val x = val y) *) |
| 108 | + RULE_ASSUM_TAC (REWRITE_RULE [VAL_WORD_SUB_EQ_0]) THEN |
| 109 | + (* EQ_DIVMOD: |- !p m n. m DIV p = n DIV p /\ m MOD p = n MOD p <=> m = n *) |
| 110 | + ONCE_REWRITE_TAC[SPEC `2 EXP 64` (GSYM EQ_DIVMOD)] THEN |
| 111 | + (* The first '.. DIV .. = .. DIV ..' part is irelevant. *) |
| 112 | + MATCH_MP_TAC (TAUT (`~Q ==> ~(P /\ Q)`)) THEN |
| 113 | + (* Simplfy! *) |
| 114 | + SIMP_TAC[MOD_MULT_ADD;VAL_BOUND_64;ARITH_RULE`~(2 EXP 64 = 0)`] THEN |
| 115 | + ASM_SIMP_TAC[MOD_LT;VAL_BOUND_64]; |
| 116 | + |
| 117 | + ALL_TAC |
| 118 | + ] THEN |
| 119 | + |
| 120 | + (* The low 64 bits of a and b are equivalent. *) |
| 121 | + (* Until the second 'bne' *) |
| 122 | + STRIP_TAC THEN |
| 123 | + ARM_STEPS_TAC EXEC (5--6) THEN |
| 124 | + |
| 125 | + (* Recognize the if condition and create two subgoals . *) |
| 126 | + FIRST_X_ASSUM MP_TAC THEN |
| 127 | + COND_CASES_TAC THENL [ |
| 128 | + (* The high 64 bits of a and b are different. *) |
| 129 | + STRIP_TAC THEN |
| 130 | + ARM_STEPS_TAC EXEC (7--8) THEN |
| 131 | + (* Returned; Finalize symbolic execution. *) |
| 132 | + ENSURES_FINAL_STATE_TAC THEN ASM_REWRITE_TAC[] THEN |
| 133 | + (* Proof pattern is similar to the first branch case *) |
| 134 | + SUBGOAL_THEN `~(a:num = b)` (fun th -> REWRITE_TAC[th]) THEN |
| 135 | + MAP_EVERY EXPAND_TAC ["a";"b"] THEN |
| 136 | + (* VAL_WORD_SUB_EQ_0: |- !x y. val (word_sub x y) = 0 <=> val x = val y) *) |
| 137 | + RULE_ASSUM_TAC (REWRITE_RULE [VAL_WORD_SUB_EQ_0]) THEN |
| 138 | + (* EQ_DIVMOD: |- !p m n. m DIV p = n DIV p /\ m MOD p = n MOD p <=> m = n *) |
| 139 | + ONCE_REWRITE_TAC[SPEC `2 EXP 64` (GSYM EQ_DIVMOD)] THEN |
| 140 | + (* The second '.. MOD .. = .. MOD ..' part is irelevant. *) |
| 141 | + MATCH_MP_TAC (TAUT (`~P ==> ~(P /\ Q)`)) THEN |
| 142 | + (* Simplfy! *) |
| 143 | + SIMP_TAC[DIV_MULT_ADD;VAL_BOUND_64;ARITH_RULE`~(2 EXP 64 = 0)`] THEN |
| 144 | + ASM_SIMP_TAC[DIV_LT;VAL_BOUND_64;ADD_CLAUSES]; |
| 145 | + |
| 146 | + ALL_TAC |
| 147 | + ] THEN |
| 148 | + |
| 149 | + (* Both limbs are equivalent! *) |
| 150 | + STRIP_TAC THEN |
| 151 | + ARM_STEPS_TAC EXEC (7--8) THEN |
| 152 | + (* Try to prove the postcondition and frame as much as possible *) |
| 153 | + ENSURES_FINAL_STATE_TAC THEN |
| 154 | + (* Use ASM_REWRITE_TAC[] to rewrite the goal using equalities in assumptions. *) |
| 155 | + ASM_REWRITE_TAC[] THEN |
| 156 | + SUBGOAL_THEN `(a:num = b)` (fun th -> REWRITE_TAC[th]) THEN |
| 157 | + RULE_ASSUM_TAC (REWRITE_RULE [VAL_WORD_SUB_EQ_0]) THEN |
| 158 | + ASM_ARITH_TAC);; |
0 commit comments