Initial Commit

This commit is contained in:
ayrton 2023-11-17 16:57:53 -07:00
commit 2182395310
28 changed files with 1522 additions and 0 deletions

242
.gitignore vendored Normal file
View file

@ -0,0 +1,242 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# AWS User-specific
.idea/**/aws.xml
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# SonarLint plugin
.idea/sonarlint/
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
ast_parser/test/.tmp

29
README.md Normal file
View file

@ -0,0 +1,29 @@
# Gazprea Fuzzer
This is a hecking fuzzer. It does the thing.
## Requirements
- Python 3.11
- NumPy
## Usage
```
usage: python -m gazprea_fuzzer.py [-h] [-b SIZE] [--seed SEED] <config_file>
Procedurally generate a test case for Gazprea
positional arguments:
config path to your configuration file
name name of the test case to generate (name.in, name.ins, name.out)
optional arguments:
-h, --help show the help message and exit
-b, --batch SIZE generate SIZE cases (fuzzer/input/nameX.in, /instream/..., /outputs/...)
--seed SEED rng seed
```
## Configuration
See the [default config file](config.yaml) for all possible options and their default values

0
__init__.py Normal file
View file

View file

View file

@ -0,0 +1,338 @@
import json
import random
from fuzzingbook.Grammars import is_valid_grammar, convert_ebnf_grammar, Grammar
from isla.solver import ISLaSolver
# from gazprea_ast_grammar import GAZPREA_TOP_LEVEL
# import gazprea_ast_grammar
from ast_parser.ast_parser import AstParser
import xml.etree.ElementTree as ET
GAZ_VOID_TYPE = 'void'
VOID_TYPE = 'void'
GAZ_BLOCK_TAG = 'block'
GAZ_RHS_TAG = 'lhs'
GAZ_RHS_TAG = 'rhs'
GAZ_RETURN_KEY = "return_type"
VAR_NAMES = ['alsdjf', 'asldfjlks', 'qowiuut', 'qoiur', 'qwioue', 'qoyiyut', 'llkjfg', 'kdjkdjf', 'asdjkfeey',
'jdhjfjheee']
ROUTINE_NAMES = ['bees', 'beans', 'hell']
GAZ_INT_OPS = ['*', '+', '-', '/', '%']
GAZ_TYPES = ['int']
GAZ_FLOAT_KEY = 'float'
GAZ_INT_KEY = 'int'
GAZ_FUNCTION_TAG = 'function'
GAZ_PROCEDURE_TAG = 'procedure'
GAZ_OPERATOR_TAG = "operator"
GAZ_LIT_TAG = "literal"
GAZ_VAR_TAG = "variable"
GAZ_OP_KEY = "op"
GAZ_NAME_KEY = "name"
GAZ_QUALIFIER_KEY = "mut"
GAZ_VAL_KEY = "value"
GAZ_TY_KEY = "type"
ANY_TYPE = "any"
def find_variables(AST):
pass
def set_variables(variable_names, variables):
pass
def set_routines(routine_names, routines):
pass
def type_check(AST, routines, variables):
pass
class AstGenerator:
def __init__(self, grammar: Grammar, params: json):
self.void_probability = 20
self.int_low = -2 ** 30
self.int_high = 2 ** 32 - 1
self.valid_var_names = VAR_NAMES
self.max_number_of_vars = 10
self.valid_routine_names = ROUTINE_NAMES
self.max_number_of_routines = 3
self.qualifier_probability = False
self.var_qualifier_probability = None
self.const_qualifier_probability = None
for key, value in params.items():
setattr(self, key, value)
if self.var_qualifier_probability is not None and self.const_qualifier_probability is not None:
self.qualifier_probability = True
self.ast_list = []
self.functions = []
assert (is_valid_grammar(grammar))
self.grammar = grammar
def fix_missing_locations(self, AST: str) -> str:
variable_names = self.get_variable_list()
routine_names = self.get_routine_list()
routines = find_routines(AST)
variables = find_variables(AST)
set_variables(variable_names, variables) # insert types and values
set_routines(routine_names, routines) # insert types
type_check(AST, routines, variables)
def test_samples(self, grammar: Grammar, iterations: int = 10, start_symbol=None, log: bool = True):
g = convert_ebnf_grammar(grammar)
solver = ISLaSolver(g, start_symbol=start_symbol, max_number_free_instantiations=iterations)
for i in range(iterations):
tree_str = str(solver.solve())
print(tree_str)
# tree = eval(tree_str)
# print(tree)
# tree = self.fix_missing_locations(tree)
# ast = AstParser(tree, from_xml=True)
# if log:
# ast.unparse()
# code = ast.input
# print(f'{code:40} # {tree_str}')
def get_variable_list(self):
pass
def populate_operator(self, operator: ET.Element, op, type):
operator.set(GAZ_OP_KEY, op)
operator.set(GAZ_TY_KEY, type)
for node in operator:
node = self.populate(node, type)
return operator
def populate_stream(self, stream: ET.Element, type):
stream.set(GAZ_TY_KEY, type)
for node in stream:
node = self.populate(node, ANY_TYPE)
return stream
def populate_literal(self, literal: ET.Element, type, value):
literal.set(GAZ_TY_KEY, type)
literal.set(GAZ_VAL_KEY, value)
return literal
def populate_variable(self, variable: ET.Element, qualifier, type, name):
variable.set(GAZ_QUALIFIER_KEY, qualifier)
variable.set(GAZ_TY_KEY, type)
variable.set(GAZ_NAME_KEY, name)
return variable
def populate_routine(self, routine: ET.Element, type, name):
routine.set(GAZ_RETURN_KEY, type)
if routine.get("name") != "main":
routine.set(GAZ_NAME_KEY, name)
if routine.tag == GAZ_PROCEDURE_TAG and type != VOID_TYPE:
routine.find("block").append(self.generate_return(type))
for block in routine:
for node in block:
if node.tag != "return":
node =self.populate(node, ANY_TYPE)
else:
node.set("type", type)
return routine
def populate_block(self, element):
for node in element:
return self.populate(node, ANY_TYPE)
def populate_xhs(self, side: ET.Element, type):
for node in side:
return self.populate(node, type)
def populate_ast(self, ast: ET.Element):
populated = self.generate_block()
for node in ast:
populated.append(self.populate(node, ANY_TYPE))
return populated
# def populate_assignment(self, name, type):
def populate(self, element: ET.Element, type: str):
if type == ANY_TYPE:
type = GAZ_TYPES[random.randint(0, len(GAZ_TYPES) - 1)]
if element.tag == GAZ_VAR_TAG:
return self.populate_variable(element, self.get_qualifier(), type, self.get_name(GAZ_VAR_TAG))
elif element.tag == GAZ_LIT_TAG:
return self.populate_literal(element, type, self.get_value(type))
elif element.tag == GAZ_OPERATOR_TAG:
return self.populate_operator(element, self.get_op(type), type)
elif element.tag == GAZ_FUNCTION_TAG:
return self.populate_routine(element, type, self.get_name(element.tag))
elif element.tag == GAZ_PROCEDURE_TAG:
type = self.void(type)
return self.populate_routine(element, type, self.get_name(element.tag))
elif element.tag in [GAZ_RHS_TAG, GAZ_RHS_TAG]:
return self.populate_xhs(element, type)
elif element.tag == GAZ_BLOCK_TAG:
return self.populate_block(element)
def get_qualifier(self):
var_weight: int = 80
const_weight: int = 20
if self.qualifier_probability:
var_weight = self.var_qualifier_probability
const_weight = self.const_qualifier_probability
a = random.randint(0, var_weight + const_weight)
if a in range(0, var_weight):
return 'var'
elif a in range(var_weight, var_weight + const_weight):
return 'const'
else:
raise ValueError("Internal Error, please report the stack trace to me")
def get_value(self, type):
if type == GAZ_INT_KEY:
return random.randint(self.int_low, self.int_high)
else:
raise TypeError("Unimplemented generator for type: " + type)
def get_name(self, name_type):
if name_type == GAZ_VAR_TAG:
return VAR_NAMES[random.randint(0, self.max_number_of_vars - 1)]
elif name_type in [GAZ_PROCEDURE_TAG, GAZ_FUNCTION_TAG]:
r_name = ROUTINE_NAMES[random.randint(0, len(ROUTINE_NAMES) - 1)]
self.functions.append(r_name)
return r_name
def get_op(self, type):
if type == GAZ_INT_KEY:
# TODO make this a parameter for peiple to change
return GAZ_INT_OPS[random.randint(0, len(GAZ_INT_OPS) - 1)]
else:
raise TypeError("Unimplemented type: " + type)
def _generate(self, element: str or None) -> ET.Element:
initial_grammar = convert_ebnf_grammar(self.grammar)
solver = ISLaSolver(initial_grammar, start_symbol=element)
ast_str = str(solver.solve())
print(ast_str)
elem = ET.fromstring(ast_str)
return elem
def generate_ast(self) -> ET.Element:
return self._generate(None)
def generate_return(self, type) -> ET.Element:
elem = self._generate('<return>')
elem.set(GAZ_TY_KEY, type)
return elem
def generate_literal(self) -> ET.Element:
return self._generate('<literal>')
def generate_variable(self) -> ET.Element:
return self._generate('<variable>')
def generate_operator(self) -> ET.Element:
return self._generate('<operator>')
def generate_block(self) -> ET.Element:
return self._generate('<block>')
def generate_routine(self) -> ET.Element:
return self._generate('<routine>')
def generate_main_routine(self) -> ET.Element:
return self._generate('<main_routine>')
def generate_declaration(self) -> ET.Element:
return self._generate('<declaration>')
def generate_stream(self) -> ET.Element:
return self._generate('<stream>')
def void(self, type):
b = random.randint(0, 100)
if b < self.void_probability:
return GAZ_VOID_TYPE
else:
return type
class AstElement:
def __init__(self, xml: ET.Element):
pass
class RoutineCall(AstElement):
def __init__(self, xml: ET.Element, dependents=None, type=None):
"""
@brief initialise a routine call object
:param xml:
:param dependents:
"""
super().__init__(xml)
if dependents is None:
dependents = []
else:
self.dependents = dependents
self.xml = xml
self.type = type
class Operator(AstElement):
def __init__(self, xml: ET.Element, params: json):
super().__init__(xml)
for key, value in params.items():
setattr(self, key, value)
def find_routines(AST: str):
"""
@brief find all of the routine and call elements in the ast
@param AST: the AST to analyse
@return the list of routine elements
"""
xml = ET.fromstring(AST)
result = list[RoutineCall]
for node in xml:
if node.tag in [GAZ_PROCEDURE_TAG, GAZ_FUNCTION_TAG]:
routine = RoutineCall(node)
result.append(routine)
if __name__ == '__main__':
pass
# ast_gen = AstGenerator(GAZPREA_TOP_LEVEL, json.loads("{}"))
# out: ET.Element = ast_gen.generate_return("int")
# print(out)
# gen = AstGenerator("{}")
# assert is_valid_grammar(gazprea_ast_grammar.GAZPREA_TOP_LEVEL)
#
# gen.test_samples(gazprea_ast_grammar.GAZPREA_TOP_LEVEL, iterations=100)
#
# initial_grammar = convert_ebnf_grammar(gazprea_ast_grammar.GAZPREA_TOP_LEVEL)
# solver = ISLaSolver(initial_grammar)
# constants_tree_str = str(solver.solve())
# print(constants_tree_str)

View file

@ -0,0 +1,62 @@
from fuzzingbook.Grammars import Grammar, is_valid_grammar, convert_ebnf_grammar
from isla.solver import ISLaSolver
GAZPREA_TOP_LEVEL: Grammar = {
# Top level elements
'<start>': ['<topBlock>'],
'<topBlock>': ['<XML_OPEN_TAG>block<XML_CLOSE_TAG><routine_list><main_routine><routine_list><XML_OPEN_SLASH>block<XML_CLOSE_TAG>'],
# TODO constants
# Routines
'<routine>': ['<function>', '<procedure>'], # TODO forward_declaration
'<function>': [
'<XML_OPEN_TAG>function name="_NAME_" return_type="_TYPE_" args="_ARGS_"<XML_CLOSE_TAG><return_block><XML_OPEN_SLASH>function<XML_CLOSE_TAG>'],
'<procedure>': [
'<XML_OPEN_TAG>procedure name="_NAME_" return_type="_TYPE_" args="_ARGS_"<XML_CLOSE_TAG><block><XML_OPEN_SLASH>procedure<XML_CLOSE_TAG>'],
'<main_routine>': [
'<XML_OPEN_TAG>procedure name="main" return_type="int" args="()"<XML_CLOSE_TAG><return_block><XML_OPEN_SLASH>procedure<XML_CLOSE_TAG>'],
'<routine_list>': ['<routine><routine_list><routine>', '<routine>'],
# Blocks
'<block>': ['<XML_OPEN_TAG>block<XML_CLOSE_TAG><statement_list><XML_OPEN_SLASH>block<XML_CLOSE_TAG>'],
'<return_block>': ['<XML_OPEN_TAG>block<XML_CLOSE_TAG><statement_list><return><XML_OPEN_SLASH>block<XML_CLOSE_TAG>'],
'<statement>': [
'<declaration>',
'<stream>',
# '<call>',
# '<return>', # TODO if/else, loop
],
'<statement_list>': ['<statement><statement_list><statement>', '<statement>'],
# Things that belong on their own lines
'<declaration>': ['<XML_OPEN_TAG>declaration<XML_CLOSE_TAG><variable><rhs><XML_OPEN_SLASH>declaration<XML_CLOSE_TAG>'],
'<stream>': ['<out_stream>'], #, '<in_stream>'],
'<return>': ['<XML_OPEN_TAG>return<XML_CLOSE_TAG><has_value><XML_OPEN_SLASH>return<XML_CLOSE_TAG>'],
'<out_stream>': ['<XML_OPEN_TAG>stream type="std_output"<XML_CLOSE_TAG><has_value><XML_OPEN_SLASH>stream<XML_CLOSE_TAG>'],
# '<in_stream>': ['<XML_OPEN_TAG>stream type="std_input"<XML_CLOSE_TAG><has_value><XML_OPEN_SLASH>stream<XML_CLOSE_TAG>'],
# Things that are part of lines
'<has_value>': ['<variable>', '<literal>', '<operator>'],
'<lhs>': ['<XML_OPEN_TAG>lhs<XML_CLOSE_TAG><has_value><XML_OPEN_SLASH>lhs<XML_CLOSE_TAG>'],
'<rhs>': ['<XML_OPEN_TAG>rhs<XML_CLOSE_TAG><has_value><XML_OPEN_SLASH>rhs<XML_CLOSE_TAG>'],
# Things that have values
'<operator>': ['<XML_OPEN_TAG>operator<XML_CLOSE_TAG><lhs><rhs><XML_OPEN_SLASH>operator<XML_CLOSE_TAG>'],
'<variable>': ['<XML_OPEN_TAG>variable mut="_MODIFIER_" type="_TYPE_" name="_NAME_"<XML_SLASH_TAG>'],
'<literal>': ['<XML_OPEN_TAG>literal type="_TYPE_" value="_VALUE_"<XML_SLASH_TAG>'],
# Helper rules
'<XML_OPEN_TAG>': ['<'],
'<XML_CLOSE_TAG>': ['>'],
'<XML_SLASH_TAG>': ['/>'],
'<XML_OPEN_SLASH>': ['</'],
}
if __name__ == "__main__":
assert is_valid_grammar(GAZPREA_TOP_LEVEL)
initial_grammar = convert_ebnf_grammar(GAZPREA_TOP_LEVEL)
solver = ISLaSolver(initial_grammar)
constants_tree_str = str(solver.solve())
print(constants_tree_str)

View file

View file

@ -0,0 +1,193 @@
import unittest
import xml.etree.ElementTree as ET
from ast_generator.ast_generator import *
from ast_generator.gazprea_ast_grammar import *
class TestGeneration(unittest.TestCase):
def setUp(self):
self.ast_gen = AstGenerator(GAZPREA_TOP_LEVEL, json.loads("{}"))
def test_generate_literal(self):
out: ET.Element = self.ast_gen.generate_literal()
self.assertIsNotNone(out.get("type"))
def test_generate_variable(self):
out: ET.Element = self.ast_gen.generate_variable()
self.assertIsNotNone(out.get("type"))
self.assertIsNotNone(out.get("name"))
def test_generate_declaration(self):
out: ET.Element = self.ast_gen.generate_declaration()
self.assertIsNotNone(out.find("variable"))
self.assertIsNotNone(out.find("rhs"))
def test_generate_operation(self):
out: ET.Element = self.ast_gen.generate_operator()
self.assertIsNotNone(out.find("lhs"))
self.assertIsNotNone(out.find("rhs"))
def test_generate_stream(self):
out: ET.Element = self.ast_gen.generate_stream()
self.assertIsNotNone(out.get("type"))
def test_generate_routine(self):
out: ET.Element = self.ast_gen.generate_routine()
self.assertIsNotNone(out.find("block"))
def test_generate_block(self):
out: ET.Element = self.ast_gen.generate_block()
self.assertIsNotNone(out)
def test_generate_main(self):
out: ET.Element = self.ast_gen.generate_main_routine()
self.assertIsNotNone(out)
self.assertEqual("main", out.get("name"))
self.assertEqual("int", out.get("return_type"))
def test_generate_ast(self):
out: ET.Element = self.ast_gen.generate_ast()
self.assertIsNotNone(out)
has_main = False
for child in out:
if child.get("name") == "main":
has_main = True
break
self.assertTrue(has_main)
class TestRoutines(unittest.TestCase):
def setUp(self):
self.ast_gen = AstGenerator(GAZPREA_TOP_LEVEL, json.loads("{}"))
def test_populate_function(self):
with open("xml/routine_1.xml", 'r') as i:
input_elemnet = ET.fromstring(i.read())
out: ET.Element = self.ast_gen.populate_routine(input_elemnet, 'int', 'blahaj')
self.assertEqual('int', out.get("return_type"))
self.assertEqual('int', out.find("block").find("return").get("type"))
def test_generate_return(self):
out: ET.Element = self.ast_gen.generate_return("int")
self.assertEqual('int', out.get("type"))
def test_populate_typed_procedure(self):
with open("xml/routine_2.xml", 'r') as i:
input_elemnet = ET.fromstring(i.read())
out: ET.Element = self.ast_gen.populate_routine(input_elemnet, 'int', 'blahaj')
self.assertEqual('int', out.get("return_type"))
self.assertEqual('int', out.find("block").find("return").get("type"))
def test_populate_void_procedure(self):
with open("xml/routine_3.xml", 'r') as i:
input_elemnet = ET.fromstring(i.read())
out: ET.Element = self.ast_gen.populate_routine(input_elemnet, 'void', 'blahaj')
self.assertEqual('void', out.get("return_type"))
self.assertIsNone(out.find("block").find("return"))
def test_populate_void_procedure_with_return(self):
with open("xml/routine_2.xml", 'r') as i:
input_elemnet = ET.fromstring(i.read())
out: ET.Element = self.ast_gen.populate_routine(input_elemnet, 'void', 'blahaj')
self.assertEqual('void', out.get("return_type"))
self.assertIsNotNone(out.find("block").find("return").get("type"))
self.assertEqual('void', out.find("block").find("return").get("type"))
def test_populate_nonvoid_procedure_without_return(self):
with open("xml/routine_3.xml", 'r') as i:
input_elemnet = ET.fromstring(i.read())
out: ET.Element = self.ast_gen.populate_routine(input_elemnet, 'int', 'blahaj')
self.assertEqual('int', out.get("return_type"))
self.assertIsNotNone(out.find("block").find("return"))
self.assertEqual('int', out.find("block").find("return").get("type"))
# def test_populate_assignment(self):
# pass
#
# def test_populate_top_block(self):
# pass
# def test_populate_call(self):
# pass
class TestValues(unittest.TestCase):
def setUp(self):
self.ast_gen = AstGenerator(GAZPREA_TOP_LEVEL ,json.loads("{}"))
def test_populate_int(self):
with open("xml/literal_1.xml", 'r') as i:
input = i.read()
input_element = ET.fromstring(input)
out: ET.Element = self.ast_gen.populate_literal(input_element, "int", 42)
self.assertEqual("int", out.get("type"))
self.assertEqual("42", str(out.get("value")))
def test_populate_var(self):
with open("xml/variable_1.xml", 'r') as i:
input = i.read()
input_element = ET.fromstring(input)
out: ET.Element = self.ast_gen.populate_variable(input_element, "var", "float", "bean")
self.assertEqual("var", out.get("mut"))
self.assertEqual("float", out.get("type"))
self.assertEqual("bean", out.get("name"))
def test_populate_operator(self):
with open("xml/operator_1.xml", 'r') as i:
input = i.read()
input_element = ET.fromstring(input)
out: ET.Element = self.ast_gen.populate_operator(input_element, '*', 'int')
inner_var = out.find("type")
self.assertEqual("int", out.get("type"))
self.assertEqual("*", out.get("op"))
self.assertEqual("int", out.find("lhs").find("variable").get("type"))
self.assertEqual("int", out.find("rhs").find("literal").get("type"))
def test_populate_stream(self):
with open("xml/stream_1.xml", 'r') as i:
input_element = ET.fromstring(i.read())
out: ET.Element = self.ast_gen.populate_stream(input_element, "std_output")
self.assertEqual("std_output", out.get("type"))
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1,54 @@
import unittest
class TestGenConcatenation(unittest.TestCase):
def test_operator_nesting(self):
pass
def test_recursive_population_of_vars(self):
pass
def test_recursive_population_of_literals(self):
pass
def test_recursive_block_population(self):
pass
def test_routine_population(self):
pass
def test_full_population(self):
pass
class TestAutoGeneration(unittest.TestCase):
def routines_call_routines(self):
pass
def test_routines_use_globals(self):
pass
def test_variable_reuse(self):
pass
def test_max_nesting_depth(self):
pass
def test_max_conditionals_loops(self):
pass # test the parameter limiting the maximum number of loops and conditionals per routine
def test_recursive_calls(self):
pass
def test_max_range_length(self):
pass
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1 @@
<literal type="%_TYPE_%" value="%_VALUE_%" />

View file

@ -0,0 +1,8 @@
<operator op="*" type="int">
<lhs>
<variable mut="var" type="int" name="a"/>
</lhs>
<rhs>
<literal type="int" value="42" />
</rhs>
</operator>

View file

@ -0,0 +1,33 @@
<function name="%_NAME_%" ref_name="_vvtmcyclqx" return_type="%_TYPE_%" args="()" >
<block>
<declaration>
<variable mut="var" type="int" name="b" ref_name="_tsfjvbryrsxdtso"/>
<rhs>
<function name="art" ref_name="_hssvswwxpt" >
</function>
</rhs>
</declaration>
<declaration>
<variable mut="var" type="int" name="a" ref_name="_pseeixbzvvhuygw"/>
<rhs>
<literal type="int" value="1" />
</rhs>
</declaration>
<stream type="std_output">
<operator op="*" type="unset">
<lhs>
<variable mut="var" type="int" name="a" ref_name="_pseeixbzvvhuygw"/>
</lhs>
<rhs>
<literal type="int" value="42" />
</rhs>
</operator>
</stream>
<return>
<literal type="%_TYPE_%" value="%_VALUE_%" />
</return>
</block>
</function>

View file

@ -0,0 +1,33 @@
<procedure name="%_NAME_%" ref_name="_vvtmcyclqx" return_type="%_TYPE_%" args="()" >
<block>
<declaration>
<variable mut="var" type="int" name="b" ref_name="_tsfjvbryrsxdtso"/>
<rhs>
<function name="art" ref_name="_hssvswwxpt" >
</function>
</rhs>
</declaration>
<declaration>
<variable mut="var" type="int" name="a" ref_name="_pseeixbzvvhuygw"/>
<rhs>
<literal type="int" value="1" />
</rhs>
</declaration>
<stream type="std_output">
<operator op="*" type="unset">
<lhs>
<variable mut="var" type="int" name="a" ref_name="_pseeixbzvvhuygw"/>
</lhs>
<rhs>
<literal type="int" value="42" />
</rhs>
</operator>
</stream>
<return>
<literal type="%_TYPE_%" value="%_VALUE_%" />
</return>
</block>
</procedure>

View file

@ -0,0 +1,29 @@
<procedure name="%_NAME_%" ref_name="_vvtmcyclqx" return_type="%_TYPE_%" args="()" >
<block>
<declaration>
<variable mut="var" type="int" name="b" ref_name="_tsfjvbryrsxdtso"/>
<rhs>
<function name="art" ref_name="_hssvswwxpt" >
</function>
</rhs>
</declaration>
<declaration>
<variable mut="var" type="int" name="a" ref_name="_pseeixbzvvhuygw"/>
<rhs>
<literal type="int" value="1" />
</rhs>
</declaration>
<stream type="std_output">
<operator op="*" type="unset">
<lhs>
<variable mut="var" type="int" name="a" ref_name="_pseeixbzvvhuygw"/>
</lhs>
<rhs>
<literal type="int" value="42" />
</rhs>
</operator>
</stream>
</block>
</procedure>

View file

@ -0,0 +1,10 @@
<stream type="std_output">
<operator op="*" type="unset">
<lhs>
<variable mut="var" type="int" name="a" ref_name="_pseeixbzvvhuygw"/>
</lhs>
<rhs>
<literal type="int" value="42" />
</rhs>
</operator>
</stream>

View file

@ -0,0 +1 @@
<variable mut="%_MUT_%" type="%_TYPE_%" name="%_NAME_%"/>

0
ast_parser/__init__.py Normal file
View file

165
ast_parser/ast_parser.py Normal file
View file

@ -0,0 +1,165 @@
import os
import shutil
import xml.etree.ElementTree as ET
def to_gazprea_type(ty: str):
if ty == "int":
return "integer"
elif ty == "bool":
return "boolean"
elif ty == "string":
return "string"
elif ty == 'void':
return 'void'
else:
raise Exception("Unknown type: " + ty)
class AstParser:
def __init__(self, input: str or ET.Element, from_xml: bool = False):
if from_xml:
self.xml = input
self.input = None
else:
self.input = input
self.xml = None
self.indentation = 0
def parse(self):
if os.path.isdir("/home/stormblessed/Code/gazprea_fuzzer_v0.2/ast_parser/test/.tmp"):
os.system("rm -rf /home/stormblessed/Code/gazprea_fuzzer_v0.2/ast_parser/test/.tmp")
os.mkdir("/home/stormblessed/Code/gazprea_fuzzer_v0.2/ast_parser/test/.tmp")
else:
os.mkdir("/home/stormblessed/Code/gazprea_fuzzer_v0.2/ast_parser/test/.tmp")
with open("/home/stormblessed/Code/gazprea_fuzzer_v0.2/ast_parser/test/.tmp/input.in", "x") as f:
f.write(self.input)
os.system("/home/stormblessed/.local/bin/gazc "
"/home/stormblessed/Code/gazprea_fuzzer_v0.2/ast_parser/test/.tmp/input.in "
"/home/stormblessed/Code/gazprea_fuzzer_v0.2/ast_parser/test/.tmp/output.out "
"/home/stormblessed/Code/gazprea_fuzzer_v0.2/ast_parser/test/.tmp/xml.xml")
self.xml = ET.parse(".tmp/xml.xml")
def unparse(self):
"""
@brief unparses the xml into valid gazprea code
:return: a string of valid gazprea code
"""
self.input = ""
for node in self.xml:
self._unparse_node(node)
def _unparse_node(self, node):
if node.tag not in ["variable", "rhs", "lhs", "literal", "operator"]:
self.input += " " * self.indentation
if node.tag == "block":
self._block_unparse(node)
elif node.tag == "declaration":
self._declaration_unparse(node)
elif node.tag == "return":
self._return_unparse(node)
elif node.tag == "operator":
self._operator_unparse(node)
elif node.tag == "stream":
self._stream_unparse(node)
elif node.tag == "literal":
self._literal_unparse(node)
elif node.tag == "procedure" or node.tag == "function":
self._routine_unparse(node)
elif node.tag == "variable":
self._variable_unparse(node)
elif node.tag == "rhs" or node.tag == "lhs":
self._xhs_unparse(node)
elif node.tag == "literal":
self._literal_unparse(node)
else:
raise Exception("Unknown tag: " + node.tag)
def _block_unparse(self, node):
self.input += "{\n"
self.indentation += 4
for child in node:
self._unparse_node(child)
self.indentation -= 4
self.input += "}\n\n"
def _declaration_unparse(self, node):
variable = node.find("variable")
rhs = node.find("rhs")
self._variable_unparse(variable, True)
self.input += "="
self._unparse_node(rhs)
self.input += ";\n"
def _variable_unparse(self, node, is_declaration = False):
if is_declaration:
mut = node.get("mut")
type = to_gazprea_type(node.get("type"))
name = node.get("name")
self.input += "{} {} {} ".format(mut, type, name)
else:
self.input += " {} ".format(node.get("name"))
def _stream_unparse(self, node):
for child in node:
self._unparse_node(child)
self.input += "-> {};\n".format(node.get("type"))
def _literal_unparse(self, node):
self.input += " {} ".format(node.get("value"))
def _xhs_unparse(self, node):
for child in node:
self._unparse_node(child)
def _operator_unparse(self, node):
self._xhs_unparse(node.find("lhs"))
self.input += "{}".format(node.get("op"))
self._xhs_unparse(node.find("rhs"))
def _return_unparse(self, node):
self.input += "return"
for child in node:
self._unparse_node(child)
self.input += ";\n"
def _routine_unparse(self, node):
return_type = ""
if node.get("return_type") != "":
return_type = "returns " + to_gazprea_type(node.get("return_type"))
self.input += "{} {}{} {} ".format(
node.tag,
node.get("name"),
node.get("args"),
return_type,
)
for child in node:
self._unparse_node(child)
# self.input += "}\n\n" #blocks are already there
if __name__ == '__main__':
input = """
function art() returns integer {
return 3;
}
procedure main() returns integer {
integer b = art();
integer a = 1;
a * 42 -> std_output;
return 0;
}
"""
parser = AstParser(input)
parser.parse()

13
ast_parser/ast_solver.py Normal file
View file

@ -0,0 +1,13 @@
import json
class AstSolver:
def __init__(self, ast: str, params: str):
json.loads(params)
self.ast = ast
for key, value in json.loads(params).items():
setattr(self, key, value)
def fix_missing_locations(self, AST):
pass

14
ast_parser/params.json Normal file
View file

@ -0,0 +1,14 @@
{
"num_vars": 10,
"num_globals": 10,
"num_functions": 2,
"num_procedures": 2,
"arith_ops": 60,
"compare_ops": 30,
"outstream_ops": 50,
"instream_ops": 10,
"largest_number": 900000,
"smallest_number": -900000
}

View file

6
ast_parser/test/input.in Normal file
View file

@ -0,0 +1,6 @@
procedure main() returns integer {
var integer a = 1 ;
a * 42 -> std_output;
return 0 ;
}

28
ast_parser/test/test.xml Normal file
View file

@ -0,0 +1,28 @@
<block>
<procedure name="main" ref_name="_cteplvqbvd" return_type="int" args="()" >
<block>
<declaration>
<variable mut="var" type="int" name="a" ref_name="_bwagkmcgpmsgosa"/>
<rhs>
<literal type="int" value="1" />
</rhs>
</declaration>
<stream type="std_output">
<operator op="*" type="unset">
<lhs>
<variable mut="var" type="int" name="a" ref_name="_bwagkmcgpmsgosa"/>
</lhs>
<rhs>
<literal type="int" value="42" />
</rhs>
</operator>
</stream>
<return>
<literal type="int" value="0" />
</return>
</block>
</procedure>
</block>

View file

@ -0,0 +1,107 @@
import unittest
from ast_parser.ast_parser import AstParser
class TestParseCode(unittest.TestCase):
def test_parse_code(self):
input = """
procedure main() returns integer {
integer a = 1;
a * 42 -> std_output;
return 0;
}
"""
parser = AstParser(input)
parser.parse()
self.assertIsNotNone(parser.xml)
self.assertEqual(parser.xml.getroot().tag, "block")
def test_unparse_variable_regular(self):
input = '<variable mut="var" type="int" name="a" ref_name="_lsiyjvtbvnpmlml"/>'
parser = AstParser(input, True)
parser.input = ""
parser._unparse_node(parser.xml)
self.assertIsNotNone(parser.input)
self.assertEqual(" a ", parser.input)
def test_unparse_variable_declaration(self):
input = '<variable mut="var" type="int" name="a" ref_name="_lsiyjvtbvnpmlml"/>'
parser = AstParser(input, True)
parser.input = ""
parser._variable_unparse(parser.xml, True)
self.assertIsNotNone(parser.input)
self.assertEqual("var integer a ", parser.input)
def test_unparse_rhs_single(self):
input = '<rhs><literal type="int" value="1" /></rhs>'
parser = AstParser(input, True)
parser.input = ""
parser._unparse_node(parser.xml)
self.assertIsNotNone(parser.input)
self.assertEqual(" 1 ", parser.input)
def test_unparse_declaration(self):
input = '<declaration><variable mut="var" type="int" name="a" ref_name="_lsiyjvtbvnpmlml"/><rhs><literal type="int" value="1" /></rhs></declaration>'
parser = AstParser(input, True)
parser.input = ""
parser._unparse_node(parser.xml)
self.assertIsNotNone(parser.input)
self.assertEqual("var integer a = 1 ;\n", parser.input)
def test_unparse_stream(self):
input = ' <stream type="std_output"> <operator op="*" type="unset"> <lhs> <variable mut="var" type="int" name="a" ref_name="_lsiyjvtbvnpmlml"/> </lhs> <rhs> <literal type="int" value="42" /> </rhs> </operator></stream>'
parser = AstParser(input, True)
parser.input = ""
parser._unparse_node(parser.xml)
self.assertIsNotNone(parser.input)
self.assertEqual(" a * 42 -> std_output;\n", parser.input)
def test_unparse_block(self):
input = '<block> <declaration> <variable mut="var" type="int" name="a" ref_name="_lsiyjvtbvnpmlml"/> <rhs> <literal type="int" value="1" /> </rhs> </declaration> <stream type="std_output"> <operator op="*" type="unset"> <lhs> <variable mut="var" type="int" name="a" ref_name="_lsiyjvtbvnpmlml"/> </lhs> <rhs> <literal type="int" value="42" /> </rhs> </operator> </stream> <return> <literal type="int" value="0" /> </return> </block>'
parser = AstParser(input, True)
parser.input = ""
parser._unparse_node(parser.xml)
self.assertIsNotNone(parser.input)
self.assertEqual("{\n var integer a = 1 ;\n a * 42 -> std_output;\n return 0 ;\n}\n\n", parser.input)
def test_unparse_operation_single(self):
input = '<operator op="*" type="unset"> <lhs> <variable mut="var" type="int" name="a" ref_name="_lsiyjvtbvnpmlml"/> </lhs> <rhs> <literal type="int" value="42" /> </rhs> </operator>'
parser = AstParser(input, True)
parser.input = ""
parser._unparse_node(parser.xml)
self.assertIsNotNone(parser.input)
self.assertEqual(" a * 42 ", parser.input)
def test_unparse_return(self):
input = '<return> <literal type="int" value="0" /> </return>'
parser = AstParser(input, True)
parser.input = ""
parser._unparse_node(parser.xml)
self.assertIsNotNone(parser.input)
self.assertEqual("return 0 ;\n", parser.input)
def test_unparse_routine(self):
input = '<procedure name="main" ref_name="_jemrvvseyj" return_type="int" args="()" ><block><declaration><variable mut="var" type="int" name="a" ref_name="_lsiyjvtbvnpmlml"/><rhs><literal type="int" value="1" /></rhs></declaration><stream type="std_output"><operator op="*" type="unset"><lhs><variable mut="var" type="int" name="a" ref_name="_lsiyjvtbvnpmlml"/></lhs><rhs><literal type="int" value="42" /></rhs></operator></stream><return><literal type="int" value="0" /></return></block></procedure>'
parser = AstParser(input, True)
parser.input = ""
parser._unparse_node(parser.xml)
self.assertIsNotNone(parser.input)
i = ' ' * parser.indentation
self.assertEqual("procedure main() returns integer {\n var integer a = 1 ;\n a * 42 -> std_output;\n return 0 ;\n}\n\n", parser.input)
def test_unparse_code(self):
with open("test.xml", "r") as input:
parser = AstParser(input.read(), True)
parser.unparse()
self.assertIsNotNone(parser.input)
with open("input.in", "r") as input:
i = input.read()
self.assertEqual(i, parser.input)
if __name__ == "__main__":
unittest.main()

70
config.yaml Normal file
View file

@ -0,0 +1,70 @@
# The default configuration for the Gazprea Fuzzer
---
generation-options:
max-nesting-depth: 5 # maximum nesting depth for statements
max-conditionals-loops: 5 # maximum number of loops/conditionals per routine
properties:
max-range-length: 5 # maximum length of ranges, vectors and tuples, (axa matrices can exist)
id-length: # length of identifiers
min: 1
max: 10
function-name-length: # length of function names
min: 1
max: 10
expression-weights: # weights for expressions
# the higher a weight, the more likely (0, +inf)
brackets: 10
# arithmetic
addition: 80
subtraction: 80
multiplication: 30
division: 10
modulo: 10
power: 5
negation: 20
# comparison
equality: 50
inequality: 50
less-than: 30
greater-than: 30
less-than-or-equal: 10
greater-than-or-equal: 10
# logical
and: 50
or: 50
not: 10
xor: 10
# vector/string
generator: 20
range: 30
filter: 10
reverse: 10
concatenation: 50
statement-weights: # set to 0 for any statements you wish to exclude
variable-declaration:
int-declaration: 50
float-declaration: 50
char-declaration: 50
string-declaration: 50
bool-declaration: 50
vector-declaration: 20
tuple-declaration: 10
matrix-declaration: 10
function-call: 20
conditional: 30
loop: 20
assignment: 40
print: 20
input: 5
return: 5 # probability for a return statement to be placed arbitrarily in the middle of a generated procedure
block-termination-probability: 0.2 # probability for a block to terminate

3
fuzzer.py Normal file
View file

@ -0,0 +1,3 @@
class GazpreaFuzzer:
def __init__(self, config: str, ):
pass

83
gazprea_fuzzer.py Normal file
View file

@ -0,0 +1,83 @@
import json
from xml import etree
from fuzzingbook import Grammars
from fuzzingbook.Grammars import is_valid_grammar
from isla.solver import ISLaSolver
import ast_generator.ast_generator
from ast_parser.ast_solver import AstSolver
from ast_parser.ast_parser import AstParser
from ast_generator import gazprea_ast_grammar
import xml.etree.ElementTree as ET
import xml.dom.minidom
import ast_parser
class GazpreaFuzzer(ISLaSolver):
"""Produce Gazprea code"""
def __init__(self,
grammar: Grammars,
start_symbol: str = "<start>",
constraint: str = "",
**kwargs) -> None:
"""
@brief initialize a Gazprea code generator
:param grammar: the grammar from which you would like to generate code
:param start_symbol: the start symbol of the grammar (default "<start>")
:param constraint: any constraints that you would like to impose on the solver
:param kwargs: any extra arguments passed to the ISLaSolver
"""
assert start_symbol in grammar
assert is_valid_grammar(grammar)
super().__init__(grammar, constraint, start_symbol=start_symbol, **kwargs)
def fuzz(self) -> str:
"""Produce the hecking code"""
AST = AstParser(eval(str(self.solve())), from_xml=True)
AstSolver.fix_missing_locations(AST)
AST.unparse()
return AST.input
if __name__ == '__main__':
gen = ast_generator.ast_generator.AstGenerator(gazprea_ast_grammar.GAZPREA_TOP_LEVEL, json.loads('{}'))
with open("debug/test.xml", 'w') as t:
et = gen.generate_ast()
dom = xml.dom.minidom.parseString(ET.tostring(et).decode('utf-8'))
pretty: str = dom.toprettyxml()
repretty = ""
for line in pretty.split('\n'):
if line.startswith("<?xml"):
pass
else:
repretty += (line + '\n')
t.write(repretty)
gen.populate_ast(et)
with open("debug/populated.xml", 'w') as t:
dom = xml.dom.minidom.parseString(ET.tostring(et).decode('utf-8'))
pretty: str = dom.toprettyxml()
repretty = ""
for line in pretty.split('\n'):
if line.startswith("<?xml"):
pass
else:
repretty += (line + '\n')
t.write(repretty)
source = ast_parser.ast_parser.AstParser(et, from_xml=True)
source.unparse()
with open("debug/test.gz", 'w') as t:
t.write(source.input)

0
test/__init__.py Normal file
View file