Skip to content

Commit 3a2bbc0

Browse files
Stevengrerv-auditortothtamas28
authored
Better storeBytes for symbolic execution (#90)
This PR aims to 1. make the result of `storeBytes` as simple as possible. 2. tackle as much as possibilies for the symbolic execution of `storeBytes` --------- Co-authored-by: devops <devops@runtimeverification.com> Co-authored-by: Tamás Tóth <tothtamas28@users.noreply.github.com>
1 parent 85e3216 commit 3a2bbc0

9 files changed

Lines changed: 53 additions & 34 deletions

File tree

package/version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.1.68
1+
0.1.69

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
44

55
[tool.poetry]
66
name = "kriscv"
7-
version = "0.1.68"
7+
version = "0.1.69"
88
description = "K tooling for the RISC-V architecture"
99
authors = [
1010
"Runtime Verification, Inc. <contact@runtimeverification.com>",

src/kriscv/kdist/riscv-semantics/lemmas/bytes-simplifications.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ module BYTES-SIMPLIFICATIONS
5252
B:Bytes [I <- V] => substrBytes(B, 0, I) +Bytes Int2Bytes(1, V, LE) +Bytes substrBytes(B, I +Int 1, lengthBytes(B) -Int I -Int 1)
5353
requires I <Int lengthBytes(B)
5454
[simplification, concrete(B, I), symbolic(V), preserves-definedness]
55+
56+
rule [multiple-bytes-update-symbolic-value]:
57+
replaceAtBytes(B, I, V) => substrBytes(B, 0, I) +Bytes V +Bytes substrBytes(B, I +Int lengthBytes(V), lengthBytes(B))
58+
requires I +Int lengthBytes(V) <Int lengthBytes(B)
59+
[simplification, concrete(B, I), symbolic(V), preserves-definedness]
5560
```
5661

5762

src/kriscv/kdist/riscv-semantics/lemmas/sparse-bytes-simplifications.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,12 @@ For symbolic execution, we need to tackle the patterns of `#bytes(B +Bytes _) _`
2727
To write a byte to a symbolic sparse byte region, we need to:
2828

2929
```k
30-
rule writeByteBF(#bytes(B +Bytes BS) EF, I, V) => #bytes(B[ I <- V ] +Bytes BS) EF
31-
requires I >=Int 0 andBool I <Int lengthBytes(B) [simplification(45)]
32-
rule writeByteBF(#bytes(B +Bytes BS) EF, I, V) => prepend(B, writeByteBF(#bytes(BS) EF, I -Int lengthBytes(B), V))
33-
requires I >=Int lengthBytes(B) [simplification(45)]
30+
rule writeBytesBF(#bytes(B +Bytes BS) EF, I, V, NUM) => #bytes(replaceAtBytes(B, I, Int2Bytes(NUM, V, LE)) +Bytes BS) EF
31+
requires I >=Int 0 andBool I +Int NUM <=Int lengthBytes(B) [simplification(45)]
32+
rule writeBytesBF(#bytes(B +Bytes BS) EF, I, V, NUM) => prepend(substrBytes(B, 0, I) +Bytes Int2Bytes(NUM, V, LE), dropFront(#bytes(BS) EF, I +Int NUM -Int lengthBytes(B)))
33+
requires I <Int lengthBytes(B) andBool I +Int NUM >Int lengthBytes(B) [simplification(45)]
34+
rule writeBytesBF(#bytes(B +Bytes BS) EF, I, V, NUM) => prepend(B, writeBytesBF(#bytes(BS) EF, I -Int lengthBytes(B), V, NUM))
35+
requires I >=Int lengthBytes(B) [simplification(45)]
3436
```
3537

3638
```k

src/kriscv/kdist/riscv-semantics/riscv.md

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -99,23 +99,19 @@ module RISCV-MEMORY
9999
100100
syntax Memory = SparseBytes
101101
```
102-
We abstract the particular memory representation behind `loadByte` and `storeByte` functions.
102+
We abstract the particular memory representation behind `loadBytes` and `storeBytes` functions.
103103
```k
104104
syntax Int ::= loadByte(memory: Memory, address: Word) [function, symbol(Memory:loadByte)]
105105
rule loadByte(MEM, W(ADDR)) => MaybeByte2Int(readByte(MEM, ADDR))
106-
107-
syntax Memory ::= storeByte(memory: Memory, address: Word, byte: Int) [function, total, symbol(Memory:storeByte)]
108-
rule storeByte(MEM, W(ADDR), B) => writeByte(MEM, ADDR, B)
109106
```
110107
For multi-byte loads and stores, we presume a little-endian architecture.
111108
```k
112109
syntax Int ::= loadBytes(memory: Memory, address: Word, numBytes: Int) [function]
113110
rule loadBytes(MEM, ADDR, 1 ) => loadByte(MEM, ADDR)
114111
rule loadBytes(MEM, ADDR, NUM) => (loadBytes(MEM, ADDR +Word W(1), NUM -Int 1) <<Int 8) |Int loadByte(MEM, ADDR) requires NUM >Int 1
115112
116-
syntax Memory ::= storeBytes(memory: Memory, address: Word, bytes: Int, numBytes: Int) [function]
117-
rule storeBytes(MEM, ADDR, BS, 1 ) => storeByte(MEM, ADDR, BS)
118-
rule storeBytes(MEM, ADDR, BS, NUM) => storeBytes(storeByte(MEM, ADDR, BS &Int 255), ADDR +Word W(1), BS >>Int 8, NUM -Int 1) requires NUM >Int 1
113+
syntax Memory ::= storeBytes(memory: Memory, address: Word, bytes: Int, numBytes: Int) [function, total, symbol(Memory:storeBytes)]
114+
rule storeBytes(MEM, W(ADDR), BS, NUM) => writeBytes(MEM, ADDR, BS, NUM)
119115
```
120116
Instructions are always 32-bits, and are stored in little-endian format regardless of the endianness of the overall architecture.
121117
```k
@@ -352,7 +348,7 @@ Dually, `SB`, `SH`, and `SW` store the least-significant `1`, `2`, and `4` bytes
352348
```k
353349
rule <instrs> SB RS2 , OFFSET ( RS1 ) => .K ...</instrs>
354350
<regs> REGS </regs>
355-
<mem> MEM => storeByte(MEM, readReg(REGS, RS1) +Word chop(OFFSET), Word2UInt(readReg(REGS, RS2)) &Int 255) </mem>
351+
<mem> MEM => storeBytes(MEM, readReg(REGS, RS1) +Word chop(OFFSET), Word2UInt(readReg(REGS, RS2)) &Int 255, 1) </mem>
356352
357353
rule <instrs> SH RS2 , OFFSET ( RS1 ) => .K ...</instrs>
358354
<regs> REGS </regs>

src/kriscv/kdist/riscv-semantics/sparse-bytes.md

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,17 @@ We provide helpers to prepend either data or an empty region to an existing `Spa
4747
rule prependEmpty(I, #empty(N) BF ) => #empty(I +Int N) BF requires I >Int 0
4848
rule prependEmpty(I, BF:SparseBytesBF) => #empty(I) BF requires I >Int 0
4949
```
50+
`dropFront` is a helper function for dropping the first `N` bytes from a `SparseBytes` value.
51+
```k
52+
syntax SparseBytes ::= dropFront(SparseBytes, Int) [function, total]
53+
rule dropFront(SBS, I) => SBS requires I <=Int 0
54+
rule dropFront(.SparseBytes, I) => .SparseBytes requires I >Int 0
55+
rule dropFront(#empty(N) BF, I) => #empty(N -Int I) BF requires I >Int 0 andBool I <Int N
56+
rule dropFront(#empty(N) BF, I) => dropFront(BF, I -Int N) requires I >Int 0 andBool I >=Int N
57+
rule dropFront(#bytes(B) EF, I) => dropFront(#bytes(substrBytes(B, I, lengthBytes(B))) EF, 0)
58+
requires I >Int 0 andBool I <Int lengthBytes(B)
59+
rule dropFront(#bytes(B) EF, I) => dropFront(EF, I -Int lengthBytes(B)) requires I >=Int lengthBytes(B)
60+
```
5061
`readByte` reads a single byte from a given index in `O(E)` time, where `E` is the number of `#empty(_)` or `#bytes(_)` entries in the list up to the location of the index. The result is either
5162
- an `Int` in the range `[0, 255)` giving the byte value at the index, or
5263
- `.Byte` if the index does not point to initialized data
@@ -76,28 +87,33 @@ We provide helpers to prepend either data or an empty region to an existing `Spa
7687
rule readByteBF(#bytes(B) EF, I) => readByteEF(EF, I -Int lengthBytes(B))
7788
requires I >=Int lengthBytes(B)
7889
```
79-
`writeByte` writes a single byte to a given index. With regards to time complexity,
90+
`writeBytes(SBS, I, V, NUM)` writes value `V` with length `NUM` bytes to a given index `I`. With regards to time complexity,
8091
- If the index is in the middle of an existing `#empty(_)` or `#bytes(_)` region, time complexity is `O(E)` where `E` is the number of entries up to the index.
8192
- If the index happens to be the first or last index in an `#empty(_)` region directly boarding a `#bytes(_)` region, then the `#bytes(_)` region must be re-allocated to append the new value, giving worst-case `O(E + B)` time, where `E` is the number of entries up to the location of the index and `B` is the size of this existing `#bytes(_)`.
8293
```k
8394
syntax SparseBytes ::=
84-
writeByte (SparseBytes , Int, Int) [function, total]
85-
| writeByteEF(SparseBytesEF, Int, Int) [function, total]
86-
| writeByteBF(SparseBytesBF, Int, Int) [function, total]
95+
writeBytes (SparseBytes , Int, Int, Int) [function, total]
96+
| writeBytesEF(SparseBytesEF, Int, Int, Int) [function, total]
97+
| writeBytesBF(SparseBytesBF, Int, Int, Int) [function, total]
8798
88-
rule writeByte(BF:SparseBytesBF, I, V) => writeByteBF(BF, I, V)
89-
rule writeByte(EF:SparseBytesEF, I, V) => writeByteEF(EF, I, V)
99+
rule writeBytes(BF:SparseBytesBF, I, V, NUM) => writeBytesBF(BF, I, V, NUM)
100+
rule writeBytes(EF:SparseBytesEF, I, V, NUM) => writeBytesEF(EF, I, V, NUM)
90101
91-
rule writeByteEF(_ , I, _) => .SparseBytes requires I <Int 0 // error case for totality
92-
rule writeByteEF(.SparseBytes, I, V) => #bytes(Int2Bytes(1, V, BE)) .SparseBytes requires I ==Int 0
93-
rule writeByteEF(.SparseBytes, I, V) => #empty(I) #bytes(Int2Bytes(1, V, BE)) .SparseBytes requires I >Int 0
94-
rule writeByteEF(#empty(N) BF, I, V) => prependEmpty(I, prepend(Int2Bytes(1, V, BE), prependEmpty((N -Int I) -Int 1, BF)))
95-
requires I >=Int 0 andBool I <Int N
96-
rule writeByteEF(#empty(N) BF, I, V) => prependEmpty(N, writeByteBF(BF, I -Int N, V)) requires I >=Int N
102+
rule writeBytesEF(_ , I, _, _) => .SparseBytes requires I <Int 0 // error case for totality
103+
rule writeBytesEF(.SparseBytes, I, V, NUM) => prependEmpty(I, #bytes(Int2Bytes(NUM, V, LE)) .SparseBytes) requires I >=Int 0
104+
rule writeBytesEF(#empty(N) BF, I, V, NUM) => prependEmpty(I, prepend(Int2Bytes(NUM, V, LE), prependEmpty(N -Int I -Int NUM, BF)))
105+
requires I >=Int 0 andBool I +Int NUM <=Int N
106+
rule writeBytesEF(#empty(N) BF, I, V, NUM) => prependEmpty(I, prepend(Int2Bytes(NUM, V, LE), dropFront(BF, I +Int NUM -Int N)))
107+
requires I <Int N andBool I +Int NUM >Int N
108+
rule writeBytesEF(#empty(N) BF, I, V, NUM) => prependEmpty(N, writeBytesBF(BF, I -Int N, V, NUM))
109+
requires I >=Int N
97110
98-
rule writeByteBF(_ , I, _) => .SparseBytes requires I <Int 0 // error case for totality
99-
rule writeByteBF(#bytes(B) EF, I, V) => #bytes(B[ I <- V ]) EF requires I >=Int 0 andBool I <Int lengthBytes(B)
100-
rule writeByteBF(#bytes(B) EF, I, V) => prepend(B, writeByteEF(EF, I -Int lengthBytes(B), V))
101-
requires I >=Int lengthBytes(B)
111+
rule writeBytesBF(_ , I, _, _ ) => .SparseBytes requires I <Int 0 // error case for totality
112+
rule writeBytesBF(#bytes(B) EF, I, V, NUM) => #bytes(replaceAtBytes(B, I, Int2Bytes(NUM, V, LE))) EF
113+
requires I >=Int 0 andBool I +Int NUM <=Int lengthBytes(B)
114+
rule writeBytesBF(#bytes(B) EF, I, V, NUM) => prepend(substrBytes(B, 0, I) +Bytes Int2Bytes(NUM, V, LE), dropFront(EF, I +Int NUM -Int lengthBytes(B)))
115+
requires I <Int lengthBytes(B) andBool I +Int NUM >Int lengthBytes(B)
116+
rule writeBytesBF(#bytes(B) EF, I, V, NUM) => prepend(B, writeBytesEF(EF, I -Int lengthBytes(B), V, NUM))
117+
requires I >=Int lengthBytes(B)
102118
endmodule
103119
```

src/kriscv/term_builder.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -318,8 +318,8 @@ def load_byte(mem: KInner, addr: KInner) -> KInner:
318318
return KApply('Memory:loadByte', mem, addr)
319319

320320

321-
def store_byte(mem: KInner, addr: KInner, value: KInner) -> KInner:
322-
return KApply('Memory:storeByte', mem, addr, value)
321+
def store_bytes(mem: KInner, addr: KInner, value: KInner, num_bytes: KInner) -> KInner:
322+
return KApply('Memory:storeBytes', mem, addr, value, num_bytes)
323323

324324

325325
def regs(dct: dict[int, int]) -> KInner:

src/tests/integration/test-data/specs/store-symbolic-bytes.k

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ module STORE-SYMBOLIC-BYTES
1616
X:Int,
1717
4
1818
) =>
19-
#bytes ( Int2Bytes ( 1 , X:Int &Int 255 , LE ) +Bytes Int2Bytes ( 1 , X:Int >>Int 8 &Int 255 , LE ) +Bytes Int2Bytes ( 1 , X:Int >>Int 8 >>Int 8 &Int 255 , LE ) +Bytes Int2Bytes ( 1 , X:Int >>Int 8 >>Int 8 >>Int 8 , LE ) +Bytes b"\x00\x00\x00\x00" ) .SparseBytes
19+
#bytes ( Int2Bytes ( 4 , X , LE ) +Bytes b"\x00\x00\x00\x00\x00\x00\x00\x00" ) .SparseBytes
2020
</mem>
2121
<test>
2222
<haltCond> ADDRESS ( W ( 0 ) ) </haltCond>

src/tests/unit/test_unit.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ def test_memory(memory: dict[int, bytes], addr: int, byte: int) -> None:
304304
memory_sb = term_builder.sparse_bytes(memory)
305305
addr_word = term_builder.word(addr)
306306

307-
store_call = term_builder.store_byte(memory_sb, addr_word, intToken(byte))
307+
store_call = term_builder.store_bytes(memory_sb, addr_word, intToken(byte), intToken(1))
308308
memory_actual_sb_kore = _eval_call_to_kore(tools, store_call, term_builder.sort_memory())
309309
memory_actual = kore_sparse_bytes(memory_actual_sb_kore)
310310

0 commit comments

Comments
 (0)