Efficient symbolic memory modeling via z3's lambda-arrays #329
Labels
performance
Issues related to runtime or simulation-time performance
symbolic-execution
Issues relating to macaw-symbolic and symbolic execution
This is not an issue, but rather a comment on https://galois.com/blog/2023/03/making-a-scalable-smt-based-machine-code-memory-model/
(I'd have put a comment on the blog, but that doesn't seem possible. In any case, GitHub is probably better suited for this discussion anyhow.)
Cool blog! It reminded me about some of the more recent advances in z3 on how large symbolic memories can be modeled. As you mention, even though the memory is excessively large, any given program only accesses a small part of it, at least for most programs of interest. To address these concerns, z3 now allows functions (expressed as lambdas) to be used as arrays. This makes a lot of sense: A memory is nothing but a mapping from addresses to values, which is exactly what a function is. With some internal solver magic, a lot of the clunky SMT-array updates can be avoided.
I've also recently added support for this in SBV, with the function
lambdaAsArray
. The idea is that you create a lambda-term (which is a z3 extension, I don't think other solvers support it quite yet), encoding the memory as a function.Here's an example I coded up that should illustrate the point:
(Note that you need sbv 10.1 to have this example run, which just got released on Hackage a few days ago.)
I'll let you look into the details, but the crucial point is we get to initialize the memory to not just the typical "all 0", but with a function that stores an arbitrary value in any address, which can be a function of that address. (See the definition of
initMem
.) If you were doing this with a regular array, you'd have an infinite conjunction, or you'd have to use a quantifier; neither of which play all that well. But with a lambda-term, it behaves quite nicely. You can runexample1
to see what a small foot-print it has as it communicates to the solver; the trick is revealed in the SMT-Lib line:which shows the
lambda
extension allowed by z3.In
example2
, we do load a program into this memory, and do some queries on it. Again, the footprint is quite small. You can see it by running it in the verbose mode. (runSMTWith z3{verbose=True} ...
)Anyhow. I wanted to share this cool z3 feature with you. It'd be interesting to see if you can incorporate this idea to Macaw and see how it fares against your solution. Of course, when
lambda
terms are around, z3 no longer guarantees decidability: You can getunknown
as an answer (or worse, loop forever); but I think if you use it in this way, it should get you far. (They also allow lambdas with sequence-folding etc., which can land you into the land of undecidability.) I'd be interested in hearing what you find out, if you play around with this.Great to see Galois is pushing the frontiers on this sort of analysis..
The text was updated successfully, but these errors were encountered: