Repository: angr/angr

Issue:
SimProcedure 'strncmp' (Comparison length too short) 
### Description

In file "angr/procedures/libc/strncmp.py"
```
#Line 44:
maxlen = min(c_a_len, c_b_len, c_limit)
```
The length for the comparison seems to be the length of the shorter string. Line 48 has the same problem.
Maybe it should be "max"?



### Steps to reproduce the bug

Test program:
```C
#include "string.h"
#include "stdio.h"
int main(void){
    char *str1 = "aaa";
    char *str2 = "aa";
    if (!strncmp(str1, str2, 3)){
        printf("same");
    }
    return 0;
}
```

Python code:
```python
import angr
p = angr.Project('./strncmp_test')
state = p.factory.entry_state()
sm = p.factory.simulation_manager(state)
sm.explore(find=0x4006C4) # printf("same")
print(sm.found)
```



### Environment

_No response_

### Additional context

_No response_


Discussion:

Resolving patch:
diff --git a/angr/procedures/libc/strncmp.py b/angr/procedures/libc/strncmp.py
--- a/angr/procedures/libc/strncmp.py
+++ b/angr/procedures/libc/strncmp.py
@@ -41,7 +41,7 @@ def run(
                 l.debug("lengths < limit and unmatched")
 
             concrete_run = True
-            maxlen = min(c_a_len, c_b_len, c_limit)
+            maxlen = min(c_a_len + 1, c_b_len + 1, c_limit)
         else:
             if self.state.solver.single_valued(limit):
                 c_limit = self.state.solver.eval(limit)
@@ -141,11 +141,11 @@ def run(
                             ),
                         ),
                         self.state.solver.ULT(a_len, i),
-                        self.state.solver.ULT(limit, i),
+                        self.state.solver.ULE(limit, i),
                     )
                 else:
                     byte_constraint = self.state.solver.Or(
-                        a_byte == b_byte, self.state.solver.ULT(a_len, i), self.state.solver.ULT(limit, i)
+                        a_byte == b_byte, self.state.solver.ULT(a_len, i), self.state.solver.ULE(limit, i)
                     )
                 match_constraints.append(byte_constraint)
 
diff --git a/angr/procedures/libc/strstr.py b/angr/procedures/libc/strstr.py
--- a/angr/procedures/libc/strstr.py
+++ b/angr/procedures/libc/strstr.py
@@ -32,9 +32,12 @@ def run(self, haystack_addr, needle_addr, haystack_strlen=None, needle_strlen=No
         if self.state.solver.symbolic(needle_strlen.ret_expr):
             cases = [[needle_strlen.ret_expr == 0, haystack_addr]]
             exclusions = [needle_strlen.ret_expr != 0]
+            not_terminated_yet_constraints = []
             remaining_symbolic = self.state.libc.max_symbolic_strstr
+
             for i in range(haystack_maxlen):
                 l.debug("... case %d (%d symbolic checks remaining)", i, remaining_symbolic)
+                not_terminated_yet_constraints.append(self.state.memory.load(haystack_addr + i, 1) != 0)
 
                 # big hack!
                 cmp_res = self.inline_call(
@@ -45,18 +48,20 @@ def run(self, haystack_addr, needle_addr, haystack_strlen=None, needle_strlen=No
                     a_len=haystack_strlen,
                     b_len=needle_strlen,
                 )
+
                 c = self.state.solver.And(
                     *(
                         [self.state.solver.UGE(haystack_strlen.ret_expr, needle_strlen.ret_expr), cmp_res.ret_expr == 0]
+                        + not_terminated_yet_constraints
                         + exclusions
                     )
                 )
+
                 exclusions.append(cmp_res.ret_expr != 0)
 
                 if self.state.solver.symbolic(c):
                     remaining_symbolic -= 1
 
-                # print "CASE:", c
                 cases.append([c, haystack_addr + i])
                 haystack_strlen.ret_expr = haystack_strlen.ret_expr - 1
 
diff --git a/angr/storage/memory_mixins/smart_find_mixin.py b/angr/storage/memory_mixins/smart_find_mixin.py
--- a/angr/storage/memory_mixins/smart_find_mixin.py
+++ b/angr/storage/memory_mixins/smart_find_mixin.py
@@ -5,10 +5,14 @@
 
 
 class SmartFindMixin(MemoryMixin):
+    """
+    Memory mixin providing basic searching over concrete and symbolic data.
+    """
+
     def find(
         self,
         addr,
-        needle,
+        data,
         max_search,
         default=None,
         endness=None,
@@ -23,6 +27,7 @@ def find(
             if endness is None:
                 endness = "Iend_BE"
 
+        needle = data
         char_num = self._calc_char_num(needle, char_size)
 
         # chunk_size is the number of bytes to cache in memory for comparison
@@ -39,7 +44,7 @@ def find(
                     addr, char_num, char_size, chunk_size, max_search, endness, condition, max_symbolic_bytes, **kwargs
                 )
             ):
-                comparison, concrete_comparison = self._find_compare(element, needle, **kwargs)
+                comparison, concrete_comparison = self._find_compare(element, needle)
 
                 if concrete_comparison is False:
                     continue
@@ -71,9 +76,6 @@ def find(
                 )
             raise
 
-        if len(cases) == 1:
-            return cases[0][1], constraints, match_indices
-
         return self._find_process_cases(cases, match_indices, constraints, default, **kwargs)
 
     def _calc_char_num(self, needle, char_size):
@@ -109,7 +111,7 @@ def _find_iter_items(
                     subaddr,
                     size=char_size * (chunk_size + char_num),
                     endness=endness,
-                    condition=condition & self._find_condition(addr + i, **kwargs),
+                    condition=condition & self._find_condition(addr + i),
                     **kwargs,
                 )
                 chunk_progress = 0
@@ -134,11 +136,11 @@ def _find_are_bytes_symbolic(self, b):
             return True
         return len(self.state.solver.eval_upto(b, 2)) > 1
 
-    def _find_condition(self, target_addr, **kwargs):
+    def _find_condition(self, target_addr):  # pylint:disable=unused-argument,no-self-use
         # TODO: fill this in in order to make each load have the correct condition associated with it
         return claripy.true
 
-    def _find_compare(self, element, target, **kwargs):
+    def _find_compare(self, element, target):
         comparison = element == target
         if self.state.solver.is_true(comparison):
             concrete_comparison = True
@@ -148,7 +150,7 @@ def _find_compare(self, element, target, **kwargs):
             concrete_comparison = None
         return comparison, concrete_comparison
 
-    def _find_process_cases(self, cases, match_indices, constraints, default, **kwargs):
+    def _find_process_cases(self, cases, match_indices, constraints, default):
         if default is None:
             default = claripy.BVV(0, self.state.arch.bits)
         if cases and cases[-1][0].is_true():


Test patch:
diff --git a/tests/test_str_funcs.py b/tests/test_str_funcs.py
--- a/tests/test_str_funcs.py
+++ b/tests/test_str_funcs.py
@@ -1,96 +1,88 @@
-import angr
-
-import logging
-
-l = logging.getLogger("angr_tests")
+# pylint: disable=missing-class-docstring,no-self-use,line-too-long
 
 import os
+import unittest
+import angr
 
 test_location = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..", "..", "binaries", "tests")
 
 
-def test_strncpy():
-    # auto_load_libs can't be disabled as the test cases failed.
-    strncpy_amd64 = angr.Project(
-        os.path.join(test_location, "x86_64", "strncpy"),
-        load_options={"auto_load_libs": True},
-        exclude_sim_procedures_list=["strncpy"],
-    )
-    explorer = strncpy_amd64.factory.simulation_manager()
-    explorer.explore(find=[0x4005FF])
-    s = explorer.found[0]
-    result = s.solver.eval(s.memory.load(s.regs.rax, 16), cast_to=bytes)
-    assert result == b"why hello there\x00"
-
-
-def test_strncpy_size():
-    # auto_load_libs can't be disabled as the test cases failed.
-    strncpy_size_amd64 = angr.Project(
-        os.path.join(test_location, "x86_64", "strncpy-size"),
-        load_options={"auto_load_libs": True},
-        exclude_sim_procedures_list=["strncpy"],
-    )
-    explorer = strncpy_size_amd64.factory.simulation_manager()
-    cfg = strncpy_size_amd64.analyses.CFG(objects=[strncpy_size_amd64.loader.main_object], normalize=True)
-    explorer.use_technique(angr.exploration_techniques.LoopSeer(cfg=cfg, bound=50))
-    explorer.explore(find=[0x40064C])
-    s = explorer.found[0]
-    result = s.solver.eval(s.memory.load(s.regs.rax, 40), cast_to=bytes)
-    assert (
-        result
-        == b"just testing things\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
-    )
-
-
-def test_strncpy_verify_null():
-    # auto_load_libs can't be disabled as the test cases failed.
-    strncpy_verify_null_amd64 = angr.Project(
-        os.path.join(test_location, "x86_64", "strncpy-verify-null"),
-        load_options={"auto_load_libs": True},
-        exclude_sim_procedures_list=["strncpy"],
-    )
-    explorer = strncpy_verify_null_amd64.factory.simulation_manager()
-    cfg = strncpy_verify_null_amd64.analyses.CFG(objects=[strncpy_verify_null_amd64.loader.main_object], normalize=True)
-    explorer.use_technique(angr.exploration_techniques.LoopSeer(cfg=cfg, bound=50))
-    explorer.explore(find=[0x40064C])
-    s = explorer.found[0]
-    result = s.solver.eval(s.memory.load(s.regs.rax, 40), cast_to=bytes)
-    assert result == b"just testing things\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00AAAAAA\x00"
-
-
-def test_strstr_and_strncpy():
-    # auto_load_libs can't be disabled as the test cases failed.
-    strstr_and_strncpy_amd64 = angr.Project(
-        os.path.join(test_location, "x86_64", "strstr_and_strncpy"),
-        load_options={"auto_load_libs": True},
-        exclude_sim_procedures_list=["strstr"],
-    )
-    explorer = strstr_and_strncpy_amd64.factory.simulation_manager()
-    cfg = strstr_and_strncpy_amd64.analyses.CFG(objects=[strstr_and_strncpy_amd64.loader.main_object], normalize=True)
-    explorer.use_technique(angr.exploration_techniques.LoopSeer(cfg=cfg, bound=50))
-    explorer.explore(find=[0x400657])
-    s = explorer.found[0]
-    result = s.solver.eval(s.memory.load(s.regs.rax, 15), cast_to=bytes)
-    assert result == b"hi th hi there\x00"
-
-
-def test_strstr():
-    # auto_load_libs can't be disabled as the test cases failed.
-    strstr_amd64 = angr.Project(
-        os.path.join(test_location, "x86_64", "strstr"),
-        load_options={"auto_load_libs": True},
-        exclude_sim_procedures_list=["strstr"],
-    )
-    explorer = strstr_amd64.factory.simulation_manager()
-    explorer.explore(find=[0x4005FB])
-    s = explorer.found[0]
-    result = s.solver.eval(s.memory.load(s.regs.rax, 9), cast_to=bytes)
-    assert result == b"hi there\x00"
+class TestStrFuncs(unittest.TestCase):
+    def test_strncpy(self):
+        # auto_load_libs can't be disabled as the test cases failed.
+        strncpy_amd64 = angr.Project(
+            os.path.join(test_location, "x86_64", "strncpy"),
+            load_options={"auto_load_libs": True},
+            exclude_sim_procedures_list=["strncpy"],
+        )
+        explorer = strncpy_amd64.factory.simulation_manager()
+        explorer.explore(find=[0x4005FF])
+        s = explorer.found[0]
+        result = s.solver.eval(s.memory.load(s.regs.rax, 16), cast_to=bytes)
+        assert result == b"why hello there\0"
+
+    def test_strncpy_size(self):
+        # auto_load_libs can't be disabled as the test cases failed.
+        strncpy_size_amd64 = angr.Project(
+            os.path.join(test_location, "x86_64", "strncpy-size"),
+            load_options={"auto_load_libs": True},
+            exclude_sim_procedures_list=["strncpy"],
+        )
+        explorer = strncpy_size_amd64.factory.simulation_manager()
+        cfg = strncpy_size_amd64.analyses.CFG(objects=[strncpy_size_amd64.loader.main_object], normalize=True)
+        explorer.use_technique(angr.exploration_techniques.LoopSeer(cfg=cfg, bound=50))
+        explorer.explore(find=[0x40064C])
+        s = explorer.found[0]
+        result = s.solver.eval(s.memory.load(s.regs.rax, 40), cast_to=bytes)
+        assert result == b"just testing things\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+
+    def test_strncpy_verify_null(self):
+        # auto_load_libs can't be disabled as the test cases failed.
+        strncpy_verify_null_amd64 = angr.Project(
+            os.path.join(test_location, "x86_64", "strncpy-verify-null"),
+            load_options={"auto_load_libs": True},
+            exclude_sim_procedures_list=["strncpy"],
+        )
+        explorer = strncpy_verify_null_amd64.factory.simulation_manager()
+        cfg = strncpy_verify_null_amd64.analyses.CFG(
+            objects=[strncpy_verify_null_amd64.loader.main_object], normalize=True
+        )
+        explorer.use_technique(angr.exploration_techniques.LoopSeer(cfg=cfg, bound=50))
+        explorer.explore(find=[0x40064C])
+        s = explorer.found[0]
+        result = s.solver.eval(s.memory.load(s.regs.rax, 40), cast_to=bytes)
+        assert result == b"just testing things\0\0\0\0\0\0\0\0\0\0\0\0\0\0AAAAAA\0"
+
+    def test_strstr_and_strncpy(self):
+        # auto_load_libs can't be disabled as the test cases failed.
+        strstr_and_strncpy_amd64 = angr.Project(
+            os.path.join(test_location, "x86_64", "strstr_and_strncpy"),
+            load_options={"auto_load_libs": True},
+            exclude_sim_procedures_list=["strstr"],
+        )
+        explorer = strstr_and_strncpy_amd64.factory.simulation_manager()
+        cfg = strstr_and_strncpy_amd64.analyses.CFG(
+            objects=[strstr_and_strncpy_amd64.loader.main_object], normalize=True
+        )
+        explorer.use_technique(angr.exploration_techniques.LoopSeer(cfg=cfg, bound=50))
+        explorer.explore(find=[0x400657])
+        s = explorer.found[0]
+        result = s.solver.eval(s.memory.load(s.regs.rax, 15), cast_to=bytes)
+        assert result == b"hi th hi there\0"
+
+    def test_strstr(self):
+        # auto_load_libs can't be disabled as the test cases failed.
+        strstr_amd64 = angr.Project(
+            os.path.join(test_location, "x86_64", "strstr"),
+            load_options={"auto_load_libs": True},
+            exclude_sim_procedures_list=["strstr"],
+        )
+        explorer = strstr_amd64.factory.simulation_manager()
+        explorer.explore(find=[0x4005FB])
+        s = explorer.found[0]
+        result = s.solver.eval(s.memory.load(s.regs.rax, 9), cast_to=bytes)
+        assert result == b"hi there\0"
 
 
 if __name__ == "__main__":
-    test_strncpy()
-    test_strncpy_size()
-    test_strncpy_verify_null()
-    test_strstr_and_strncpy()
-    test_strstr()
+    unittest.main()
diff --git a/tests/test_string.py b/tests/test_string.py
--- a/tests/test_string.py
+++ b/tests/test_string.py
@@ -1,10 +1,24 @@
-import sys
+# pylint: disable=missing-class-docstring,no-self-use,line-too-long
+
+import logging
 import random
-import angr
+import unittest
+from itertools import combinations
 
+import angr
 from angr import SimState, SIM_LIBRARIES
 
-FAKE_ADDR = 0x100000
+from common import broken
+
+log = logging.getLogger("angr.tests.string")
+
+
+def make_state_with_stdin(content):
+    s = SimState(arch="AMD64", mode="symbolic")
+    stdin_storage = angr.storage.file.SimFile("stdin", content=content)
+    stdin = angr.storage.file.SimFileDescriptor(stdin_storage)
+    s.register_plugin("posix", angr.state_plugins.SimSystemPosix(stdin=stdin_storage, fd={0: stdin}))
+    return s
 
 
 def make_func(name):
@@ -34,955 +48,1052 @@ def make_func(name):
 scanf = make_func("scanf")
 wcscmp = make_func("wcscmp")
 
-import logging
-
-l = logging.getLogger("angr.tests.string")
-
-
-def make_state_with_stdin(content):
-    s = SimState(arch="AMD64", mode="symbolic")
-    stdin_storage = angr.storage.file.SimFile("stdin", content=content)
-    stdin = angr.storage.file.SimFileDescriptor(stdin_storage)
-    s.register_plugin("posix", angr.state_plugins.SimSystemPosix(stdin=stdin_storage, fd={0: stdin}))
-    return s
-
-
-def test_inline_strlen():
-    s = SimState(arch="AMD64", mode="symbolic")
-
-    l.info("fully concrete string")
-    a_str = s.solver.BVV(0x41414100, 32)
-    a_addr = s.solver.BVV(0x10, 64)
-    s.memory.store(a_addr, a_str, endness="Iend_BE")
-    a_len = strlen(s, arguments=[a_addr])
-    assert s.solver.unique(a_len)
-    assert s.solver.eval(a_len) == 3
-
-    l.info("concrete-terminated string")
-    b_str = s.solver.Concat(s.solver.BVS("mystring", 24), s.solver.BVV(0, 8))
-    b_addr = s.solver.BVV(0x20, 64)
-    s.memory.store(b_addr, b_str, endness="Iend_BE")
-    b_len = strlen(s, arguments=[b_addr])
-    assert s.solver.max_int(b_len) == 3
-    assert tuple(sorted(s.solver.eval_upto(b_len, 10))) == (0, 1, 2, 3)
-
-    l.info("fully unconstrained")
-    u_addr = s.solver.BVV(0x50, 64)
-    u_len_sp = strlen(s, arguments=[u_addr])
-    u_len = u_len_sp
-    assert len(s.solver.eval_upto(u_len, 100)) == s.libc.buf_symbolic_bytes
-    assert s.solver.max_int(u_len) == s.libc.buf_symbolic_bytes - 1
-    # print u_len_sp.solver.maximum_null
-
-    # s.add_constraints(u_len < 16)
-
-    assert s.solver.eval_upto(s.memory.load(0x50 + u_len, 1), 300) == [0]
-    #
-    # This tests if a strlen can influence a symbolic str.
-    #
-    l.info("Trying to influence length.")
-    s = SimState(arch="AMD64", mode="symbolic")
-    str_c = s.solver.BVS("some_string", 8 * 16)
-    c_addr = s.solver.BVV(0x10, 64)
-    s.memory.store(c_addr, str_c, endness="Iend_BE")
-    c_len = strlen(s, arguments=[c_addr])
-    assert len(s.solver.eval_upto(c_len, 100)) == s.libc.buf_symbolic_bytes
-    assert s.solver.max_int(c_len) == s.libc.buf_symbolic_bytes - 1
-
-    one_s = s.copy()
-    one_s.add_constraints(c_len == 1)
-    assert one_s.solver.eval(str_c, cast_to=bytes).index(b"\x00") == 1
-    str_test = one_s.memory.load(c_addr, 2, endness="Iend_BE")
-    assert len(one_s.solver.eval_upto(str_test, 300, cast_to=bytes)) == 255
-
-    for i in range(16):
-        test_s = s.copy()
-        test_s.add_constraints(c_len == i)
-        str_test = test_s.memory.load(c_addr, i + 1, endness="Iend_BE")
-        assert test_s.solver.eval(str_test, cast_to=bytes).index(b"\x00") == i
-        for j in range(i):
-            assert not test_s.solver.unique(test_s.memory.load(c_addr + j, 1))
-
-
-def test_inline_strcmp():
-    s = SimState(arch="AMD64", mode="symbolic")
-    str_a = s.solver.BVV(0x41414100, 32)
-    str_b = s.solver.BVS("mystring", 32)
-
-    a_addr = s.solver.BVV(0x10, 64)
-    b_addr = s.solver.BVV(0xB0, 64)
-
-    s.memory.store(a_addr, str_a, endness="Iend_BE")
-    s.memory.store(b_addr, str_b, endness="Iend_BE")
-
-    s_cmp = s.copy()
-    cmpres = strcmp(s_cmp, arguments=[a_addr, b_addr])
-    s_match = s_cmp.copy()
-    s_nomatch = s_cmp.copy()
-    s_match.add_constraints(cmpres == 0)
-    s_nomatch.add_constraints(cmpres != 0)
-
-    assert s_match.solver.unique(str_b)
-    assert not s_nomatch.solver.unique(str_b)
-    assert s_match.solver.eval(str_b, cast_to=bytes) == b"AAA\x00"
-
-    s_ncmp = s.copy()
-    ncmpres = strncmp(s_ncmp, arguments=[a_addr, b_addr, s.solver.BVV(2, s.arch.bits)])
-    s_match = s_ncmp.copy()
-    s_nomatch = s_ncmp.copy()
-    s_match.add_constraints(ncmpres == 0)
-    s_nomatch.add_constraints(ncmpres != 0)
-
-    assert not s_match.solver.unique(str_b)
-    assert s_match.solver.unique(s_match.memory.load(b_addr, 2))
-    assert len(s_match.solver.eval_upto(s_match.memory.load(b_addr, 3), 300)) == 256
-    assert not s_nomatch.solver.unique(str_b)
-
-    l.info("concrete a, symbolic b")
-    s = SimState(arch="AMD64", mode="symbolic")
-    str_a = s.solver.BVV(0x41424300, 32)
-    str_b = s.solver.BVS("mystring", 32)
-    a_addr = s.solver.BVV(0x10, 64)
-    b_addr = s.solver.BVV(0xB0, 64)
-    s.memory.store(a_addr, str_a, endness="Iend_BE")
-    s.memory.store(b_addr, str_b, endness="Iend_BE")
-
-    s_cmp = s.copy()
-    cmpres = strncmp(s_cmp, arguments=[a_addr, b_addr, s.solver.BVV(2, s_cmp.arch.bits)])
-    s_match = s_cmp.copy()
-    s_nomatch = s_cmp.copy()
-    s_match.add_constraints(cmpres == 0)
-    s_nomatch.add_constraints(cmpres != 0)
-
-    assert s_match.solver.solution(str_b, 0x41420000)
-    assert s_match.solver.solution(str_b, 0x41421234)
-    assert s_match.solver.solution(str_b, 0x41424300)
-    assert not s_nomatch.solver.solution(str_b, 0x41420000)
-    assert not s_nomatch.solver.solution(str_b, 0x41421234)
-    assert not s_nomatch.solver.solution(str_b, 0x41424300)
-
-    l.info("symbolic a, symbolic b")
-    s = SimState(arch="AMD64", mode="symbolic")
-    a_addr = s.solver.BVV(0x10, 64)
-    b_addr = s.solver.BVV(0xB0, 64)
-
-    s_cmp = s.copy()
-    cmpres = strcmp(s_cmp, arguments=[a_addr, b_addr])
-    s_match = s_cmp.copy()
-    s_nomatch = s_cmp.copy()
-    s_match.add_constraints(cmpres == 0)
-    s_nomatch.add_constraints(cmpres != 0)
-
-    m_res = strcmp(s_match, arguments=[a_addr, b_addr])
-    s_match.add_constraints(m_res != 0)
-    nm_res = strcmp(s_nomatch, arguments=[a_addr, b_addr])
-    s_nomatch.add_constraints(nm_res == 0)
-
-    assert not s_match.satisfiable()
-    assert not s_match.satisfiable()
-
-
-def test_inline_strncmp():
-    l.info("symbolic left, symbolic right, symbolic len")
-    s = SimState(arch="AMD64", mode="symbolic")
-    left = s.solver.BVS("left", 32)
-    left_addr = s.solver.BVV(0x1000, 64)
-    right = s.solver.BVS("right", 32)
-    right_addr = s.solver.BVV(0x2000, 64)
-    maxlen = s.solver.BVS("len", 64)
-
-    s.memory.store(left_addr, left)
-    s.memory.store(right_addr, right)
-
-    s.add_constraints(strlen(s, arguments=[left_addr]) == 3)
-    s.add_constraints(strlen(s, arguments=[right_addr]) == 0)
-
-    s.add_constraints(maxlen != 0)
-    c = strncmp(s, arguments=[left_addr, right_addr, maxlen])
-
-    s_match = s.copy()
-    s_match.add_constraints(c == 0)
-    assert not s_match.satisfiable()
-    # assert s_match.solver.min_int(maxlen) == 3
-
-    s_nomatch = s.copy()
-    s_nomatch.add_constraints(c != 0)
-    assert s_nomatch.satisfiable()
-    # assert s_nomatch.solver.max_int(maxlen) == 2
-
-    l.info("zero-length")
-    s = SimState(arch="AMD64", mode="symbolic")
-    left = s.solver.BVS("left", 32)
-    left_addr = s.solver.BVV(0x1000, 64)
-    right = s.solver.BVS("right", 32)
-    right_addr = s.solver.BVV(0x2000, 64)
-    maxlen = s.solver.BVS("len", 64)
-    left_len = strlen(s, arguments=[left_addr])
-    right_len = strlen(s, arguments=[right_addr])
-    c = strncmp(s, arguments=[left_addr, right_addr, maxlen])
-
-    s.add_constraints(right_len == 0)
-    s.add_constraints(left_len == 0)
-    # s.add_constraints(c == 0)
-    s.add_constraints(maxlen == 0)
-    assert s.satisfiable()
-
-
-def broken_inline_strstr():
-    l.info("concrete haystack and needle")
-    s = SimState(arch="AMD64", mode="symbolic")
-    str_haystack = s.solver.BVV(0x41424300, 32)
-    str_needle = s.solver.BVV(0x42430000, 32)
-    addr_haystack = s.solver.BVV(0x10, 64)
-    addr_needle = s.solver.BVV(0xB0, 64)
-    s.memory.store(addr_haystack, str_haystack, endness="Iend_BE")
-    s.memory.store(addr_needle, str_needle, endness="Iend_BE")
-
-    ss_res = strstr(s, arguments=[addr_haystack, addr_needle])
-    assert s.solver.unique(ss_res)
-    assert s.solver.eval(ss_res) == 0x11
-
-    l.info("concrete haystack, symbolic needle")
-    s = SimState(arch="AMD64", mode="symbolic")
-    str_haystack = s.solver.BVV(0x41424300, 32)
-    str_needle = s.solver.BVS("wtf", 32)
-    addr_haystack = s.solver.BVV(0x10, 64)
-    addr_needle = s.solver.BVV(0xB0, 64)
-    s.memory.store(addr_haystack, str_haystack, endness="Iend_BE")
-    s.memory.store(addr_needle, str_needle, endness="Iend_BE")
-
-    ss_res = strstr(s, arguments=[addr_haystack, addr_needle])
-    assert not s.solver.unique(ss_res)
-    assert len(s.solver.eval_upto(ss_res, 10)) == 4
-
-    s_match = s.copy()
-    s_nomatch = s.copy()
-    s_match.add_constraints(ss_res != 0)
-    s_nomatch.add_constraints(ss_res == 0)
-
-    match_needle = str_needle[31:16]
-    assert len(s_match.solver.eval_upto(match_needle, 300)) == 259
-    assert len(s_match.solver.eval_upto(str_needle, 10)) == 10
-
-    l.info("symbolic haystack, symbolic needle")
-    s = SimState(arch="AMD64", mode="symbolic")
-    s.libc.buf_symbolic_bytes = 5
-    addr_haystack = s.solver.BVV(0x10, 64)
-    addr_needle = s.solver.BVV(0xB0, 64)
-    len_needle = strlen(s, arguments=[addr_needle])
-
-    ss_res = strstr(s, arguments=[addr_haystack, addr_needle])
-    assert not s.solver.unique(ss_res)
-    assert len(s.solver.eval_upto(ss_res, 100)) == s.libc.buf_symbolic_bytes
-
-    s_match = s.copy()
-    s_nomatch = s.copy()
-    s_match.add_constraints(ss_res != 0)
-    s_nomatch.add_constraints(ss_res == 0)
-
-    match_cmp = strncmp(s_match, arguments=[ss_res, addr_needle, len_needle])
-    assert s_match.solver.eval_upto(match_cmp, 10) == [0]
-
-    r_mm = strstr(s_match, arguments=[addr_haystack, addr_needle])
-    s_match.add_constraints(r_mm == 0)
-    assert not s_match.satisfiable()
-
-    assert s_nomatch.satisfiable()
-    s_nss = s_nomatch.copy()
-    nomatch_ss = strstr(s_nss, arguments=[addr_haystack, addr_needle])
-    s_nss.add_constraints(nomatch_ss != 0)
-    assert not s_nss.satisfiable()
-
-
-def test_strstr_inconsistency():
-    l.info("symbolic haystack, symbolic needle")
-    s = SimState(arch="AMD64", mode="symbolic")
-    s.libc.buf_symbolic_bytes = 2
-    addr_haystack = s.solver.BVV(0x10, 64)
-    addr_needle = s.solver.BVV(0xB0, 64)
-    # len_needle = strlen(s, inline=True, arguments=[addr_needle])
-
-    ss_res = strstr(s, arguments=[addr_haystack, addr_needle])
-
-    # slh_res = strlen(s, inline=True, arguments=[addr_haystack])
-    # sln_res = strlen(s, inline=True, arguments=[addr_needle])
-    # print "LENH:", s.solver.eval_upto(slh_res, 100)
-    # print "LENN:", s.solver.eval_upto(sln_res, 100)
-
-    assert not s.solver.unique(ss_res)
-    assert sorted(s.solver.eval_upto(ss_res, 100)) == [0] + list(range(0x10, 0x10 + s.libc.buf_symbolic_bytes - 1))
-
-    s.add_constraints(ss_res != 0)
-    ss2 = strstr(s, arguments=[addr_haystack, addr_needle])
-    s.add_constraints(ss2 == 0)
-    assert not s.satisfiable()
-
-
-def test_memcpy():
-    l.info("concrete src, concrete dst, concrete len")
-    l.debug("... full copy")
-    s = SimState(arch="AMD64", mode="symbolic")
-    dst = s.solver.BVV(0x41414141, 32)
-    dst_addr = s.solver.BVV(0x1000, 64)
-    src = s.solver.BVV(0x42424242, 32)
-    src_addr = s.solver.BVV(0x2000, 64)
-
-    s.memory.store(dst_addr, dst)
-    s.memory.store(src_addr, src)
-    memcpy(s, arguments=[dst_addr, src_addr, s.solver.BVV(4, 64)])
-    new_dst = s.memory.load(dst_addr, 4, endness="Iend_BE")
-    assert s.solver.eval_upto(new_dst, 2, cast_to=bytes) == [b"BBBB"]
-
-    l.info("giant copy")
-    s = SimState(arch="AMD64", mode="symbolic", remove_options=angr.options.simplification)
-    s.memory._maximum_symbolic_size = 0x2000000
-    size = s.solver.BVV(0x1000000, 64)
-    data = s.solver.BVS("giant", 8 * 0x1_000_000)
-    dst_addr = s.solver.BVV(0x2000000, 64)
-    src_addr = s.solver.BVV(0x4000000, 64)
-    s.memory.store(src_addr, data)
-
-    memcpy(s, arguments=[dst_addr, src_addr, size])
-    assert s.memory.load(dst_addr, size) is s.memory.load(src_addr, size)
-
-    l.debug("... partial copy")
-    s = SimState(arch="AMD64", mode="symbolic")
-    s.memory.store(dst_addr, dst)
-    s.memory.store(src_addr, src)
-    memcpy(s, arguments=[dst_addr, src_addr, s.solver.BVV(2, 64)])
-    new_dst = s.memory.load(dst_addr, 4, endness="Iend_BE")
-    assert s.solver.eval_upto(new_dst, 2, cast_to=bytes) == [b"BBAA"]
-
-    l.info("symbolic src, concrete dst, concrete len")
-    s = SimState(arch="AMD64", mode="symbolic")
-    dst = s.solver.BVV(0x41414141, 32)
-    dst_addr = s.solver.BVV(0x1000, 64)
-    src = s.solver.BVS("src", 32)
-    src_addr = s.solver.BVV(0x2000, 64)
-
-    s.memory.store(dst_addr, dst)
-    s.memory.store(src_addr, src)
-
-    # make sure it copies it all
-    memcpy(s, arguments=[dst_addr, src_addr, s.solver.BVV(4, 64)])
-    assert s.satisfiable()
-    s.add_constraints(src != s.memory.load(dst_addr, 4))
-    assert not s.satisfiable()
-
-    l.info("symbolic src, concrete dst, symbolic len")
-    s = SimState(arch="AMD64", mode="symbolic")
-    dst = s.solver.BVV(0x41414141, 32)
-    dst_addr = s.solver.BVV(0x1000, 64)
-    src = s.solver.BVS("src", 32)
-    src_addr = s.solver.BVV(0x2000, 64)
-    cpylen = s.solver.BVS("len", 64)
-
-    s.memory.store(dst_addr, dst)
-    s.memory.store(src_addr, src)
-    s.add_constraints(cpylen < 10)
-    memcpy(s, arguments=[dst_addr, src_addr, cpylen])
-    result = s.memory.load(dst_addr, 4, endness="Iend_BE")
-
-    # make sure it copies it all
-    s1 = s.copy()
-    s1.add_constraints(cpylen == 1)
-    assert s1.solver.unique(s1.memory.load(dst_addr + 1, 3))
-    assert len(s1.solver.eval_upto(s1.memory.load(dst_addr, 1), 300)) == 256
-
-    s2 = s.copy()
-    s2.add_constraints(cpylen == 2)
-    assert len(s2.solver.eval_upto(result[31:24], 300)) == 256
-    assert len(s2.solver.eval_upto(result[23:16], 300)) == 256
-    assert s2.solver.eval_upto(result[15:0], 300, cast_to=bytes) == [b"AA"]
-
-    l.info("concrete src, concrete dst, symbolic len")
-    dst = s2.solver.BVV(0x41414141, 32)
-    dst_addr = s2.solver.BVV(0x1000, 64)
-    src = s2.solver.BVV(0x42424242, 32)
-    src_addr = s2.solver.BVV(0x2000, 64)
-
-    s = SimState(arch="AMD64", mode="symbolic")
-    s.memory.store(dst_addr, dst)
-    s.memory.store(src_addr, src)
-    cpylen = s.solver.BVS("len", 64)
-
-    s.add_constraints(s.solver.ULE(cpylen, 4))
-    memcpy(s, arguments=[dst_addr, src_addr, cpylen])
-    new_dst = s.memory.load(dst_addr, 4, endness="Iend_BE")
-    assert sorted(s.solver.eval_upto(new_dst, 300, cast_to=bytes)) == [b"AAAA", b"BAAA", b"BBAA", b"BBBA", b"BBBB"]
-
-
-def test_memcmp():
-    l.info("concrete src, concrete dst, concrete len")
-
-    l.debug("... full cmp")
-    s = SimState(arch="AMD64", mode="symbolic")
-    dst = s.solver.BVV(0x41414141, 32)
-    dst_addr = s.solver.BVV(0x1000, 64)
-    src = s.solver.BVV(0x42424242, 32)
-    src_addr = s.solver.BVV(0x2000, 64)
-    s.memory.store(dst_addr, dst)
-    s.memory.store(src_addr, src)
-    r = memcmp(s, arguments=[dst_addr, src_addr, s.solver.BVV(4, 64)])
-    assert s.satisfiable()
-
-    s_pos = s.copy()
-    s_pos.add_constraints(r.SGE(0))
-    assert not s_pos.satisfiable()
-
-    s_neg = s.copy()
-    s_neg.add_constraints(r.SLT(0))
-    assert s_neg.satisfiable()
-
-    l.debug("... zero cmp")
-    s = SimState(arch="AMD64", mode="symbolic")
-    s.memory.store(dst_addr, dst)
-    s.memory.store(src_addr, src)
-    r = memcmp(s, arguments=[dst_addr, src_addr, s.solver.BVV(0, 64)])
-    assert s.solver.eval_upto(r, 2) == [0]
-
-    l.info("symbolic src, concrete dst, concrete len")
-    s = SimState(arch="AMD64", mode="symbolic")
-    dst = s.solver.BVV(0x41414141, 32)
-    dst_addr = s.solver.BVV(0x1000, 64)
-    src = s.solver.BVS("src", 32)
-
-    src_addr = s.solver.BVV(0x2000, 64)
-
-    s.memory.store(dst_addr, dst)
-    s.memory.store(src_addr, src)
-
-    # make sure it copies it all
-    r = memcmp(s, arguments=[dst_addr, src_addr, s.solver.BVV(4, 64)])
-
-    s_match = s.copy()
-    s_match.add_constraints(r == 0)
-    m = s_match.memory.load(src_addr, 4)
-    assert s_match.solver.eval_upto(m, 2) == [0x41414141]
-
-    s_nomatch = s.copy()
-    s_nomatch.add_constraints(r != 0)
-    m = s_nomatch.memory.load(src_addr, 4)
-    assert not s_nomatch.solver.solution(m, 0x41414141)
-
-    l.info("symbolic src, concrete dst, symbolic len")
-    s = SimState(arch="AMD64", mode="symbolic")
-    dst = s.solver.BVV(0x41414141, 32)
-    dst_addr = s.solver.BVV(0x1000, 64)
-    src = s.solver.BVS("src", 32)
-    src_addr = s.solver.BVV(0x2000, 64)
-    cmplen = s.solver.BVS("len", 64)
-
-    s.memory.store(dst_addr, dst)
-    s.memory.store(src_addr, src)
-    r = memcmp(s, arguments=[dst_addr, src_addr, cmplen])
-
-    # look at effects of different lengths
-    s1 = s.copy()
-    s1.add_constraints(cmplen == 1)
-    s1.add_constraints(r == 0)
-    l.debug("... simplifying")
-    s1.solver._solver.simplify()
-    l.debug("... solving")
-    assert s1.solver.eval_upto(src[31:24], 2) == [0x41]
-    assert not s1.solver.unique(src[31:16])
-    l.debug("... solved")
-
-    s2 = s.copy()
-    s2.add_constraints(cmplen == 2)
-    s2.add_constraints(r == 0)
-    assert s2.solver.eval_upto(s2.memory.load(src_addr, 2), 2) == [0x4141]
-    assert not s2.solver.unique(s2.memory.load(src_addr, 3))
-
-    s2u = s.copy()
-    s2u.add_constraints(cmplen == 2)
-    s2u.add_constraints(r == 1)
-    assert not s2u.solver.solution(s2u.memory.load(src_addr, 2), 0x4141)
-
-
-def test_strncpy():
-    l.info("concrete src, concrete dst, concrete len")
-    l.debug("... full copy")
-    s = SimState(arch="AMD64", mode="symbolic")
-    dst = s.solver.BVV(0x41414100, 32)
-    dst_addr = s.solver.BVV(0x1000, 64)
-    src = s.solver.BVV(0x42420000, 32)
-    src_addr = s.solver.BVV(0x2000, 64)
-
-    s.memory.store(dst_addr, dst)
-    s.memory.store(src_addr, src)
-    strncpy(s, arguments=[dst_addr, src_addr, s.solver.BVV(3, 64)])
-    new_dst = s.memory.load(dst_addr, 4, endness="Iend_BE")
-    assert s.solver.eval(new_dst, cast_to=bytes) == b"BB\x00\x00"
-
-    l.debug("... partial copy")
-    s = SimState(arch="AMD64", mode="symbolic")
-    s.memory.store(dst_addr, dst)
-    s.memory.store(src_addr, src)
-    strncpy(s, arguments=[dst_addr, src_addr, s.solver.BVV(2, 64)])
-    new_dst = s.memory.load(dst_addr, 4, endness="Iend_BE")
-    assert s.solver.eval_upto(new_dst, 2, cast_to=bytes) == [b"BBA\x00"]
-
-    l.info("symbolic src, concrete dst, concrete len")
-    s = SimState(arch="AMD64", mode="symbolic")
-    dst = s.solver.BVV(0x41414100, 32)
-    dst_addr = s.solver.BVV(0x1000, 64)
-    src = s.solver.BVS("src", 32)
-    src_addr = s.solver.BVV(0x2000, 64)
-
-    s.memory.store(dst_addr, dst)
-    s.memory.store(src_addr, src)
-
-    # make sure it copies it all
-    s.add_constraints(strlen(s, arguments=[src_addr]) == 2)
-
-    # sanity check
-    s_false = s.copy()
-    s_false.add_constraints(strlen(s_false, arguments=[src_addr]) == 3)
-    assert not s_false.satisfiable()
-
-    strncpy(s, arguments=[dst_addr, src_addr, 3])
-    assert s.satisfiable()
-    c = strcmp(s, arguments=[dst_addr, src_addr])
-
-    assert s.solver.eval_upto(c, 10) == [0]
-
-    l.info("symbolic src, concrete dst, symbolic len")
-    s = SimState(arch="AMD64", mode="symbolic")
-    dst = s.solver.BVV(0x41414100, 32)
-    dst_addr = s.solver.BVV(0x1000, 64)
-    src = s.solver.BVS("src", 32)
-    src_addr = s.solver.BVV(0x2000, 64)
-    maxlen = s.solver.BVS("len", 64)
-
-    s.memory.store(dst_addr, dst)
-    s.memory.store(src_addr, src)
-
-    # make sure it copies it all
-    s.add_constraints(strlen(s, arguments=[src_addr]) == 2)
-    strncpy(s, arguments=[dst_addr, src_addr, maxlen])
-    c = strcmp(s, arguments=[dst_addr, src_addr])
-
-    s_match = s.copy()
-    s_match.add_constraints(c == 0)
-    assert s_match.solver.min_int(maxlen) == 3
-
-    s_nomatch = s.copy()
-    s_nomatch.add_constraints(c != 0)
-    assert s_nomatch.solver.max_int(maxlen) == 2
-
-    l.info("concrete src, concrete dst, symbolic len")
-    l.debug("... full copy")
-    s = SimState(arch="AMD64", mode="symbolic")
-
-    dst = s.solver.BVV(0x41414100, 32)
-    dst_addr = s.solver.BVV(0x1000, 64)
-    src = s.solver.BVV(0x42420000, 32)
-    src_addr = s.solver.BVV(0x2000, 64)
-    maxlen = s.solver.BVS("len", 64)
-
-    s.memory.store(dst_addr, dst)
-    s.memory.store(src_addr, src)
-    strncpy(s, arguments=[dst_addr, src_addr, maxlen])
-    r = s.memory.load(dst_addr, 4, endness="Iend_BE")
-    # print repr(r.solver.eval_upto(r, 10, cast_to=bytes))
-    assert sorted(s.solver.eval_upto(r, 10, cast_to=bytes)) == [b"AAA\x00", b"BAA\x00", b"BB\x00\x00", b"BBA\x00"]
-
-
-def test_strcpy():
-    l.info("concrete src, concrete dst")
-
-    l.debug("... full copy")
-    s = SimState(arch="AMD64", mode="symbolic")
-    dst = s.solver.BVV(0x41414100, 32)
-    dst_addr = s.solver.BVV(0x1000, 64)
-    src = s.solver.BVV(0x42420000, 32)
-    src_addr = s.solver.BVV(0x2000, 64)
-    s.memory.store(dst_addr, dst)
-    s.memory.store(src_addr, src)
-    strcpy(s, arguments=[dst_addr, src_addr])
-    new_dst = s.memory.load(dst_addr, 4, endness="Iend_BE")
-    assert s.solver.eval(new_dst, cast_to=bytes) == b"BB\x00\x00"
-
-    l.info("symbolic src, concrete dst")
-    dst = s.solver.BVV(0x41414100, 32)
-    dst_addr = s.solver.BVV(0x1000, 64)
-    src = s.solver.BVS("src", 32)
-    src_addr = s.solver.BVV(0x2000, 64)
-
-    s = SimState(arch="AMD64", mode="symbolic")
-    s.memory.store(dst_addr, dst)
-    s.memory.store(src_addr, src)
-
-    ln = strlen(s, arguments=[src_addr])
-
-    strcpy(s, arguments=[dst_addr, src_addr])
-
-    cm = strcmp(s, arguments=[dst_addr, src_addr])
-
-    s.add_constraints(cm == 0)
-
-    s.add_constraints(ln == 15)
-    # readsize = 16
-    # both_strs = s.solver.Concat(
-    #     *[s.memory.load(dst_addr, readsize, endness="Iend_BE"), s.memory.load(src_addr, readsize, endness="Iend_BE")]
-    # )
-    # for i in s.solver.eval_upto(both_strs, 50, cast_to=bytes):
-    #     assert s.solver.eval_upto(10) == [0]
-    #     assert s.solver.solution(s.memory.load(dst_addr, 4, endness="Iend_BE"), 0x42434400)
-    #     assert s.solver.solution(s.memory.load(dst_addr, 4, endness="Iend_BE"), 0x42434445)
-    #     assert s.solver.solution(s.memory.load(dst_addr, 4, endness="Iend_BE"), 0x00414100)
-    #     assert not s.solver.solution(s.memory.load(dst_addr, 4, endness="Iend_BE"), 0x00010203)
 
+class TestStringSimProcedures(unittest.TestCase):
+    def test_strlen(self):
+        s = SimState(arch="AMD64", mode="symbolic")
+
+        log.info("fully concrete string")
+        a_str = s.solver.BVV(0x41414100, 32)
+        a_addr = s.solver.BVV(0x10, 64)
+        s.memory.store(a_addr, a_str, endness="Iend_BE")
+        a_len = strlen(s, arguments=[a_addr])
+        assert s.solver.unique(a_len)
+        assert s.solver.eval(a_len) == 3
+
+        log.info("concrete-terminated string")
+        b_str = s.solver.Concat(s.solver.BVS("mystring", 24), s.solver.BVV(0, 8))
+        b_addr = s.solver.BVV(0x20, 64)
+        s.memory.store(b_addr, b_str, endness="Iend_BE")
+        b_len = strlen(s, arguments=[b_addr])
+        assert s.solver.max_int(b_len) == 3
+        assert tuple(sorted(s.solver.eval_upto(b_len, 10))) == (0, 1, 2, 3)
+
+        log.info("fully unconstrained")
+        u_addr = s.solver.BVV(0x50, 64)
+        u_len_sp = strlen(s, arguments=[u_addr])
+        u_len = u_len_sp
+        assert len(s.solver.eval_upto(u_len, 100)) == s.libc.buf_symbolic_bytes
+        assert s.solver.max_int(u_len) == s.libc.buf_symbolic_bytes - 1
+        # print u_len_sp.solver.maximum_null
+
+        # s.add_constraints(u_len < 16)
+
+        assert s.solver.eval_upto(s.memory.load(0x50 + u_len, 1), 300) == [0]
+        #
+        # This tests if a strlen can influence a symbolic str.
+        #
+        log.info("Trying to influence length.")
+        s = SimState(arch="AMD64", mode="symbolic")
+        str_c = s.solver.BVS("some_string", 8 * 16)
+        c_addr = s.solver.BVV(0x10, 64)
+        s.memory.store(c_addr, str_c, endness="Iend_BE")
+        c_len = strlen(s, arguments=[c_addr])
+        assert len(s.solver.eval_upto(c_len, 100)) == s.libc.buf_symbolic_bytes
+        assert s.solver.max_int(c_len) == s.libc.buf_symbolic_bytes - 1
+
+        one_s = s.copy()
+        one_s.add_constraints(c_len == 1)
+        assert one_s.solver.eval(str_c, cast_to=bytes).index(b"\x00") == 1
+        str_test = one_s.memory.load(c_addr, 2, endness="Iend_BE")
+        assert len(one_s.solver.eval_upto(str_test, 300, cast_to=bytes)) == 255
+
+        for i in range(16):
+            test_s = s.copy()
+            test_s.add_constraints(c_len == i)
+            str_test = test_s.memory.load(c_addr, i + 1, endness="Iend_BE")
+            assert test_s.solver.eval(str_test, cast_to=bytes).index(b"\x00") == i
+            for j in range(i):
+                assert not test_s.solver.unique(test_s.memory.load(c_addr + j, 1))
+
+    def test_strcmp(self):
+        s = SimState(arch="AMD64", mode="symbolic")
+        str_a = s.solver.BVV(0x41414100, 32)
+        str_b = s.solver.BVS("mystring", 32)
+
+        a_addr = s.solver.BVV(0x10, 64)
+        b_addr = s.solver.BVV(0xB0, 64)
+
+        s.memory.store(a_addr, str_a, endness="Iend_BE")
+        s.memory.store(b_addr, str_b, endness="Iend_BE")
+
+        s_cmp = s.copy()
+        cmpres = strcmp(s_cmp, arguments=[a_addr, b_addr])
+        s_match = s_cmp.copy()
+        s_nomatch = s_cmp.copy()
+        s_match.add_constraints(cmpres == 0)
+        s_nomatch.add_constraints(cmpres != 0)
+
+        assert s_match.solver.unique(str_b)
+        assert not s_nomatch.solver.unique(str_b)
+        assert s_match.solver.eval(str_b, cast_to=bytes) == b"AAA\x00"
+
+        s_ncmp = s.copy()
+        ncmpres = strncmp(s_ncmp, arguments=[a_addr, b_addr, s.solver.BVV(2, s.arch.bits)])
+        s_match = s_ncmp.copy()
+        s_nomatch = s_ncmp.copy()
+        s_match.add_constraints(ncmpres == 0)
+        s_nomatch.add_constraints(ncmpres != 0)
+
+        assert not s_match.solver.unique(str_b)
+        assert s_match.solver.unique(s_match.memory.load(b_addr, 2))
+        assert len(s_match.solver.eval_upto(s_match.memory.load(b_addr, 3), 300)) == 256
+        assert not s_nomatch.solver.unique(str_b)
+
+        log.info("concrete a, symbolic b")
+        s = SimState(arch="AMD64", mode="symbolic")
+        str_a = s.solver.BVV(0x41424300, 32)
+        str_b = s.solver.BVS("mystring", 32)
+        a_addr = s.solver.BVV(0x10, 64)
+        b_addr = s.solver.BVV(0xB0, 64)
+        s.memory.store(a_addr, str_a, endness="Iend_BE")
+        s.memory.store(b_addr, str_b, endness="Iend_BE")
+
+        s_cmp = s.copy()
+        cmpres = strncmp(s_cmp, arguments=[a_addr, b_addr, s.solver.BVV(2, s_cmp.arch.bits)])
+        s_match = s_cmp.copy()
+        s_nomatch = s_cmp.copy()
+        s_match.add_constraints(cmpres == 0)
+        s_nomatch.add_constraints(cmpres != 0)
+
+        assert s_match.solver.solution(str_b, 0x41420000)
+        assert s_match.solver.solution(str_b, 0x41421234)
+        assert s_match.solver.solution(str_b, 0x41424300)
+        assert not s_nomatch.solver.solution(str_b, 0x41420000)
+        assert not s_nomatch.solver.solution(str_b, 0x41421234)
+        assert not s_nomatch.solver.solution(str_b, 0x41424300)
+
+        log.info("symbolic a, symbolic b")
+        s = SimState(arch="AMD64", mode="symbolic")
+        a_addr = s.solver.BVV(0x10, 64)
+        b_addr = s.solver.BVV(0xB0, 64)
+
+        s_cmp = s.copy()
+        cmpres = strcmp(s_cmp, arguments=[a_addr, b_addr])
+        s_match = s_cmp.copy()
+        s_nomatch = s_cmp.copy()
+        s_match.add_constraints(cmpres == 0)
+        s_nomatch.add_constraints(cmpres != 0)
+
+        m_res = strcmp(s_match, arguments=[a_addr, b_addr])
+        s_match.add_constraints(m_res != 0)
+        nm_res = strcmp(s_nomatch, arguments=[a_addr, b_addr])
+        s_nomatch.add_constraints(nm_res == 0)
+
+        assert not s_match.satisfiable()
+        assert not s_match.satisfiable()
+
+    def test_strncmp(self):
+        log.info("symbolic left, symbolic right, symbolic len")
+        s = SimState(arch="AMD64", mode="symbolic")
+        left = s.solver.BVS("left", 32)
+        left_addr = s.solver.BVV(0x1000, 64)
+        right = s.solver.BVS("right", 32)
+        right_addr = s.solver.BVV(0x2000, 64)
+        maxlen = s.solver.BVS("len", 64)
+
+        s.memory.store(left_addr, left)
+        s.memory.store(right_addr, right)
+
+        s.add_constraints(strlen(s, arguments=[left_addr]) == 3)
+        s.add_constraints(strlen(s, arguments=[right_addr]) == 0)
+
+        s.add_constraints(maxlen != 0)
+        c = strncmp(s, arguments=[left_addr, right_addr, maxlen])
+
+        s_match = s.copy()
+        s_match.add_constraints(c == 0)
+        assert not s_match.satisfiable()
+        # assert s_match.solver.min_int(maxlen) == 3
+
+        s_nomatch = s.copy()
+        s_nomatch.add_constraints(c != 0)
+        assert s_nomatch.satisfiable()
+        # assert s_nomatch.solver.max_int(maxlen) == 2
+
+        log.info("zero-length")
+        s = SimState(arch="AMD64", mode="symbolic")
+        left = s.solver.BVS("left", 32)
+        left_addr = s.solver.BVV(0x1000, 64)
+        right = s.solver.BVS("right", 32)
+        right_addr = s.solver.BVV(0x2000, 64)
+        maxlen = s.solver.BVS("len", 64)
+        left_len = strlen(s, arguments=[left_addr])
+        right_len = strlen(s, arguments=[right_addr])
+        c = strncmp(s, arguments=[left_addr, right_addr, maxlen])
+
+        s.add_constraints(right_len == 0)
+        s.add_constraints(left_len == 0)
+        # s.add_constraints(c == 0)
+        s.add_constraints(maxlen == 0)
+        assert s.satisfiable()
+
+    def test_strncmp_longer_limit(self):
+        log.info("concrete a, concrete b, concrete n")
+        s = SimState(arch="AMD64", mode="symbolic")
+        str_a = s.solver.BVV(b"ABC\0")
+        str_b = s.solver.BVV(b"AB\0")
+        addr_a = s.solver.BVV(0x10, 64)
+        addr_b = s.solver.BVV(0xB0, 64)
+        s.memory.store(addr_a, str_a, endness="Iend_BE")
+        s.memory.store(addr_b, str_b, endness="Iend_BE")
+
+        ss_res = strncmp(s, arguments=[addr_a, addr_b, s.solver.BVV(3, 64)])
+        assert s.solver.unique(ss_res)
+        assert s.solver.eval(ss_res) != 0
+
+    def test_strncmp_find_limits(self):
+        log.info("concrete a, concrete b, symbolic n")
+        s = SimState(arch="AMD64", mode="symbolic")
+        str_a = s.solver.BVV(b"ABCD\0")
+        str_b = s.solver.BVV(b"ABCE\0")
+        addr_a = s.solver.BVV(0x10, 64)
+        addr_b = s.solver.BVV(0xB0, 64)
+        s.memory.store(addr_a, str_a, endness="Iend_BE")
+        s.memory.store(addr_b, str_b, endness="Iend_BE")
+
+        n = s.solver.BVS("n", 64)
+        ss_res = strncmp(s, arguments=[addr_a, addr_b, n])
+        s.add_constraints(ss_res == 0)
+        assert set(s.solver.eval_upto(n, 10)) == {0, 1, 2, 3}
+
+    def test_strncmp_find_prefix(self):
+        log.info("concrete a, symbolic b, symbolic n")
+        s = SimState(arch="AMD64", mode="symbolic")
+        str_a = s.solver.BVV(b"ABCD\0")
+        str_b = s.solver.BVS("str_b", len(str_a))
+        addr_a = s.solver.BVV(0x10, 64)
+        addr_b = s.solver.BVV(0xB0, 64)
+        s.memory.store(addr_a, str_a, endness="Iend_BE")
+        s.memory.store(addr_b, str_b, endness="Iend_BE")
+
+        # All prefixes should be valid
+        n = strlen(s, arguments=[addr_b])
+        ss_res = strncmp(s, arguments=[addr_a, addr_b, n])
+        s.add_constraints(ss_res == 0)
+        assert set(s.solver.eval_upto(n, 10)) == {0, 1, 2, 3, 4}
+
+    def test_strncmp_find_prefix_unsat(self):
+        log.info("concrete a, concrete b, symbolic n")
+        s = SimState(arch="AMD64", mode="symbolic")
+        str_a = s.solver.BVV(b"\0\0\0\0\0")
+        str_b = s.solver.BVV(b"ABCE\0")
+        addr_a = s.solver.BVV(0x10, 64)
+        addr_b = s.solver.BVV(0xB0, 64)
+        s.memory.store(addr_a, str_a, endness="Iend_BE")
+        s.memory.store(addr_b, str_b, endness="Iend_BE")
+
+        # No prefix should be valid
+        n = strlen(s, arguments=[addr_b])
+        ss_res = strncmp(s, arguments=[addr_a, addr_b, n])
+        s.add_constraints(ss_res == 0)
+        assert not s.satisfiable()
+
+    def test_strncmp_find_input_for_limit(self):
+        log.info("concrete a, symbolic b, symbolic n")
+        s = SimState(arch="AMD64", mode="symbolic")
+        str_a = s.solver.BVV(b"ABCD\0")
+        str_b = s.solver.BVS("str_b", len(str_a))
+        addr_a = s.solver.BVV(0x10, 64)
+        addr_b = s.solver.BVV(0xB0, 64)
+        s.memory.store(addr_a, str_a, endness="Iend_BE")
+        s.memory.store(addr_b, str_b, endness="Iend_BE")
+
+        n = s.solver.BVS("n", 64)
+        ss_res = strncmp(s, arguments=[addr_a, addr_b, n])
+        s.add_constraints(ss_res == 0)
+
+        # Check constrained limit produces only expected input
+        for i in range(1, len(str_b) // 8):
+            s2 = s.copy()
+            s2.add_constraints(n == i)
+            hi, lo = 5 * 8 - 1, (5 - i) * 8
+            substr_a, substr_b = str_a[hi:lo], str_b[hi:lo]
+            assert s2.solver.unique(substr_b)
+            assert s2.solver.solution(substr_b, substr_a)
+
+    def test_strstr_conc_haystack_conc_needle(self):
+        log.info("concrete haystack and needle")
+        s = SimState(arch="AMD64", mode="symbolic")
+        str_haystack = s.solver.BVV(0x41424300, 32)
+        str_needle = s.solver.BVV(0x42430000, 32)
+        addr_haystack = s.solver.BVV(0x10, 64)
+        addr_needle = s.solver.BVV(0xB0, 64)
+        s.memory.store(addr_haystack, str_haystack, endness="Iend_BE")
+        s.memory.store(addr_needle, str_needle, endness="Iend_BE")
+
+        ss_res = strstr(s, arguments=[addr_haystack, addr_needle])
+        assert s.solver.unique(ss_res)
+        assert s.solver.eval(ss_res) == 0x11
+
+    def test_strstr_conc_haystack_sym_needle(self):
+        log.info("concrete haystack, symbolic needle")
+        s = SimState(arch="AMD64", mode="symbolic")
+        s.libc.max_symbolic_strstr = 20
+        haystack = b"ABCD"
+        str_haystack = s.solver.BVV(haystack + b"\0")
+        str_needle = s.solver.BVS("needle", 32)
+        addr_haystack = s.solver.BVV(0x10, 64)
+        addr_needle = s.solver.BVV(0xB0, 64)
+        s.memory.store(addr_haystack, str_haystack, endness="Iend_BE")
+        s.memory.store(addr_needle, str_needle, endness="Iend_BE")
+        s.memory.store(addr_needle + 4, s.solver.BVV(0, 8))
+
+        ss_res = strstr(s, arguments=[addr_haystack, addr_needle])
+        results = set(s.solver.eval_upto(ss_res, len(haystack) * 2))
+        expected = {(addr_haystack.cv + i) for i in range(len(haystack))} | {0}
+        assert results == expected
+
+        s_match = s.copy()
+        s_nomatch = s.copy()
+        s_match.add_constraints(ss_res != 0)
+        s_nomatch.add_constraints(ss_res == 0)
+
+        # Check all substrings
+        substrings = [haystack[l:h] for l, h in combinations(range(len(haystack) + 1), 2)]
+        needle_len = len(str_needle) // 8
+        for substring in substrings:
+            solution = s.solver.BVV(substring.ljust(needle_len, b"\0"))
+            assert s_match.solver.solution(str_needle, solution)
+        assert not s_match.solver.solution(str_needle, s.solver.BVV(b"AC\0\0"))
+        assert not s_match.solver.solution(str_needle, s.solver.BVV(b"AD\0\0"))
+        assert s_nomatch.solver.satisfiable()
+
+    def test_strstr_sym_haystack_conc_needle(self):
+        log.info("symbolic haystack, concrete needle")
+        s = SimState(arch="AMD64", mode="symbolic")
+        s.libc.max_symbolic_strstr = 20
+        str_haystack = s.solver.BVS("haystack", 5 * 8).concat(s.solver.BVV(0, 8))
+        str_needle = s.solver.BVV(b"ABC\0")
+        addr_haystack = s.solver.BVV(0x10, 64)
+        addr_needle = s.solver.BVV(0xB0, 64)
+        s.memory.store(addr_haystack, str_haystack, endness="Iend_BE")
+        s.memory.store(addr_needle, str_needle, endness="Iend_BE")
+
+        ss_res = strstr(s, arguments=[addr_haystack, addr_needle])
+
+        s_match = s.copy()
+        s_match.add_constraints(ss_res != 0)
+        s_nomatch = s.copy()
+        s_nomatch.add_constraints(ss_res == 0)
+
+        num_possible = min(s.libc.max_symbolic_strstr, 1 + (len(str_haystack) - len(str_needle)) // 8)
+        expected = set(range(addr_haystack.cv, addr_haystack.cv + num_possible))
+        results = set(s_match.solver.eval_exact(ss_res, num_possible))
+        assert results == expected
+        assert s_nomatch.solver.satisfiable()
+
+    def test_strstr_sym_haystack_sym_needle(self):
+        log.info("symbolic haystack, symbolic needle")
+        s = SimState(arch="AMD64", mode="symbolic")
+        s.libc.max_symbolic_strstr = 20
+        s.libc.buf_symbolic_bytes = 10
+        str_haystack = s.solver.BVS("haystack", s.libc.buf_symbolic_bytes * 8)
+        str_needle = s.solver.BVS("needle", s.libc.buf_symbolic_bytes * 8)
+        addr_haystack = s.solver.BVV(0x10, 64)
+        addr_needle = s.solver.BVV(0xB0, 64)
+        s.memory.store(addr_haystack, str_haystack, endness="Iend_BE")
+        s.memory.store(addr_needle, str_needle, endness="Iend_BE")
+
+        ss_res = strstr(s, arguments=[addr_haystack, addr_needle])
+
+        s_match = s.copy()
+        s_nomatch = s.copy()
+        s_match.add_constraints(ss_res != 0)
+        s_nomatch.add_constraints(ss_res == 0)
+
+        s_i = s_match.copy()
+        s_i.add_constraints(str_needle == s.solver.BVV(b"123".ljust(s.libc.buf_symbolic_bytes, b"\0")))
+
+        # Check needle is not found after end of haystack
+        s_ez = s_i.copy()
+        s_ez.add_constraints(str_haystack[len(str_haystack) - 1 : len(str_haystack) - 8] == 0)
+        assert not s_ez.satisfiable()
+
+        # Check target of specific offset
+        for i in range(5):
+            s_n = s_i.copy()
+            s_n.add_constraints(ss_res == addr_haystack + i)
+            h = s_n.solver.eval(str_haystack, cast_to=bytes)
+            assert h.find(b"123") == i
+
+        len_needle = strlen(s_match, arguments=[addr_needle])
+        match_cmp = strncmp(s_match, arguments=[ss_res, addr_needle, len_needle])
+        assert s_match.solver.eval_upto(match_cmp, 10) == [0]
+
+        r_mm = strstr(s_match, arguments=[addr_haystack, addr_needle])
+        s_match.add_constraints(r_mm == 0)
+        assert not s_match.satisfiable()
+
+        assert s_nomatch.satisfiable()
+        s_nss = s_nomatch.copy()
+        nomatch_ss = strstr(s_nss, arguments=[addr_haystack, addr_needle])
+        s_nss.add_constraints(nomatch_ss != 0)
+        assert not s_nss.satisfiable()
+
+    def test_strstr_inconsistency(self):
+        log.info("symbolic haystack, symbolic needle")
+        s = SimState(arch="AMD64", mode="symbolic")
+        s.libc.buf_symbolic_bytes = 2
+        addr_haystack = s.solver.BVV(0x10, 64)
+        addr_needle = s.solver.BVV(0xB0, 64)
+        # len_needle = strlen(s, inline=True, arguments=[addr_needle])
+
+        ss_res = strstr(s, arguments=[addr_haystack, addr_needle])
+
+        # slh_res = strlen(s, inline=True, arguments=[addr_haystack])
+        # sln_res = strlen(s, inline=True, arguments=[addr_needle])
+        # print "LENH:", s.solver.eval_upto(slh_res, 100)
+        # print "LENN:", s.solver.eval_upto(sln_res, 100)
+
+        assert not s.solver.unique(ss_res)
+        assert sorted(s.solver.eval_upto(ss_res, 100)) == [0] + list(range(0x10, 0x10 + s.libc.buf_symbolic_bytes - 1))
+
+        s.add_constraints(ss_res != 0)
+        ss2 = strstr(s, arguments=[addr_haystack, addr_needle])
+        s.add_constraints(ss2 == 0)
+        assert not s.satisfiable()
+
+    def test_memcpy(self):
+        log.info("concrete src, concrete dst, concrete len")
+        log.debug("... full copy")
+        s = SimState(arch="AMD64", mode="symbolic")
+        dst = s.solver.BVV(0x41414141, 32)
+        dst_addr = s.solver.BVV(0x1000, 64)
+        src = s.solver.BVV(0x42424242, 32)
+        src_addr = s.solver.BVV(0x2000, 64)
+
+        s.memory.store(dst_addr, dst)
+        s.memory.store(src_addr, src)
+        memcpy(s, arguments=[dst_addr, src_addr, s.solver.BVV(4, 64)])
+        new_dst = s.memory.load(dst_addr, 4, endness="Iend_BE")
+        assert s.solver.eval_upto(new_dst, 2, cast_to=bytes) == [b"BBBB"]
+
+        log.info("giant copy")
+        s = SimState(arch="AMD64", mode="symbolic", remove_options=angr.options.simplification)
+        s.memory._maximum_symbolic_size = 0x2000000
+        size = s.solver.BVV(0x1000000, 64)
+        data = s.solver.BVS("giant", 8 * 0x1_000_000)
+        dst_addr = s.solver.BVV(0x2000000, 64)
+        src_addr = s.solver.BVV(0x4000000, 64)
+        s.memory.store(src_addr, data)
+
+        memcpy(s, arguments=[dst_addr, src_addr, size])
+        assert s.memory.load(dst_addr, size) is s.memory.load(src_addr, size)
+
+        log.debug("... partial copy")
+        s = SimState(arch="AMD64", mode="symbolic")
+        s.memory.store(dst_addr, dst)
+        s.memory.store(src_addr, src)
+        memcpy(s, arguments=[dst_addr, src_addr, s.solver.BVV(2, 64)])
+        new_dst = s.memory.load(dst_addr, 4, endness="Iend_BE")
+        assert s.solver.eval_upto(new_dst, 2, cast_to=bytes) == [b"BBAA"]
+
+        log.info("symbolic src, concrete dst, concrete len")
+        s = SimState(arch="AMD64", mode="symbolic")
+        dst = s.solver.BVV(0x41414141, 32)
+        dst_addr = s.solver.BVV(0x1000, 64)
+        src = s.solver.BVS("src", 32)
+        src_addr = s.solver.BVV(0x2000, 64)
+
+        s.memory.store(dst_addr, dst)
+        s.memory.store(src_addr, src)
+
+        # make sure it copies it all
+        memcpy(s, arguments=[dst_addr, src_addr, s.solver.BVV(4, 64)])
+        assert s.satisfiable()
+        s.add_constraints(src != s.memory.load(dst_addr, 4))
+        assert not s.satisfiable()
+
+        log.info("symbolic src, concrete dst, symbolic len")
+        s = SimState(arch="AMD64", mode="symbolic")
+        dst = s.solver.BVV(0x41414141, 32)
+        dst_addr = s.solver.BVV(0x1000, 64)
+        src = s.solver.BVS("src", 32)
+        src_addr = s.solver.BVV(0x2000, 64)
+        cpylen = s.solver.BVS("len", 64)
+
+        s.memory.store(dst_addr, dst)
+        s.memory.store(src_addr, src)
+        s.add_constraints(cpylen < 10)
+        memcpy(s, arguments=[dst_addr, src_addr, cpylen])
+        result = s.memory.load(dst_addr, 4, endness="Iend_BE")
+
+        # make sure it copies it all
+        s1 = s.copy()
+        s1.add_constraints(cpylen == 1)
+        assert s1.solver.unique(s1.memory.load(dst_addr + 1, 3))
+        assert len(s1.solver.eval_upto(s1.memory.load(dst_addr, 1), 300)) == 256
 
-def broken_sprintf():
-    l.info("concrete src, concrete dst, concrete len")
-    s = SimState(mode="symbolic", arch="PPC32")
-    format_str = s.solver.BVV(0x25640000, 32)
-    format_addr = s.solver.BVV(0x2000, 32)
-    # dst = s.solver.BVV("destination", 128)
-    dst_addr = s.solver.BVV(0x1000, 32)
-    arg = s.solver.BVS("some_number", 32)
-
-    s.memory.store(format_addr, format_str)
-
-    sprintf(s, arguments=[dst_addr, format_addr, arg])
-
-    for i in range(9):
-        j = random.randint(10**i, 10 ** (i + 1))
         s2 = s.copy()
-        s2.add_constraints(arg == j)
-        # print s2.solver.eval_upto(s2.memory.load(dst_addr, i+2), 2, cast_to=bytes), repr(b"%d\x00" % j)
-        assert s2.solver.eval_upto(s2.memory.load(dst_addr, i + 2), 2, cast_to=bytes) == [b"%d\x00" % j]
-
-    s2 = s.copy()
-    s2.add_constraints(arg == 0)
-    # print s2.solver.eval_upto(s2.memory.load(dst_addr, 2), 2, cast_to=bytes), repr(b"%d\x00" % 0)
-    assert s2.solver.eval_upto(s2.memory.load(dst_addr, 2), 2, cast_to=bytes) == [b"%d\x00" % 0]
-
-
-def test_memset():
-    l.info("concrete src, concrete dst, concrete len")
-    s = SimState(arch="PPC32", mode="symbolic")
-    dst = s.solver.BVV(0, 128)
-    dst_addr = s.solver.BVV(0x1000, 32)
-    char = s.solver.BVV(0x00000041, 32)
-    char2 = s.solver.BVV(0x50505050, 32)
-    length = s.solver.BVS("some_length", 32)
-
-    s.memory.store(dst_addr, dst)
-    memset(s, arguments=[dst_addr, char, s.solver.BVV(3, 32)])
-    assert s.solver.eval(s.memory.load(dst_addr, 4)) == 0x41414100
-
-    l.debug("Symbolic length")
-    s = SimState(arch="PPC32", mode="symbolic")
-    s.memory.store(dst_addr, dst)
-    length = s.solver.BVS("some_length", 32)
-    s.add_constraints(length < 10)
-    memset(s, arguments=[dst_addr, char2, length])
-
-    l.debug("Trying 2")
-    s_two = s.copy()
-    s_two.add_constraints(length == 2)
-    assert s_two.solver.eval(s_two.memory.load(dst_addr, 4)) == 0x50500000
-
-    l.debug("Trying 0")
-    s_zero = s.copy()
-    s_zero.add_constraints(length == 0)
-    assert s_zero.solver.eval(s_zero.memory.load(dst_addr, 4)) == 0x00000000
-
-    l.debug("Trying 5")
-    s_five = s.copy()
-    s_five.add_constraints(length == 5)
-    assert s_five.solver.eval(s_five.memory.load(dst_addr, 6)) == 0x505050505000
-
-
-def test_strchr():
-    l.info("concrete haystack and needle")
-    s = SimState(arch="AMD64", mode="symbolic")
-    str_haystack = s.solver.BVV(0x41424300, 32)
-    str_needle = s.solver.BVV(0x42, 64)
-    addr_haystack = s.solver.BVV(0x10, 64)
-    s.memory.store(addr_haystack, str_haystack, endness="Iend_BE")
-
-    ss_res = strchr(s, arguments=[addr_haystack, str_needle])
-    assert s.solver.unique(ss_res)
-    assert s.solver.eval(ss_res) == 0x11
-
-    l.info("concrete haystack, symbolic needle")
-    s = SimState(arch="AMD64", mode="symbolic")
-    str_haystack = s.solver.BVV(0x41424300, 32)
-    str_needle = s.solver.BVS("wtf", 64)
-    chr_needle = str_needle[7:0]
-    addr_haystack = s.solver.BVV(0x10, 64)
-    s.memory.store(addr_haystack, str_haystack, endness="Iend_BE")
-
-    ss_res = strchr(s, arguments=[addr_haystack, str_needle])
-    assert not s.solver.unique(ss_res)
-    assert len(s.solver.eval_upto(ss_res, 10)) == 5
-
-    s_match = s.copy()
-    s_nomatch = s.copy()
-    s_match.add_constraints(ss_res != 0)
-    s_nomatch.add_constraints(ss_res == 0)
-
-    assert s_match.satisfiable()
-    assert s_nomatch.satisfiable()
-    assert len(s_match.solver.eval_upto(chr_needle, 300)) == 4
-    assert len(s_nomatch.solver.eval_upto(chr_needle, 300)) == 252
-    assert sorted(s_match.solver.eval_upto(ss_res, 300)) == [0x10, 0x11, 0x12, 0x13]
-    assert sorted(s_match.solver.eval_upto(chr_needle, 300)) == [0x00, 0x41, 0x42, 0x43]
-
-    s_match.memory.store(ss_res, s_match.solver.BVV(0x44, 8))
-    assert sorted(s_match.solver.eval_upto(s_match.memory.load(0x10, 1), 300)) == [0x41, 0x44]
-    assert sorted(s_match.solver.eval_upto(s_match.memory.load(0x11, 1), 300)) == [0x42, 0x44]
-    assert sorted(s_match.solver.eval_upto(s_match.memory.load(0x12, 1), 300)) == [0x43, 0x44]
-    assert sorted(s_match.solver.eval_upto(s_match.memory.load(0x13, 1), 300)) == [0x00, 0x44]
-
-    # l.info("symbolic haystack, symbolic needle")
-    # s = SimState(arch="AMD64", mode="symbolic")
-    # s.libc.buf_symbolic_bytes = 5
-    # addr_haystack = s.solver.BVV(0x10, 64)
-    # addr_needle = s.solver.BVV(0xb0, 64)
-    # len_needle = strlen(s, inline=True, arguments=[addr_needle])
-
-    # ss_res = strstr(s, inline=True, arguments=[addr_haystack, addr_needle])
-    # ss_val = s.expr_value(ss_res)
-
-    # assert not ss_val.is_unique()
-    # assert len(ss_val.solver.eval_upto(100)) == s.libc.buf_symbolic_bytes
-
-    # s_match = s.copy()
-    # s_nomatch = s.copy()
-    # s_match.add_constraints(ss_res != 0)
-    # s_nomatch.add_constraints(ss_res == 0)
-
-    # match_cmp = strncmp(s_match, inline=True, arguments=[ss_res, addr_needle, len_needle])
-    # match_cmp_val = s_match.expr_value(match_cmp)
-    # assert match_cmp_val.solver.eval_upto(10) == [0]
-
-    # r_mm = strstr(s_match, inline=True, arguments=[addr_haystack, addr_needle])
-    # s_match.add_constraints(r_mm == 0)
-    # assert not s_match.satisfiable()
-
-    # assert s_nomatch.satisfiable()
-    # s_nss = s_nomatch.copy()
-    # nomatch_ss = strstr(s_nss, inline=True, arguments=[addr_haystack, addr_needle])
-    # s_nss.add_constraints(nomatch_ss != 0)
-    # assert not s_nss.satisfiable()
-
-
-def broken_strtok_r():
-    l.debug("CONCRETE MODE")
-    s = SimState(arch="AMD64", mode="symbolic")
-    s.memory.store(100, s.solver.BVV(0x4141414241414241424300, 88), endness="Iend_BE")
-    s.memory.store(200, s.solver.BVV(0x4200, 16), endness="Iend_BE")
-    str_ptr = s.solver.BVV(100, s.arch.bits)
-    delim_ptr = s.solver.BVV(200, s.arch.bits)
-    state_ptr = s.solver.BVV(300, s.arch.bits)
-
-    st1 = strtok_r(s, arguments=[str_ptr, delim_ptr, state_ptr])
-    assert s.solver.eval_upto(st1, 10) == [104]
-    assert s.solver.eval_upto(s.memory.load(st1 - 1, 1), 10) == [0]
-    assert s.solver.eval_upto(s.memory.load(200, 2), 10) == [0x4200]
-
-    st2 = strtok_r(s, arguments=[s.solver.BVV(0, s.arch.bits), delim_ptr, state_ptr])
-    assert s.solver.eval_upto(st2, 10) == [107]
-    assert s.solver.eval_upto(s.memory.load(st2 - 1, 1), 10) == [0]
-
-    st3 = strtok_r(s, arguments=[s.solver.BVV(0, s.arch.bits), delim_ptr, state_ptr])
-    assert s.solver.eval_upto(st3, 10) == [109]
-    assert s.solver.eval_upto(s.memory.load(st3 - 1, 1), 10) == [0]
-
-    st4 = strtok_r(s, arguments=[s.solver.BVV(0, s.arch.bits), delim_ptr, state_ptr])
-    assert s.solver.eval_upto(st4, 10) == [0]
-    assert s.solver.eval_upto(s.memory.load(300, s.arch.bytes, endness=s.arch.memory_endness), 10) == [109]
-
-    st5 = strtok_r(s, arguments=[s.solver.BVV(0, s.arch.bits), delim_ptr, state_ptr])
-    assert s.solver.eval_upto(st5, 10) == [0]
-    assert s.solver.eval_upto(s.memory.load(300, s.arch.bytes, endness=s.arch.memory_endness), 10) == [109]
-
-    s.memory.store(1000, s.solver.BVV(0x4141414241414241424300, 88), endness="Iend_BE")
-    s.memory.store(2000, s.solver.BVV(0x4200, 16), endness="Iend_BE")
-    str_ptr = s.solver.BVV(1000, s.arch.bits)
-    delim_ptr = s.solver.BVV(2000, s.arch.bits)
-    state_ptr = s.solver.BVV(3000, s.arch.bits)
-
-    st1 = strtok_r(s, arguments=[str_ptr, delim_ptr, state_ptr])
-    assert s.solver.eval_upto(st1, 10) == [1004]
-    assert s.solver.eval_upto(s.memory.load(st1 - 1, 1), 10) == [0]
-    assert s.solver.eval_upto(s.memory.load(2000, 2), 10) == [0x4200]
-
-    st2 = strtok_r(s, arguments=[s.solver.BVV(0, s.arch.bits), delim_ptr, state_ptr])
-    assert s.solver.eval_upto(st2, 10) == [1007]
-    assert s.solver.eval_upto(s.memory.load(st2 - 1, 1), 10) == [0]
-
-    st3 = strtok_r(s, arguments=[s.solver.BVV(0, s.arch.bits), delim_ptr, state_ptr])
-    assert s.solver.eval_upto(st3, 10) == [1009]
-    assert s.solver.eval_upto(s.memory.load(st3 - 1, 1), 10) == [0]
-
-    st4 = strtok_r(s, arguments=[s.solver.BVV(0, s.arch.bits), delim_ptr, state_ptr])
-    assert s.solver.eval_upto(st4, 10) == [0]
-    assert s.solver.eval_upto(s.memory.load(3000, s.arch.bytes, endness=s.arch.memory_endness), 10) == [1009]
-
-    st5 = strtok_r(s, arguments=[s.solver.BVV(0, s.arch.bits), delim_ptr, state_ptr])
-    assert s.solver.eval_upto(st5, 10) == [0]
-    assert s.solver.eval_upto(s.memory.load(3000, s.arch.bytes, endness=s.arch.memory_endness), 10) == [1009]
-
-    s = SimState(arch="AMD64", mode="symbolic")
-    str_ptr = s.solver.BVV(100, s.arch.bits)
-    delim_ptr = s.solver.BVV(200, s.arch.bits)
-    state_ptr = s.solver.BVV(300, s.arch.bits)
-
-    s.add_constraints(s.memory.load(delim_ptr, 1) != 0)
-
-    st1 = strtok_r(s, arguments=[str_ptr, delim_ptr, state_ptr])
-    s.add_constraints(st1 != 0)
-    assert s.solver.eval_upto(s.memory.load(st1 - 1, 1), 10) == [0]
-
-
-def test_getc():
-    s = make_state_with_stdin(b"1234")
-    stdin = s.posix.get_fd(0)
-    s.mem[0x1000 + 0x70].int = 0
-
-    assert s.solver.eval_one(stdin.tell()) == 0
-
-    # The argument of getc should be a FILE *
-    c = getc(s, [0x1000])
-    assert s.solver.eval_upto(c, 300) == [0x31]
-    assert s.solver.eval_upto(stdin.tell(), 300) == [1]
-
-    c = getc(s, [0x1000])
-    assert s.solver.eval_upto(c, 300) == [0x32]
-    assert s.solver.eval_upto(stdin.tell(), 300) == [2]
-
-    c = getc(s, [0x1000])
-    assert s.solver.eval_upto(c, 300) == [0x33]
-    assert s.solver.eval_upto(stdin.tell(), 300) == [3]
-
-    c = getc(s, [0x1000])
-    assert s.solver.eval_upto(c, 300) == [0x34]
-    assert s.solver.eval_upto(stdin.tell(), 300) == [4]
+        s2.add_constraints(cpylen == 2)
+        assert len(s2.solver.eval_upto(result[31:24], 300)) == 256
+        assert len(s2.solver.eval_upto(result[23:16], 300)) == 256
+        assert s2.solver.eval_upto(result[15:0], 300, cast_to=bytes) == [b"AA"]
+
+        log.info("concrete src, concrete dst, symbolic len")
+        dst = s2.solver.BVV(0x41414141, 32)
+        dst_addr = s2.solver.BVV(0x1000, 64)
+        src = s2.solver.BVV(0x42424242, 32)
+        src_addr = s2.solver.BVV(0x2000, 64)
+
+        s = SimState(arch="AMD64", mode="symbolic")
+        s.memory.store(dst_addr, dst)
+        s.memory.store(src_addr, src)
+        cpylen = s.solver.BVS("len", 64)
+
+        s.add_constraints(s.solver.ULE(cpylen, 4))
+        memcpy(s, arguments=[dst_addr, src_addr, cpylen])
+        new_dst = s.memory.load(dst_addr, 4, endness="Iend_BE")
+        assert sorted(s.solver.eval_upto(new_dst, 300, cast_to=bytes)) == [b"AAAA", b"BAAA", b"BBAA", b"BBBA", b"BBBB"]
+
+    def test_memcmp(self):
+        log.info("concrete src, concrete dst, concrete len")
+
+        log.debug("... full cmp")
+        s = SimState(arch="AMD64", mode="symbolic")
+        dst = s.solver.BVV(0x41414141, 32)
+        dst_addr = s.solver.BVV(0x1000, 64)
+        src = s.solver.BVV(0x42424242, 32)
+        src_addr = s.solver.BVV(0x2000, 64)
+        s.memory.store(dst_addr, dst)
+        s.memory.store(src_addr, src)
+        r = memcmp(s, arguments=[dst_addr, src_addr, s.solver.BVV(4, 64)])
+        assert s.satisfiable()
+
+        s_pos = s.copy()
+        s_pos.add_constraints(r.SGE(0))
+        assert not s_pos.satisfiable()
+
+        s_neg = s.copy()
+        s_neg.add_constraints(r.SLT(0))
+        assert s_neg.satisfiable()
+
+        log.debug("... zero cmp")
+        s = SimState(arch="AMD64", mode="symbolic")
+        s.memory.store(dst_addr, dst)
+        s.memory.store(src_addr, src)
+        r = memcmp(s, arguments=[dst_addr, src_addr, s.solver.BVV(0, 64)])
+        assert s.solver.eval_upto(r, 2) == [0]
+
+        log.info("symbolic src, concrete dst, concrete len")
+        s = SimState(arch="AMD64", mode="symbolic")
+        dst = s.solver.BVV(0x41414141, 32)
+        dst_addr = s.solver.BVV(0x1000, 64)
+        src = s.solver.BVS("src", 32)
+
+        src_addr = s.solver.BVV(0x2000, 64)
+
+        s.memory.store(dst_addr, dst)
+        s.memory.store(src_addr, src)
+
+        # make sure it copies it all
+        r = memcmp(s, arguments=[dst_addr, src_addr, s.solver.BVV(4, 64)])
+
+        s_match = s.copy()
+        s_match.add_constraints(r == 0)
+        m = s_match.memory.load(src_addr, 4)
+        assert s_match.solver.eval_upto(m, 2) == [0x41414141]
+
+        s_nomatch = s.copy()
+        s_nomatch.add_constraints(r != 0)
+        m = s_nomatch.memory.load(src_addr, 4)
+        assert not s_nomatch.solver.solution(m, 0x41414141)
+
+        log.info("symbolic src, concrete dst, symbolic len")
+        s = SimState(arch="AMD64", mode="symbolic")
+        dst = s.solver.BVV(0x41414141, 32)
+        dst_addr = s.solver.BVV(0x1000, 64)
+        src = s.solver.BVS("src", 32)
+        src_addr = s.solver.BVV(0x2000, 64)
+        cmplen = s.solver.BVS("len", 64)
+
+        s.memory.store(dst_addr, dst)
+        s.memory.store(src_addr, src)
+        r = memcmp(s, arguments=[dst_addr, src_addr, cmplen])
+
+        # look at effects of different lengths
+        s1 = s.copy()
+        s1.add_constraints(cmplen == 1)
+        s1.add_constraints(r == 0)
+        log.debug("... simplifying")
+        s1.solver._solver.simplify()
+        log.debug("... solving")
+        assert s1.solver.eval_upto(src[31:24], 2) == [0x41]
+        assert not s1.solver.unique(src[31:16])
+        log.debug("... solved")
 
+        s2 = s.copy()
+        s2.add_constraints(cmplen == 2)
+        s2.add_constraints(r == 0)
+        assert s2.solver.eval_upto(s2.memory.load(src_addr, 2), 2) == [0x4141]
+        assert not s2.solver.unique(s2.memory.load(src_addr, 3))
+
+        s2u = s.copy()
+        s2u.add_constraints(cmplen == 2)
+        s2u.add_constraints(r == 1)
+        assert not s2u.solver.solution(s2u.memory.load(src_addr, 2), 0x4141)
+
+    def test_strncpy(self):
+        log.info("concrete src, concrete dst, concrete len")
+        log.debug("... full copy")
+        s = SimState(arch="AMD64", mode="symbolic")
+        dst = s.solver.BVV(0x41414100, 32)
+        dst_addr = s.solver.BVV(0x1000, 64)
+        src = s.solver.BVV(0x42420000, 32)
+        src_addr = s.solver.BVV(0x2000, 64)
+
+        s.memory.store(dst_addr, dst)
+        s.memory.store(src_addr, src)
+        strncpy(s, arguments=[dst_addr, src_addr, s.solver.BVV(3, 64)])
+        new_dst = s.memory.load(dst_addr, 4, endness="Iend_BE")
+        assert s.solver.eval(new_dst, cast_to=bytes) == b"BB\x00\x00"
+
+        log.debug("... partial copy")
+        s = SimState(arch="AMD64", mode="symbolic")
+        s.memory.store(dst_addr, dst)
+        s.memory.store(src_addr, src)
+        strncpy(s, arguments=[dst_addr, src_addr, s.solver.BVV(2, 64)])
+        new_dst = s.memory.load(dst_addr, 4, endness="Iend_BE")
+        assert s.solver.eval_upto(new_dst, 2, cast_to=bytes) == [b"BBA\x00"]
+
+        log.info("symbolic src, concrete dst, concrete len")
+        s = SimState(arch="AMD64", mode="symbolic")
+        dst = s.solver.BVV(0x41414100, 32)
+        dst_addr = s.solver.BVV(0x1000, 64)
+        src = s.solver.BVS("src", 32)
+        src_addr = s.solver.BVV(0x2000, 64)
+
+        s.memory.store(dst_addr, dst)
+        s.memory.store(src_addr, src)
+
+        # make sure it copies it all
+        s.add_constraints(strlen(s, arguments=[src_addr]) == 2)
+
+        # sanity check
+        s_false = s.copy()
+        s_false.add_constraints(strlen(s_false, arguments=[src_addr]) == 3)
+        assert not s_false.satisfiable()
+
+        strncpy(s, arguments=[dst_addr, src_addr, 3])
+        assert s.satisfiable()
+        c = strcmp(s, arguments=[dst_addr, src_addr])
+
+        assert s.solver.eval_upto(c, 10) == [0]
+
+        log.info("symbolic src, concrete dst, symbolic len")
+        s = SimState(arch="AMD64", mode="symbolic")
+        dst = s.solver.BVV(0x41414100, 32)
+        dst_addr = s.solver.BVV(0x1000, 64)
+        src = s.solver.BVS("src", 32)
+        src_addr = s.solver.BVV(0x2000, 64)
+        maxlen = s.solver.BVS("len", 64)
+
+        s.memory.store(dst_addr, dst)
+        s.memory.store(src_addr, src)
+
+        # make sure it copies it all
+        s.add_constraints(strlen(s, arguments=[src_addr]) == 2)
+        strncpy(s, arguments=[dst_addr, src_addr, maxlen])
+        c = strcmp(s, arguments=[dst_addr, src_addr])
+
+        s_match = s.copy()
+        s_match.add_constraints(c == 0)
+        assert s_match.solver.min_int(maxlen) == 3
+
+        s_nomatch = s.copy()
+        s_nomatch.add_constraints(c != 0)
+        assert s_nomatch.solver.max_int(maxlen) == 2
+
+        log.info("concrete src, concrete dst, symbolic len")
+        log.debug("... full copy")
+        s = SimState(arch="AMD64", mode="symbolic")
+
+        dst = s.solver.BVV(0x41414100, 32)
+        dst_addr = s.solver.BVV(0x1000, 64)
+        src = s.solver.BVV(0x42420000, 32)
+        src_addr = s.solver.BVV(0x2000, 64)
+        maxlen = s.solver.BVS("len", 64)
+
+        s.memory.store(dst_addr, dst)
+        s.memory.store(src_addr, src)
+        strncpy(s, arguments=[dst_addr, src_addr, maxlen])
+        r = s.memory.load(dst_addr, 4, endness="Iend_BE")
+        # print repr(r.solver.eval_upto(r, 10, cast_to=bytes))
+        assert sorted(s.solver.eval_upto(r, 10, cast_to=bytes)) == [b"AAA\x00", b"BAA\x00", b"BB\x00\x00", b"BBA\x00"]
+
+    def test_strcpy(self):
+        log.info("concrete src, concrete dst")
+
+        log.debug("... full copy")
+        s = SimState(arch="AMD64", mode="symbolic")
+        dst = s.solver.BVV(0x41414100, 32)
+        dst_addr = s.solver.BVV(0x1000, 64)
+        src = s.solver.BVV(0x42420000, 32)
+        src_addr = s.solver.BVV(0x2000, 64)
+        s.memory.store(dst_addr, dst)
+        s.memory.store(src_addr, src)
+        strcpy(s, arguments=[dst_addr, src_addr])
+        new_dst = s.memory.load(dst_addr, 4, endness="Iend_BE")
+        assert s.solver.eval(new_dst, cast_to=bytes) == b"BB\x00\x00"
+
+        log.info("symbolic src, concrete dst")
+        dst = s.solver.BVV(0x41414100, 32)
+        dst_addr = s.solver.BVV(0x1000, 64)
+        src = s.solver.BVS("src", 32)
+        src_addr = s.solver.BVV(0x2000, 64)
+
+        s = SimState(arch="AMD64", mode="symbolic")
+        s.memory.store(dst_addr, dst)
+        s.memory.store(src_addr, src)
+
+        ln = strlen(s, arguments=[src_addr])
+
+        strcpy(s, arguments=[dst_addr, src_addr])
+
+        cm = strcmp(s, arguments=[dst_addr, src_addr])
+
+        s.add_constraints(cm == 0)
+
+        s.add_constraints(ln == 15)
+        # readsize = 16
+        # both_strs = s.solver.Concat(
+        #     *[
+        #         s.memory.load(dst_addr, readsize, endness="Iend_BE"),
+        #         s.memory.load(src_addr, readsize, endness="Iend_BE"),
+        #     ]
+        # )
+        # for i in s.solver.eval_upto(both_strs, 50, cast_to=bytes):
+        #     assert s.solver.eval_upto(10) == [0]
+        #     assert s.solver.solution(s.memory.load(dst_addr, 4, endness="Iend_BE"), 0x42434400)
+        #     assert s.solver.solution(s.memory.load(dst_addr, 4, endness="Iend_BE"), 0x42434445)
+        #     assert s.solver.solution(s.memory.load(dst_addr, 4, endness="Iend_BE"), 0x00414100)
+        #     assert not s.solver.solution(s.memory.load(dst_addr, 4, endness="Iend_BE"), 0x00010203)
+
+    @broken
+    def test_sprintf(self):
+        log.info("concrete src, concrete dst, concrete len")
+        s = SimState(mode="symbolic", arch="PPC32")
+        format_str = s.solver.BVV(0x25640000, 32)
+        format_addr = s.solver.BVV(0x2000, 32)
+        # dst = s.solver.BVV("destination", 128)
+        dst_addr = s.solver.BVV(0x1000, 32)
+        arg = s.solver.BVS("some_number", 32)
+
+        s.memory.store(format_addr, format_str)
+
+        sprintf(s, arguments=[dst_addr, format_addr, arg])
+
+        for i in range(9):
+            j = random.randint(10**i, 10 ** (i + 1))
+            s2 = s.copy()
+            s2.add_constraints(arg == j)
+            # print s2.solver.eval_upto(s2.memory.load(dst_addr, i+2), 2, cast_to=bytes), repr(b"%d\x00" % j)
+            assert s2.solver.eval_upto(s2.memory.load(dst_addr, i + 2), 2, cast_to=bytes) == [b"%d\x00" % j]
 
-def test_getchar():
-    s = make_state_with_stdin(b"1234")
-    stdin = s.posix.get_fd(0)
-
-    assert s.solver.eval_upto(stdin.tell(), 300) == [0]
-    c = getchar(s, arguments=[])
-    assert s.solver.eval_upto(c, 300) == [0x31]
-    assert s.solver.eval_upto(stdin.tell(), 300) == [1]
-
-    c = getchar(s, arguments=[])
-    assert s.solver.eval_upto(c, 300) == [0x32]
-    assert s.solver.eval_upto(stdin.tell(), 300) == [2]
-
-    c = getchar(s, arguments=[])
-    assert s.solver.eval_upto(c, 300) == [0x33]
-    assert s.solver.eval_upto(stdin.tell(), 300) == [3]
-
-    c = getchar(s, arguments=[])
-    assert s.solver.eval_upto(c, 300) == [0x34]
-    assert s.solver.eval_upto(stdin.tell(), 300) == [4]
-
-
-def test_scanf():
-    s = make_state_with_stdin(b"Hello\n")
-    s.memory.store(0x2000, b"%1s\0")
-    scanf(s, [0x2000, 0x1000])
-    assert s.solver.eval_upto(s.memory.load(0x1000, 2), 2, cast_to=bytes) == [b"H\x00"]
-
-
-def test_strcmp():
-    l.info("concrete a, concrete b")
-    s = SimState(arch="AMD64", mode="symbolic")
-    a_addr = s.solver.BVV(0x10, 64)
-    b_addr = s.solver.BVV(0xB0, 64)
-
-    s.memory.store(a_addr, b"heck\x00")
-    s.memory.store(b_addr, b"heck\x00")
+        s2 = s.copy()
+        s2.add_constraints(arg == 0)
+        # print s2.solver.eval_upto(s2.memory.load(dst_addr, 2), 2, cast_to=bytes), repr(b"%d\x00" % 0)
+        assert s2.solver.eval_upto(s2.memory.load(dst_addr, 2), 2, cast_to=bytes) == [b"%d\x00" % 0]
+
+    def test_memset(self):
+        log.info("concrete src, concrete dst, concrete len")
+        s = SimState(arch="PPC32", mode="symbolic")
+        dst = s.solver.BVV(0, 128)
+        dst_addr = s.solver.BVV(0x1000, 32)
+        char = s.solver.BVV(0x00000041, 32)
+        char2 = s.solver.BVV(0x50505050, 32)
+        length = s.solver.BVS("some_length", 32)
+
+        s.memory.store(dst_addr, dst)
+        memset(s, arguments=[dst_addr, char, s.solver.BVV(3, 32)])
+        assert s.solver.eval(s.memory.load(dst_addr, 4)) == 0x41414100
+
+        log.debug("Symbolic length")
+        s = SimState(arch="PPC32", mode="symbolic")
+        s.memory.store(dst_addr, dst)
+        length = s.solver.BVS("some_length", 32)
+        s.add_constraints(length < 10)
+        memset(s, arguments=[dst_addr, char2, length])
+
+        log.debug("Trying 2")
+        s_two = s.copy()
+        s_two.add_constraints(length == 2)
+        assert s_two.solver.eval(s_two.memory.load(dst_addr, 4)) == 0x50500000
+
+        log.debug("Trying 0")
+        s_zero = s.copy()
+        s_zero.add_constraints(length == 0)
+        assert s_zero.solver.eval(s_zero.memory.load(dst_addr, 4)) == 0x00000000
+
+        log.debug("Trying 5")
+        s_five = s.copy()
+        s_five.add_constraints(length == 5)
+        assert s_five.solver.eval(s_five.memory.load(dst_addr, 6)) == 0x505050505000
+
+    def test_strchr(self):
+        log.info("concrete haystack and needle")
+        s = SimState(arch="AMD64", mode="symbolic")
+        str_haystack = s.solver.BVV(0x41424300, 32)
+        str_needle = s.solver.BVV(0x42, 64)
+        addr_haystack = s.solver.BVV(0x10, 64)
+        s.memory.store(addr_haystack, str_haystack, endness="Iend_BE")
+
+        ss_res = strchr(s, arguments=[addr_haystack, str_needle])
+        assert s.solver.unique(ss_res)
+        assert s.solver.eval(ss_res) == 0x11
+
+        log.info("concrete haystack, symbolic needle")
+        s = SimState(arch="AMD64", mode="symbolic")
+        str_haystack = s.solver.BVV(0x41424300, 32)
+        str_needle = s.solver.BVS("wtf", 64)
+        chr_needle = str_needle[7:0]
+        addr_haystack = s.solver.BVV(0x10, 64)
+        s.memory.store(addr_haystack, str_haystack, endness="Iend_BE")
+
+        ss_res = strchr(s, arguments=[addr_haystack, str_needle])
+        assert not s.solver.unique(ss_res)
+        assert len(s.solver.eval_upto(ss_res, 10)) == 5
+
+        s_match = s.copy()
+        s_nomatch = s.copy()
+        s_match.add_constraints(ss_res != 0)
+        s_nomatch.add_constraints(ss_res == 0)
+
+        assert s_match.satisfiable()
+        assert s_nomatch.satisfiable()
+        assert len(s_match.solver.eval_upto(chr_needle, 300)) == 4
+        assert len(s_nomatch.solver.eval_upto(chr_needle, 300)) == 252
+        assert sorted(s_match.solver.eval_upto(ss_res, 300)) == [0x10, 0x11, 0x12, 0x13]
+        assert sorted(s_match.solver.eval_upto(chr_needle, 300)) == [0x00, 0x41, 0x42, 0x43]
+
+        s_match.memory.store(ss_res, s_match.solver.BVV(0x44, 8))
+        assert sorted(s_match.solver.eval_upto(s_match.memory.load(0x10, 1), 300)) == [0x41, 0x44]
+        assert sorted(s_match.solver.eval_upto(s_match.memory.load(0x11, 1), 300)) == [0x42, 0x44]
+        assert sorted(s_match.solver.eval_upto(s_match.memory.load(0x12, 1), 300)) == [0x43, 0x44]
+        assert sorted(s_match.solver.eval_upto(s_match.memory.load(0x13, 1), 300)) == [0x00, 0x44]
+
+        # l.info("symbolic haystack, symbolic needle")
+        # s = SimState(arch="AMD64", mode="symbolic")
+        # s.libc.buf_symbolic_bytes = 5
+        # addr_haystack = s.solver.BVV(0x10, 64)
+        # addr_needle = s.solver.BVV(0xb0, 64)
+        # len_needle = strlen(s, inline=True, arguments=[addr_needle])
+
+        # ss_res = strstr(s, inline=True, arguments=[addr_haystack, addr_needle])
+        # ss_val = s.expr_value(ss_res)
+
+        # assert not ss_val.is_unique()
+        # assert len(ss_val.solver.eval_upto(100)) == s.libc.buf_symbolic_bytes
+
+        # s_match = s.copy()
+        # s_nomatch = s.copy()
+        # s_match.add_constraints(ss_res != 0)
+        # s_nomatch.add_constraints(ss_res == 0)
+
+        # match_cmp = strncmp(s_match, inline=True, arguments=[ss_res, addr_needle, len_needle])
+        # match_cmp_val = s_match.expr_value(match_cmp)
+        # assert match_cmp_val.solver.eval_upto(10) == [0]
+
+        # r_mm = strstr(s_match, inline=True, arguments=[addr_haystack, addr_needle])
+        # s_match.add_constraints(r_mm == 0)
+        # assert not s_match.satisfiable()
+
+        # assert s_nomatch.satisfiable()
+        # s_nss = s_nomatch.copy()
+        # nomatch_ss = strstr(s_nss, inline=True, arguments=[addr_haystack, addr_needle])
+        # s_nss.add_constraints(nomatch_ss != 0)
+        # assert not s_nss.satisfiable()
+
+    @broken
+    def test_strtok_r(self):
+        log.debug("CONCRETE MODE")
+        s = SimState(arch="AMD64", mode="symbolic")
+        s.memory.store(100, s.solver.BVV(0x4141414241414241424300, 88), endness="Iend_BE")
+        s.memory.store(200, s.solver.BVV(0x4200, 16), endness="Iend_BE")
+        str_ptr = s.solver.BVV(100, s.arch.bits)
+        delim_ptr = s.solver.BVV(200, s.arch.bits)
+        state_ptr = s.solver.BVV(300, s.arch.bits)
+
+        st1 = strtok_r(s, arguments=[str_ptr, delim_ptr, state_ptr])
+        assert s.solver.eval_upto(st1, 10) == [104]
+        assert s.solver.eval_upto(s.memory.load(st1 - 1, 1), 10) == [0]
+        assert s.solver.eval_upto(s.memory.load(200, 2), 10) == [0x4200]
+
+        st2 = strtok_r(s, arguments=[s.solver.BVV(0, s.arch.bits), delim_ptr, state_ptr])
+        assert s.solver.eval_upto(st2, 10) == [107]
+        assert s.solver.eval_upto(s.memory.load(st2 - 1, 1), 10) == [0]
+
+        st3 = strtok_r(s, arguments=[s.solver.BVV(0, s.arch.bits), delim_ptr, state_ptr])
+        assert s.solver.eval_upto(st3, 10) == [109]
+        assert s.solver.eval_upto(s.memory.load(st3 - 1, 1), 10) == [0]
+
+        st4 = strtok_r(s, arguments=[s.solver.BVV(0, s.arch.bits), delim_ptr, state_ptr])
+        assert s.solver.eval_upto(st4, 10) == [0]
+        assert s.solver.eval_upto(s.memory.load(300, s.arch.bytes, endness=s.arch.memory_endness), 10) == [109]
+
+        st5 = strtok_r(s, arguments=[s.solver.BVV(0, s.arch.bits), delim_ptr, state_ptr])
+        assert s.solver.eval_upto(st5, 10) == [0]
+        assert s.solver.eval_upto(s.memory.load(300, s.arch.bytes, endness=s.arch.memory_endness), 10) == [109]
+
+        s.memory.store(1000, s.solver.BVV(0x4141414241414241424300, 88), endness="Iend_BE")
+        s.memory.store(2000, s.solver.BVV(0x4200, 16), endness="Iend_BE")
+        str_ptr = s.solver.BVV(1000, s.arch.bits)
+        delim_ptr = s.solver.BVV(2000, s.arch.bits)
+        state_ptr = s.solver.BVV(3000, s.arch.bits)
+
+        st1 = strtok_r(s, arguments=[str_ptr, delim_ptr, state_ptr])
+        assert s.solver.eval_upto(st1, 10) == [1004]
+        assert s.solver.eval_upto(s.memory.load(st1 - 1, 1), 10) == [0]
+        assert s.solver.eval_upto(s.memory.load(2000, 2), 10) == [0x4200]
+
+        st2 = strtok_r(s, arguments=[s.solver.BVV(0, s.arch.bits), delim_ptr, state_ptr])
+        assert s.solver.eval_upto(st2, 10) == [1007]
+        assert s.solver.eval_upto(s.memory.load(st2 - 1, 1), 10) == [0]
+
+        st3 = strtok_r(s, arguments=[s.solver.BVV(0, s.arch.bits), delim_ptr, state_ptr])
+        assert s.solver.eval_upto(st3, 10) == [1009]
+        assert s.solver.eval_upto(s.memory.load(st3 - 1, 1), 10) == [0]
+
+        st4 = strtok_r(s, arguments=[s.solver.BVV(0, s.arch.bits), delim_ptr, state_ptr])
+        assert s.solver.eval_upto(st4, 10) == [0]
+        assert s.solver.eval_upto(s.memory.load(3000, s.arch.bytes, endness=s.arch.memory_endness), 10) == [1009]
+
+        st5 = strtok_r(s, arguments=[s.solver.BVV(0, s.arch.bits), delim_ptr, state_ptr])
+        assert s.solver.eval_upto(st5, 10) == [0]
+        assert s.solver.eval_upto(s.memory.load(3000, s.arch.bytes, endness=s.arch.memory_endness), 10) == [1009]
+
+        s = SimState(arch="AMD64", mode="symbolic")
+        str_ptr = s.solver.BVV(100, s.arch.bits)
+        delim_ptr = s.solver.BVV(200, s.arch.bits)
+        state_ptr = s.solver.BVV(300, s.arch.bits)
+
+        s.add_constraints(s.memory.load(delim_ptr, 1) != 0)
+
+        st1 = strtok_r(s, arguments=[str_ptr, delim_ptr, state_ptr])
+        s.add_constraints(st1 != 0)
+        assert s.solver.eval_upto(s.memory.load(st1 - 1, 1), 10) == [0]
+
+    def test_getc(self):
+        s = make_state_with_stdin(b"1234")
+        stdin = s.posix.get_fd(0)
+        s.mem[0x1000 + 0x70].int = 0
+
+        assert s.solver.eval_one(stdin.tell()) == 0
+
+        # The argument of getc should be a FILE *
+        c = getc(s, [0x1000])
+        assert s.solver.eval_upto(c, 300) == [0x31]
+        assert s.solver.eval_upto(stdin.tell(), 300) == [1]
+
+        c = getc(s, [0x1000])
+        assert s.solver.eval_upto(c, 300) == [0x32]
+        assert s.solver.eval_upto(stdin.tell(), 300) == [2]
+
+        c = getc(s, [0x1000])
+        assert s.solver.eval_upto(c, 300) == [0x33]
+        assert s.solver.eval_upto(stdin.tell(), 300) == [3]
+
+        c = getc(s, [0x1000])
+        assert s.solver.eval_upto(c, 300) == [0x34]
+        assert s.solver.eval_upto(stdin.tell(), 300) == [4]
+
+    def test_getchar(self):
+        s = make_state_with_stdin(b"1234")
+        stdin = s.posix.get_fd(0)
+
+        assert s.solver.eval_upto(stdin.tell(), 300) == [0]
+        c = getchar(s, arguments=[])
+        assert s.solver.eval_upto(c, 300) == [0x31]
+        assert s.solver.eval_upto(stdin.tell(), 300) == [1]
+
+        c = getchar(s, arguments=[])
+        assert s.solver.eval_upto(c, 300) == [0x32]
+        assert s.solver.eval_upto(stdin.tell(), 300) == [2]
+
+        c = getchar(s, arguments=[])
+        assert s.solver.eval_upto(c, 300) == [0x33]
+        assert s.solver.eval_upto(stdin.tell(), 300) == [3]
 
-    r = strcmp(s, arguments=[a_addr, b_addr])
-    assert s.solver.eval_upto(r, 2) == [0]
+        c = getchar(s, arguments=[])
+        assert s.solver.eval_upto(c, 300) == [0x34]
+        assert s.solver.eval_upto(stdin.tell(), 300) == [4]
 
-    l.info("concrete a, empty b")
-    s = SimState(arch="AMD64", mode="symbolic")
-    a_addr = s.solver.BVV(0x10, 64)
-    b_addr = s.solver.BVV(0xB0, 64)
+    def test_scanf(self):
+        s = make_state_with_stdin(b"Hello\n")
+        s.memory.store(0x2000, b"%1s\0")
+        scanf(s, [0x2000, 0x1000])
+        assert s.solver.eval_upto(s.memory.load(0x1000, 2), 2, cast_to=bytes) == [b"H\x00"]
 
-    s.memory.store(a_addr, b"heck\x00")
-    s.memory.store(b_addr, b"\x00")
+    def test_strcmp_concrete(self):
+        log.info("concrete a, concrete b")
+        s = SimState(arch="AMD64", mode="symbolic")
+        a_addr = s.solver.BVV(0x10, 64)
+        b_addr = s.solver.BVV(0xB0, 64)
 
-    r = strcmp(s, arguments=[a_addr, b_addr])
-    assert s.solver.eval_upto(r, 2) == [1]
+        s.memory.store(a_addr, b"heck\x00")
+        s.memory.store(b_addr, b"heck\x00")
 
-    l.info("empty a, concrete b")
-    s = SimState(arch="AMD64", mode="symbolic")
-    a_addr = s.solver.BVV(0x10, 64)
-    b_addr = s.solver.BVV(0xB0, 64)
+        r = strcmp(s, arguments=[a_addr, b_addr])
+        assert s.solver.eval_upto(r, 2) == [0]
 
-    s.memory.store(a_addr, b"\x00")
-    s.memory.store(b_addr, b"heck\x00")
+        log.info("concrete a, empty b")
+        s = SimState(arch="AMD64", mode="symbolic")
+        a_addr = s.solver.BVV(0x10, 64)
+        b_addr = s.solver.BVV(0xB0, 64)
 
-    r = strcmp(s, arguments=[a_addr, b_addr])
-    assert s.solver.eval_upto(r, 2) == [0xFFFFFFFF]
+        s.memory.store(a_addr, b"heck\x00")
+        s.memory.store(b_addr, b"\x00")
 
-    l.info("empty a, empty b")
-    s = SimState(arch="AMD64", mode="symbolic")
-    a_addr = s.solver.BVV(0x10, 64)
-    b_addr = s.solver.BVV(0xB0, 64)
-
-    s.memory.store(a_addr, b"\x00")
-    s.memory.store(b_addr, b"\x00")
+        r = strcmp(s, arguments=[a_addr, b_addr])
+        assert s.solver.eval_upto(r, 2) == [1]
 
-    r = strcmp(s, arguments=[a_addr, b_addr])
-    assert s.solver.eval_upto(r, 2) == [0]
+        log.info("empty a, concrete b")
+        s = SimState(arch="AMD64", mode="symbolic")
+        a_addr = s.solver.BVV(0x10, 64)
+        b_addr = s.solver.BVV(0xB0, 64)
 
+        s.memory.store(a_addr, b"\x00")
+        s.memory.store(b_addr, b"heck\x00")
 
-def test_wcscmp():
-    # concrete cases for the wide char version sufficiently overlap with strcmp and friends
-    l.info("concrete a, symbolic b")
-    s = SimState(arch="AMD64", mode="symbolic")
-    heck = "heck\x00".encode("utf-16")[2:]  # remove encoding prefix
-    a_addr = s.solver.BVV(0x10, 64)
-    b_addr = s.solver.BVV(0xB0, 64)
-    b_bvs = s.solver.BVS("b", len(heck) * 8)
+        r = strcmp(s, arguments=[a_addr, b_addr])
+        assert s.solver.eval_upto(r, 2) == [0xFFFFFFFF]
 
-    s.memory.store(a_addr, heck)
-    s.memory.store(b_addr, b_bvs)
+        log.info("empty a, empty b")
+        s = SimState(arch="AMD64", mode="symbolic")
+        a_addr = s.solver.BVV(0x10, 64)
+        b_addr = s.solver.BVV(0xB0, 64)
 
-    r = wcscmp(s, arguments=[a_addr, b_addr])
+        s.memory.store(a_addr, b"\x00")
+        s.memory.store(b_addr, b"\x00")
 
-    solutions = s.solver.eval_upto(b_bvs, 2, cast_to=bytes, extra_constraints=(r == 0,))
-    assert solutions == [heck]
+        r = strcmp(s, arguments=[a_addr, b_addr])
+        assert s.solver.eval_upto(r, 2) == [0]
 
+    def test_wcscmp(self):
+        # concrete cases for the wide char version sufficiently overlap with strcmp and friends
+        log.info("concrete a, symbolic b")
+        s = SimState(arch="AMD64", mode="symbolic")
+        heck = "heck\x00".encode("utf-16")[2:]  # remove encoding prefix
+        a_addr = s.solver.BVV(0x10, 64)
+        b_addr = s.solver.BVV(0xB0, 64)
+        b_bvs = s.solver.BVS("b", len(heck) * 8)
 
-def test_string_without_null():
-    s = SimState(arch="AMD64", mode="symbolic")
-    str_ = b"abcd"
-    str_addr = s.solver.BVV(0x10, 64)
-    s.memory.store(str_addr, str_)
-    assert s.solver.eval(s.mem[str_addr].string.resolved, cast_to=bytes) == b"abcd"
+        s.memory.store(a_addr, heck)
+        s.memory.store(b_addr, b_bvs)
 
+        r = wcscmp(s, arguments=[a_addr, b_addr])
 
-def run_all():
-    def print_test_name(name):
-        print("#" * (len(name) + 8))
-        print("###", name, "###")
-        print("#" * (len(name) + 8))
+        solutions = s.solver.eval_upto(b_bvs, 2, cast_to=bytes, extra_constraints=(r == 0,))
+        assert solutions == [heck]
 
-    functions = globals()
-    all_functions = dict(filter((lambda kv: kv[0].startswith("test_")), functions.items()))
-    for f in sorted(all_functions.keys()):
-        if hasattr(all_functions[f], "__call__"):
-            print_test_name(f)
-            all_functions[f]()
+    def test_string_without_null(self):
+        s = SimState(arch="AMD64", mode="symbolic")
+        str_ = b"abcd"
+        str_addr = s.solver.BVV(0x10, 64)
+        s.memory.store(str_addr, str_)
+        assert s.solver.eval(s.mem[str_addr].string.resolved, cast_to=bytes) == b"abcd"
 
 
 if __name__ == "__main__":
-    if len(sys.argv) > 1:
-        globals()["test_" + sys.argv[1]]()
-    else:
-        run_all()
+    unittest.main()