Crash on parseSMTLib2String
rcdickerson opened this issue · 8 comments
I'm seeing the following using haskell-z3 version 408.0 and z3 version 4.8.4 on a 64-bit Pop!_OS (Ubuntu-based) install, OS version 19.04.
Parsing a simple SMTLib2 string and displaying the AST crashes with what looks like a memory management error:
$ ghci
GHCi, version 8.4.4: http://www.haskell.org/ghc/ :? for help
Prelude> import Z3.Monad
Prelude Z3.Monad> ast <- evalZ3 $ parseSMTLib2String "(assert true)" [] [] [] []
Prelude Z3.Monad> s <- evalZ3 $ astToString ast
double free or corruption (out)
Aborted (core dumped)
I inspected this in gdb and saw the following backtrace:
#0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
#1 0x00007ffff67f9535 in __GI_abort () at abort.c:79
#2 0x00007ffff6860726 in __libc_message (action=action@entry=do_abort, fmt=fmt@entry=0x7ffff6986952 "%s\n") at ../sysdeps/posix/libc_fatal.c:181
#3 0x00007ffff686759a in malloc_printerr (str=str@entry=0x7ffff6984ab3 "free(): invalid size") at malloc.c:5352
#4 0x00007ffff68693dc in _int_free (av=<optimized out>, p=0x7fffffff94a0, have_lock=<optimized out>) at malloc.c:4185
#5 0x00007ffff6b909c7 in shared_occs::operator()(expr*, shared_occs_mark&) [clone .cold.69] () from /lib/libz3.so
#6 0x00007ffff78493ac in shared_occs::operator()(expr*) () from /lib/libz3.so
#7 0x00007ffff7878eff in smt2_printer::process(expr*, obj_ref<app, ast_manager>&) () from /lib/libz3.so
#8 0x00007ffff787a825 in smt2_printer::operator()(expr*, unsigned int, char const*, obj_ref<app, ast_manager>&, sbuffer<symbol, 16u>&) () from /lib/libz3.so
#9 0x00007ffff7873ced in ast_smt2_pp(std::ostream&, expr*, smt2_pp_environment&, params_ref const&, unsigned int, unsigned int, char const*) () from /lib/libz3.so
#10 0x00007ffff7873ed7 in operator<<(std::ostream&, mk_ismt2_pp const&) () from /lib/libz3.so
#11 0x00007ffff6c19093 in Z3_ast_to_string () from /lib/libz3.so
#12 0x00000000005c1263 in ?? ()
#13 0x0000000000000000 in ?? ()
I was unable to reproduce this on an up-to-date MacOS install with z3 version 4.8.0 (so an earlier point release.)
Because of the "double free" in the error message, I originally thought this might be a finalizer race or similar, with a certain GC ordering triggering some bad sequence of free()
s. Seeing "invalid size" in the backtrace and the MacOS non-repro makes me wonder if it could be something more platform-specific, though?
I'm stuck on what to investigate next to understand why this is happening. Any thoughts?
Hi @rcdickerson, the bindings are rather low-level, and the "higher-level" Monad
API is just offered for convenience but it doesn't really add any safety. In particular, if you look at the code, evalZ3
creates a new Z3 Context
, and any Z3 object created inside evalZ3
is only valid for that particular Context
. What you are doing here is illegal:
ast <- evalZ3 $ parseSMTLib2String "(assert true)" [] [] [] []
s <- evalZ3 $ astToString ast
...
I don't know exactly where this crashes and why it doesn't crash on some platforms or with earlier versions. That is a detail of the C++ implementation. At the high-level, the issue is that the ast
belongs to the Context
created by the first evalZ3
, the second evalZ3
expects it to belong to its own Context
. (Edited: I previously said that the first Context
may have been finalized, but that shouldn't happen, it should wait for the ast
to be GCed.)
You should do instead:
evalZ3 $ do
ast <- parseSMTLib2String "(assert true)" [] [] [] []
s <- astToString ast
...
If you want that kind of flexibility then you should use Z3.Base
and keep around the Context
object yourself. (Note that Z3.Monad
is essentially a Reader
monad that stores the Context
.) You don't need to call any finalizers manually though, that should be handled for you by the GC.
Thanks for the response! I didn't realize there was one Context
per evalZ3
-- I will restructure my code accordingly. :)
So actually the same thing happens to me even with a single top-level evalZ3
:
$ ghci
GHCi, version 8.4.4: http://www.haskell.org/ghc/ :? for help
Prelude> import Z3.Monad
Prelude Z3.Monad> evalZ3 $ astToString =<< parseSMTLib2String "(assert true)" [] [] [] []
double free or corruption (out)
Aborted (core dumped)
I see the same thing directly using Z3.Base
as well:
$ ghci
GHCi, version 8.4.4: http://www.haskell.org/ghc/ :? for help
Prelude> import Z3.Base
Prelude Z3.Base> cfg <- mkConfig
Prelude Z3.Base> ctx <- mkContext cfg
Prelude Z3.Base> ast <- parseSMTLib2String ctx "(assert (= 1 1))" [] [] [] []
Prelude Z3.Base> astToString ctx ast
double free or corruption (out)
Aborted (core dumped)
As another data point, I tried this with the Python bindings as well to see if my Z3 install was the problem. Seems to work:
$ python
Python 2.7.16 (default, Apr 6 2019, 01:42:57)
[GCC 8.3.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from z3 import *
>>> str(parse_smt2_string("(assert (= 1 1))"))
'[1 == 1]'
Sorry for taking a year or so to reply. :-) It seems to be because the Z3 folks changed the return type of the function. It used to return a Z3_ast
but it now returns a Z3_ast_vector
. If that is all then it should be easy to fix. Patches are welcome!
I am currently trying to fix this ✌️
Fixed by @maurobringolf in 140e3e7 (PR #44), thanks a lot!