Add a native example program & test case for #1604 (#1606)

* Organize examples in Makefile

* .gitignore binaries created in examples/linux

* Add a new Linux example using fclose

* Add a test case for the fclose example binary

See also #1602 and #1604.

* Add a comment for the change from #1604

* Delete an obsolete comment

* Add type hints to native integration tests

* Do not squelch command output from test

* Use `--worksapce` for `test_logger_verbosity`

This should prevent extra mcore_* directories from hanging around after testing.

* Reformat test_integration_native.py via `black`
This commit is contained in:
Brad Larsen
2020-02-04 09:22:53 -08:00
committed by GitHub
parent 934a4f0e33
commit 2527fa5c40
6 changed files with 102 additions and 21 deletions
+14
View File
@@ -0,0 +1,14 @@
arguments
basic
crackme
crackme.c
fclose
helloworld
ibranch
indexhell
nostdlib
sendmail
simple_copy
simpleassert
sindex
strncmp
+14 -1
View File
@@ -3,7 +3,20 @@ CFLAGS=-O3 -static
NOSTDLIBFLAGS=-fno-builtin -static -nostdlib -fomit-frame-pointer -fno-stack-protector
PYTHON=python3
EXAMPLES=basic sindex strncmp arguments ibranch sendmail crackme indexhell helloworld simple_copy simpleassert
EXAMPLES= \
arguments \
basic \
crackme \
fclose \
helloworld \
ibranch \
indexhell \
sendmail \
simpleassert \
simple_copy \
sindex \
strncmp \
OTHER_EXAMPLES=nostdlib
all: $(EXAMPLES) $(OTHER_EXAMPLES)
+31
View File
@@ -0,0 +1,31 @@
// This example closes file descriptors 0, 1, and 2 (which correspond to stdin,
// stdout, and stderr in most environments).
//
// This serves as a reduced testcase for what most of the programs in GNU
// coreutils do. See #1602 and #1604 on GitHub.
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv) {
if (argc >= 2 && strcmp(argv[1], "--close") == 0) {
fprintf(stdout, "Closing file handles!\n");
int rc = 0;
if (fclose(stdin) != 0) {
rc += 1;
}
if (fclose(stdout) != 0) {
rc += 2;
}
if (fclose(stderr) != 0) {
rc += 4;
}
return rc;
} else {
fprintf(stdout, "Not doing anything.\n");
return 0;
}
}
+2
View File
@@ -665,12 +665,14 @@ class Linux(Platform):
else:
self.files.append(file_or_buffer)
# If file descriptors for stdin/stdout/stderr aren't closed, propagate them
if self.files[0]:
self.files[0].peer = self.output
if self.files[1]:
self.files[1].peer = self.output
if self.files[2]:
self.files[2].peer = self.output
self._getdents_c = state["_getdents_c"]
self._closed_files = state["closed_files"]
self.input.peer = self.files[0]
Binary file not shown.
+41 -20
View File
@@ -10,26 +10,26 @@ import tempfile
from manticore.binary import Elf, CGCElf
from manticore.native.mappings import mmap, munmap
DIRPATH = os.path.dirname(__file__)
from typing import List, Set
# TLDR: when we launch `python -m manticore` and one uses PyCharm remote interpreter
# the `python` might not refer to proper interpreter. The `/proc/self/exe` is a workaround
# so one doesn't have to set up virtualenv in a remote interpreter.
PYTHON_BIN = sys.executable
DIRPATH: str = os.path.dirname(__file__)
PYTHON_BIN: str = sys.executable
class NativeIntegrationTest(unittest.TestCase):
_multiprocess_can_split_ = True
def setUp(self):
def setUp(self) -> None:
# Create a temporary directory
self.test_dir = tempfile.mkdtemp()
def tearDown(self):
def tearDown(self) -> None:
# Remove the directory after the test
shutil.rmtree(self.test_dir)
def _load_visited_set(self, visited):
def _load_visited_set(self, visited: str) -> Set[int]:
self.assertTrue(os.path.exists(visited))
with open(visited, "r") as f:
@@ -37,7 +37,7 @@ class NativeIntegrationTest(unittest.TestCase):
return set(int(x[2:], 16) for x in vitems)
def _run_with_timeout(self, procargs, logfile, timeout=1200):
def _run_with_timeout(self, procargs: List[str], logfile: str, timeout: int = 1200) -> None:
with open(os.path.join(os.pardir, logfile), "w") as output:
po = subprocess.Popen(procargs, stdout=output)
@@ -52,7 +52,7 @@ class NativeIntegrationTest(unittest.TestCase):
self.assertTrue(secs_used < timeout)
sys.stderr.write("\n")
def test_timeout(self):
def test_timeout(self) -> None:
filename = os.path.abspath(os.path.join(DIRPATH, "binaries", "arguments_linux_amd64"))
self.assertTrue(filename.startswith(os.getcwd()))
filename = filename[len(os.getcwd()) + 1 :]
@@ -79,12 +79,15 @@ class NativeIntegrationTest(unittest.TestCase):
self.assertTrue(time.time() - t < 20)
def test_logger_verbosity(self):
def test_logger_verbosity(self) -> None:
"""
Tests that default verbosity produces the expected volume of output
"""
filename = os.path.join(DIRPATH, "binaries", "basic_linux_amd64")
output = subprocess.check_output([PYTHON_BIN, "-m", "manticore", "--no-color", filename])
workspace = os.path.join(self.test_dir, "workspace")
output = subprocess.check_output(
[PYTHON_BIN, "-m", "manticore", "--no-color", "--workspace", workspace, filename]
)
output_lines = output.splitlines()
@@ -96,8 +99,8 @@ class NativeIntegrationTest(unittest.TestCase):
self.assertIn(b"Generated testcase No. 1 -", output_lines[2])
def _test_arguments_assertions_aux(
self, binname, testcases_number, visited, add_assertion=False
):
self, binname: str, testcases_number: int, visited: List[int], add_assertion: bool = False
) -> None:
filename = os.path.abspath(os.path.join(DIRPATH, "binaries", binname))
self.assertTrue(filename.startswith(os.getcwd()))
@@ -155,7 +158,7 @@ class NativeIntegrationTest(unittest.TestCase):
self.assertEqual(len(set(visited)), len(visited)) # just a sanity check
def test_arguments_assertions_amd64(self):
def test_arguments_assertions_amd64(self) -> None:
self._test_arguments_assertions_aux(
"arguments_linux_amd64",
testcases_number=1,
@@ -194,7 +197,7 @@ class NativeIntegrationTest(unittest.TestCase):
add_assertion=True,
)
def test_arguments_assertions_armv7(self):
def test_arguments_assertions_armv7(self) -> None:
self._test_arguments_assertions_aux(
"arguments_linux_armv7",
testcases_number=19,
@@ -215,7 +218,7 @@ class NativeIntegrationTest(unittest.TestCase):
],
)
def test_decree(self):
def test_decree(self) -> None:
filename = os.path.abspath(os.path.join(DIRPATH, "binaries", "cadet_decree_x86"))
self.assertTrue(filename.startswith(os.getcwd()))
filename = filename[len(os.getcwd()) + 1 :]
@@ -242,7 +245,7 @@ class NativeIntegrationTest(unittest.TestCase):
actual = self._load_visited_set(os.path.join(DIRPATH, workspace, "visited.txt"))
self.assertTrue(len(actual) > 100)
def test_basic_arm(self):
def test_basic_arm(self) -> None:
filename = os.path.abspath(os.path.join(DIRPATH, "binaries", "basic_linux_armv7"))
workspace = os.path.join(self.test_dir, "workspace")
cmd = [PYTHON_BIN, "-m", "manticore", "--no-color", "--workspace", workspace, filename]
@@ -260,7 +263,25 @@ class NativeIntegrationTest(unittest.TestCase):
with open(os.path.join(workspace, "test_00000001.stdout")) as f:
self.assertIn("Message", f.read())
def test_brk_regression(self):
def test_fclose_linux_amd64(self) -> None:
"""
Tests that the fclose example for amd64 linux doesn't crash; see #1602 and #1604.
"""
filename = os.path.abspath(os.path.join(DIRPATH, "binaries", "fclose_linux_amd64"))
workspace = os.path.join(self.test_dir, "workspace")
cmd = [
PYTHON_BIN,
"-m",
"manticore",
"--no-color",
"--workspace",
workspace,
filename,
"+++++++",
]
subprocess.check_call(cmd)
def test_brk_regression(self) -> None:
"""
Tests for brk behavior. Source of brk_static_amd64:
@@ -299,7 +320,7 @@ class NativeIntegrationTest(unittest.TestCase):
# with open(os.path.join(workspace, "test_00000000.messages")) as f:
# self.assertIn("finished with exit status: 0", f.read())
def test_unaligned_mappings(self):
def test_unaligned_mappings(self) -> None:
# This test ensures that mapping file contents at non page-aligned offsets is possible.
filename = os.path.join(os.path.dirname(__file__), "binaries", "basic_linux_amd64")
with open(filename, "rb") as f: