From 8a504ac17836ea3bf8e48ac9cbc173a342c7186c Mon Sep 17 00:00:00 2001 From: Yanju Chen Date: Wed, 13 Mar 2024 12:00:30 -0700 Subject: [PATCH] added multiple new detectors - detector_emptyf - detector_magicv - detector_susinst - detector_divrd - detector_downcast --- README.md | 15 +++- pyproject.toml | 2 +- tests/public/divrd0/build/main.aleo | 9 +- tests/public/divrd0/src/main.leo | 10 ++- tests/public/emptyf0/.gitignore | 5 ++ tests/public/emptyf0/README.md | 13 +++ tests/public/emptyf0/build/main.aleo | 19 ++++ tests/public/emptyf0/build/program.json | 6 ++ tests/public/emptyf0/leo.lock | 1 + tests/public/emptyf0/program.json | 6 ++ tests/public/emptyf0/src/main.leo | 24 +++++ tests/public/magicv0/.gitignore | 5 ++ tests/public/magicv0/README.md | 13 +++ tests/public/magicv0/build/main.aleo | 15 ++++ tests/public/magicv0/build/program.json | 6 ++ tests/public/magicv0/leo.lock | 1 + tests/public/magicv0/program.json | 6 ++ tests/public/magicv0/src/main.leo | 24 +++++ tests/public/susinst0/.gitignore | 5 ++ tests/public/susinst0/README.md | 13 +++ tests/public/susinst0/build/main.aleo | 21 +++++ tests/public/susinst0/build/program.json | 6 ++ tests/public/susinst0/leo.lock | 1 + tests/public/susinst0/program.json | 6 ++ tests/public/susinst0/src/main.leo | 29 ++++++ tests/test4.ipynb | 108 ++++++++++++----------- vanguard/aleo/detectors/__init__.py | 7 +- vanguard/aleo/detectors/divrd.py | 60 +++++++++++++ vanguard/aleo/detectors/downcast.py | 22 +++++ vanguard/aleo/detectors/emptyf.py | 36 ++++++++ vanguard/aleo/detectors/magicv.py | 25 ++++++ vanguard/aleo/detectors/susinst.py | 40 +++++++++ vanguard/aleo/grammar/instructions.py | 1 + vanguard/aleo/grammar/misc.py | 21 +++++ 34 files changed, 519 insertions(+), 62 deletions(-) create mode 100644 tests/public/emptyf0/.gitignore create mode 100644 tests/public/emptyf0/README.md create mode 100644 tests/public/emptyf0/build/main.aleo create mode 100644 tests/public/emptyf0/build/program.json create mode 100644 tests/public/emptyf0/leo.lock create mode 100644 tests/public/emptyf0/program.json create mode 100644 tests/public/emptyf0/src/main.leo create mode 100644 tests/public/magicv0/.gitignore create mode 100644 tests/public/magicv0/README.md create mode 100644 tests/public/magicv0/build/main.aleo create mode 100644 tests/public/magicv0/build/program.json create mode 100644 tests/public/magicv0/leo.lock create mode 100644 tests/public/magicv0/program.json create mode 100644 tests/public/magicv0/src/main.leo create mode 100644 tests/public/susinst0/.gitignore create mode 100644 tests/public/susinst0/README.md create mode 100644 tests/public/susinst0/build/main.aleo create mode 100644 tests/public/susinst0/build/program.json create mode 100644 tests/public/susinst0/leo.lock create mode 100644 tests/public/susinst0/program.json create mode 100644 tests/public/susinst0/src/main.leo create mode 100644 vanguard/aleo/detectors/divrd.py create mode 100644 vanguard/aleo/detectors/downcast.py create mode 100644 vanguard/aleo/detectors/emptyf.py create mode 100644 vanguard/aleo/detectors/magicv.py create mode 100644 vanguard/aleo/detectors/susinst.py diff --git a/README.md b/README.md index 793f603..a00c70f 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,8 @@ The following libraries are required for running (different components of) the t - [pandas](https://pandas.pydata.org/) (2.1.4+) for data analysis in test suite - [tabulate](https://github.com/astanin/python-tabulate) (0.9.0+) for result table rendering - Leo (**7ac50d8**) for compiling and running all benchmarks enclosed - - The tools is tested under this version, but newer version of Lao may also work. + - The tools is tested under this version, but newer version of Leo may also work. + - Older version may not work, as there are some breaking changes of Leo project structure. ## Usage @@ -51,7 +52,7 @@ pip uninstall vanguard After installation, you can directly use the commandline executable `vanguard-aleo` provided: ```bash -usage: vanguard-aleo [-h] [-b BUILD] [-p PID] [-f FIDS] [-d {divz,infoleak,rtcnst,unused}] [-v] +usage: vanguard-aleo [-h] [-b BUILD] [-p PID] [-f FIDS] [-d {divrd,divz,downcast,emptyf,infoleak,magicv,rtcnst,susinst,unused}] [-v] options: -h, --help show this help message and exit @@ -59,7 +60,7 @@ options: project build path, default: ./ -p PID, --pid PID program id, default: -f FIDS, --fids FIDS function ids (separated by comma, no space), default: - -d {divz,infoleak,rtcnst,unused}, --detector {divz,infoleak,rtcnst,unused} + -d {divrd,divz,downcast,emptyf,infoleak,magicv,rtcnst,susinst,unused}, --detector {divrd,divz,downcast,emptyf,infoleak,magicv,rtcnst,susinst,unused} detector to use, default: infoleak -v, --verbose whether or not to return extra info, default: False ``` @@ -151,6 +152,11 @@ from vanguard.aleo.detectors import detector_infoleak from vanguard.aleo.detectors import detector_rtcnst from vanguard.aleo.detectors import detector_unused from vanguard.aleo.detectors import detector_divz +from vanguard.aleo.detectors import detector_emptyf +from vanguard.aleo.detectors import detector_magicv +from vanguard.aleo.detectors import detector_susinst +from vanguard.aleo.detectors import detector_divrd +from vanguard.aleo.detectors import detector_downcast ... ``` @@ -170,6 +176,9 @@ You can find examples showing Leo/Aleo vulnerabilities with comments and annotat | rtcnst0/ | Returning constant | | underflow0/ | Arithmetic underflow | | unused0/ | Unused variable/signal | +| emptyf0/ | Empty functionality | +| magicv0/ | Magic variable | +| susinst0/ | Suspicious instruction | ## Parser/Lexer Generation diff --git a/pyproject.toml b/pyproject.toml index 3689f73..75605d2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "vanguard" -version = "0.0.4" +version = "0.0.5" authors = [ { name="Yanju Chen", email="yanju@veridise.com" }, ] diff --git a/tests/public/divrd0/build/main.aleo b/tests/public/divrd0/build/main.aleo index 74fbb71..dee5fa4 100644 --- a/tests/public/divrd0/build/main.aleo +++ b/tests/public/divrd0/build/main.aleo @@ -3,8 +3,8 @@ program divrd0.aleo; function vanguard_helper: - cast true true false true false true true into r0 as [boolean; 7u32]; - output r0 as [boolean; 7u32].private; + cast true true false true false true true true into r0 as [boolean; 8u32]; + output r0 as [boolean; 8u32].private; function ex0: @@ -48,3 +48,8 @@ function ex6: mul 15u8 2u8 into r0; div r0 9u8 into r1; output r1 as u8.private; + + +function ex7: + div 15u8 2u8 into r0; + output r0 as u8.private; diff --git a/tests/public/divrd0/src/main.leo b/tests/public/divrd0/src/main.leo index 5150e32..a64bb2b 100644 --- a/tests/public/divrd0/src/main.leo +++ b/tests/public/divrd0/src/main.leo @@ -1,10 +1,10 @@ // The 'divrd0' program. program divrd0.aleo { - transition vanguard_helper() -> [bool; 7] { + transition vanguard_helper() -> [bool; 8] { return [ label_ex0, label_ex1, label_ex2, label_ex3, - label_ex4, label_ex5, label_ex6, + label_ex4, label_ex5, label_ex6, label_ex7, ]; } @@ -69,4 +69,10 @@ program divrd0.aleo { let b: u8 = a * 2u8 / 9u8; return b; } + + const label_ex7: bool = true; + transition ex7 () -> u8 { + let a: u8 = 15u8 / 2u8; + return a; + } } diff --git a/tests/public/emptyf0/.gitignore b/tests/public/emptyf0/.gitignore new file mode 100644 index 0000000..f721f7f --- /dev/null +++ b/tests/public/emptyf0/.gitignore @@ -0,0 +1,5 @@ +.env +*.avm +*.prover +*.verifier +outputs/ diff --git a/tests/public/emptyf0/README.md b/tests/public/emptyf0/README.md new file mode 100644 index 0000000..cb1338b --- /dev/null +++ b/tests/public/emptyf0/README.md @@ -0,0 +1,13 @@ +# emptyf0.aleo + +## Build Guide + +To compile this Aleo program, run: +```bash +snarkvm build +``` + +To execute this Aleo program, run: +```bash +snarkvm run hello +``` diff --git a/tests/public/emptyf0/build/main.aleo b/tests/public/emptyf0/build/main.aleo new file mode 100644 index 0000000..69ccc85 --- /dev/null +++ b/tests/public/emptyf0/build/main.aleo @@ -0,0 +1,19 @@ +program emptyf0.aleo; + + + +function vanguard_helper: + cast true into r0 as [boolean; 1u32]; + output r0 as [boolean; 1u32].private; + + +function ex0: + + +function ex1: + async ex1 into r0; + output 9u8 as u8.private; + output r0 as emptyf0.aleo/ex1.future; + +finalize ex1: + assert.eq true true; diff --git a/tests/public/emptyf0/build/program.json b/tests/public/emptyf0/build/program.json new file mode 100644 index 0000000..fa72430 --- /dev/null +++ b/tests/public/emptyf0/build/program.json @@ -0,0 +1,6 @@ +{ + "program": "emptyf0.aleo", + "version": "0.0.0", + "description": "", + "license": "MIT" +} diff --git a/tests/public/emptyf0/leo.lock b/tests/public/emptyf0/leo.lock new file mode 100644 index 0000000..c4293b3 --- /dev/null +++ b/tests/public/emptyf0/leo.lock @@ -0,0 +1 @@ +package = [] diff --git a/tests/public/emptyf0/program.json b/tests/public/emptyf0/program.json new file mode 100644 index 0000000..fa72430 --- /dev/null +++ b/tests/public/emptyf0/program.json @@ -0,0 +1,6 @@ +{ + "program": "emptyf0.aleo", + "version": "0.0.0", + "description": "", + "license": "MIT" +} diff --git a/tests/public/emptyf0/src/main.leo b/tests/public/emptyf0/src/main.leo new file mode 100644 index 0000000..40bfd56 --- /dev/null +++ b/tests/public/emptyf0/src/main.leo @@ -0,0 +1,24 @@ +// The 'emptyf0' program. +program emptyf0.aleo { + // by default, arguments without visibility are private + // but in finalize, they have to be public + transition vanguard_helper() -> [bool; 2] { + return [ + label_ex0, label_ex1, + ]; + } + + const label_ex0: bool = true; + transition ex0() { + } + + const label_ex1: bool = true; + transition ex1() -> u8 { + return 9u8 then finalize(); + } + finalize ex1() { + assert(true); + } + + +} diff --git a/tests/public/magicv0/.gitignore b/tests/public/magicv0/.gitignore new file mode 100644 index 0000000..f721f7f --- /dev/null +++ b/tests/public/magicv0/.gitignore @@ -0,0 +1,5 @@ +.env +*.avm +*.prover +*.verifier +outputs/ diff --git a/tests/public/magicv0/README.md b/tests/public/magicv0/README.md new file mode 100644 index 0000000..e01d0af --- /dev/null +++ b/tests/public/magicv0/README.md @@ -0,0 +1,13 @@ +# magicv0.aleo + +## Build Guide + +To compile this Aleo program, run: +```bash +snarkvm build +``` + +To execute this Aleo program, run: +```bash +snarkvm run hello +``` diff --git a/tests/public/magicv0/build/main.aleo b/tests/public/magicv0/build/main.aleo new file mode 100644 index 0000000..6872ed3 --- /dev/null +++ b/tests/public/magicv0/build/main.aleo @@ -0,0 +1,15 @@ +program magicv0.aleo; + + + +function vanguard_helper: + cast true true into r0 as [boolean; 2u32]; + output r0 as [boolean; 2u32].private; + + +function ex0: + output aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc as address.private; + + +function ex1: + output 123u8 as u8.private; diff --git a/tests/public/magicv0/build/program.json b/tests/public/magicv0/build/program.json new file mode 100644 index 0000000..ed151f7 --- /dev/null +++ b/tests/public/magicv0/build/program.json @@ -0,0 +1,6 @@ +{ + "program": "magicv0.aleo", + "version": "0.0.0", + "description": "", + "license": "MIT" +} diff --git a/tests/public/magicv0/leo.lock b/tests/public/magicv0/leo.lock new file mode 100644 index 0000000..c4293b3 --- /dev/null +++ b/tests/public/magicv0/leo.lock @@ -0,0 +1 @@ +package = [] diff --git a/tests/public/magicv0/program.json b/tests/public/magicv0/program.json new file mode 100644 index 0000000..ed151f7 --- /dev/null +++ b/tests/public/magicv0/program.json @@ -0,0 +1,6 @@ +{ + "program": "magicv0.aleo", + "version": "0.0.0", + "description": "", + "license": "MIT" +} diff --git a/tests/public/magicv0/src/main.leo b/tests/public/magicv0/src/main.leo new file mode 100644 index 0000000..c0ce6c1 --- /dev/null +++ b/tests/public/magicv0/src/main.leo @@ -0,0 +1,24 @@ +// The 'magicv0' program. +program magicv0.aleo { + // by default, arguments without visibility are private + // but in finalize, they have to be public + transition vanguard_helper() -> [bool; 2] { + return [ + label_ex0, label_ex1, + ]; + } + + const label_ex0: bool = true; + transition ex0() -> address { + let a: address = aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc; + return a; + } + + const label_ex1: bool = true; + transition ex1() -> u8 { + let a: u8 = 123u8; + return a; + } + + +} diff --git a/tests/public/susinst0/.gitignore b/tests/public/susinst0/.gitignore new file mode 100644 index 0000000..f721f7f --- /dev/null +++ b/tests/public/susinst0/.gitignore @@ -0,0 +1,5 @@ +.env +*.avm +*.prover +*.verifier +outputs/ diff --git a/tests/public/susinst0/README.md b/tests/public/susinst0/README.md new file mode 100644 index 0000000..9247509 --- /dev/null +++ b/tests/public/susinst0/README.md @@ -0,0 +1,13 @@ +# susinst0.aleo + +## Build Guide + +To compile this Aleo program, run: +```bash +snarkvm build +``` + +To execute this Aleo program, run: +```bash +snarkvm run hello +``` diff --git a/tests/public/susinst0/build/main.aleo b/tests/public/susinst0/build/main.aleo new file mode 100644 index 0000000..a6ac725 --- /dev/null +++ b/tests/public/susinst0/build/main.aleo @@ -0,0 +1,21 @@ +program susinst0.aleo; + + + +function vanguard_helper: + cast true true true into r0 as [boolean; 3u32]; + output r0 as [boolean; 3u32].private; + + +function ex0: + assert.eq true true; + + +function ex1: + add 123u8 0u8 into r0; + output r0 as u8.private; + + +function ex2: + mul 123u8 1u8 into r0; + output r0 as u8.private; diff --git a/tests/public/susinst0/build/program.json b/tests/public/susinst0/build/program.json new file mode 100644 index 0000000..b9ad403 --- /dev/null +++ b/tests/public/susinst0/build/program.json @@ -0,0 +1,6 @@ +{ + "program": "susinst0.aleo", + "version": "0.0.0", + "description": "", + "license": "MIT" +} diff --git a/tests/public/susinst0/leo.lock b/tests/public/susinst0/leo.lock new file mode 100644 index 0000000..c4293b3 --- /dev/null +++ b/tests/public/susinst0/leo.lock @@ -0,0 +1 @@ +package = [] diff --git a/tests/public/susinst0/program.json b/tests/public/susinst0/program.json new file mode 100644 index 0000000..b9ad403 --- /dev/null +++ b/tests/public/susinst0/program.json @@ -0,0 +1,6 @@ +{ + "program": "susinst0.aleo", + "version": "0.0.0", + "description": "", + "license": "MIT" +} diff --git a/tests/public/susinst0/src/main.leo b/tests/public/susinst0/src/main.leo new file mode 100644 index 0000000..8ebb7a6 --- /dev/null +++ b/tests/public/susinst0/src/main.leo @@ -0,0 +1,29 @@ +// The 'susinst0' program. +program susinst0.aleo { + // by default, arguments without visibility are private + // but in finalize, they have to be public + transition vanguard_helper() -> [bool; 3] { + return [ + label_ex0, label_ex1, label_ex2 + ]; + } + + const label_ex0: bool = true; + transition ex0() { + assert(true); + } + + const label_ex1: bool = true; + transition ex1() -> u8 { + let a: u8 = 123u8 + 0u8; + return a; + } + + const label_ex2: bool = true; + transition ex2() -> u8 { + let a: u8 = 123u8 * 1u8; + return a; + } + + +} diff --git a/tests/test4.ipynb b/tests/test4.ipynb index 1ad4284..d9ff4b6 100644 --- a/tests/test4.ipynb +++ b/tests/test4.ipynb @@ -26,6 +26,11 @@ "from vanguard.aleo.detectors import detector_rtcnst\n", "from vanguard.aleo.detectors import detector_unused\n", "from vanguard.aleo.detectors import detector_divz\n", + "from vanguard.aleo.detectors import detector_emptyf\n", + "from vanguard.aleo.detectors import detector_magicv\n", + "from vanguard.aleo.detectors import detector_susinst\n", + "from vanguard.aleo.detectors import detector_divrd\n", + "from vanguard.aleo.detectors import detector_downcast\n", "\n", "from vanguard.aleo.testing import run_test_suite" ] @@ -46,9 +51,14 @@ ], "source": [ "# project_name = \"divz0\"\n", - "project_name = \"infoleak0\"\n", + "# project_name = \"infoleak0\"\n", "# project_name = \"unused0\"\n", "# project_name = \"rtcnst0\"\n", + "# project_name = \"emptyf0\"\n", + "# project_name = \"magicv0\"\n", + "# project_name = \"susinst0\"\n", + "# project_name = \"divrd0\"\n", + "project_name = \"downcast0\"\n", "build_path = f\"./tests/public/{project_name}/build/\"\n", "env = AleoEnvironment.from_project(build_path)\n", "main = env.main" @@ -56,47 +66,57 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 6, "id": "0dc6fcff-f8b2-44cb-8919-fda9b23c0b49", "metadata": {}, "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "# [debug] ret: ['{: , : }']\n", - "# [debug] pvars: ['r0.content']\n", - "# [debug] lines: ['output r0.content as struct_ex22.public;']\n" - ] - }, { "data": { "text/plain": [ - "(True, ['output r0.content as struct_ex22.public;'])" + "(True, ['cast 65530u16 into r0 as u8;'])" ] }, - "execution_count": 8, + "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "fid = \"ex22\"\n", + "fid = \"ex1\"\n", "# detector_divz(env, main.id, fid, readable=True)\n", - "detector_infoleak(env, main.id, fid, readable=True)" + "# detector_infoleak(env, main.id, fid, readable=True)\n", + "# detector_emptyf(env, main.id, fid, readable=True)\n", + "# detector_magicv(env, main.id, fid, readable=True)\n", + "# detector_susinst(env, main.id, fid, readable=True)\n", + "# detector_divrd(env, main.id, fid, readable=True)\n", + "detector_downcast(env, main.id, fid, readable=True)" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "id": "e895857f-8473-4e0e-a561-3553469ceceb", "metadata": {}, - "outputs": [], - "source": [] + "outputs": [ + { + "ename": "ValueError", + "evalue": "4 is not in list", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[6], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43m[\u001b[49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m,\u001b[49m\u001b[38;5;241;43m2\u001b[39;49m\u001b[43m,\u001b[49m\u001b[38;5;241;43m3\u001b[39;49m\u001b[43m]\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mindex\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m4\u001b[39;49m\u001b[43m)\u001b[49m\n", + "\u001b[0;31mValueError\u001b[0m: 4 is not in list" + ] + } + ], + "source": [ + "[1,2,3].index(4)" + ] }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 12, "id": "a983f9b5-286c-4748-b2c7-b450ff0d7948", "metadata": {}, "outputs": [ @@ -105,50 +125,32 @@ "output_type": "stream", "text": [ "# [debug] deploy: main.aleo\n", - "# [✓][test] pid: infoleak0.aleo, fid: ex0, expected: True, actual: True\n", - "# [✓][test] pid: infoleak0.aleo, fid: ex1, expected: False, actual: False\n", - "# [✓][test] pid: infoleak0.aleo, fid: ex2, expected: True, actual: True\n", - "# [✓][test] pid: infoleak0.aleo, fid: ex3, expected: True, actual: True\n", - "# [✓][test] pid: infoleak0.aleo, fid: ex4, expected: True, actual: True\n", - "# [✓][test] pid: infoleak0.aleo, fid: ex5, expected: True, actual: True\n", - "# [✓][test] pid: infoleak0.aleo, fid: ex6, expected: True, actual: True\n", - "# [✓][test] pid: infoleak0.aleo, fid: ex7, expected: False, actual: False\n", - "# [✓][test] pid: infoleak0.aleo, fid: ex8, expected: True, actual: True\n", - "# [✓][test] pid: infoleak0.aleo, fid: ex9, expected: True, actual: True\n", - "# [✓][test] pid: infoleak0.aleo, fid: ex10, expected: True, actual: True\n", - "# [✗][test] pid: infoleak0.aleo, fid: ex11, expected: False, actual: True\n", - "# [✓][test] pid: infoleak0.aleo, fid: ex12, expected: True, actual: True\n", - "# [✓][test] pid: infoleak0.aleo, fid: ex13, expected: True, actual: True\n", - "# [✓][test] pid: infoleak0.aleo, fid: ex14, expected: True, actual: True\n", - "# [✓][test] pid: infoleak0.aleo, fid: ex15, expected: True, actual: True\n", - "# [✓][test] pid: infoleak0.aleo, fid: ex16, expected: True, actual: True\n", - "# [✓][test] pid: infoleak0.aleo, fid: ex17, expected: True, actual: True\n", - "# [✓][test] pid: infoleak0.aleo, fid: ex18, expected: True, actual: True\n", - "# [✓][test] pid: infoleak0.aleo, fid: ex19, expected: True, actual: True\n", - "# [✓][test] pid: infoleak0.aleo, fid: ex20, expected: True, actual: True\n", - "# [✓][test] pid: infoleak0.aleo, fid: ex21, expected: True, actual: True\n", - "# [✓][test] pid: infoleak0.aleo, fid: ex22, expected: True, actual: True\n", - "# [✓][test] pid: infoleak0.aleo, fid: ex23, expected: True, actual: True\n", - "# [✓][test] pid: infoleak0.aleo, fid: ex24, expected: False, actual: False\n", - "# [test] accuracy: 24/25 (0.9600)\n", + "# [✓][test] pid: unused0.aleo, fid: ex0, expected: False, actual: False\n", + "# [✓][test] pid: unused0.aleo, fid: ex1, expected: True, actual: True\n", + "# [✓][test] pid: unused0.aleo, fid: ex2, expected: True, actual: True\n", + "# [✗][test] pid: unused0.aleo, fid: ex3, expected: False, actual: True\n", + "# [✓][test] pid: unused0.aleo, fid: ex4, expected: True, actual: True\n", + "# [✗][test] pid: unused0.aleo, fid: ex5, expected: False, actual: True\n", + "# [✓][test] pid: unused0.aleo, fid: ex6, expected: True, actual: True\n", + "# [test] accuracy: 5/7 (0.7143)\n", "# [test] confusion matrix:\n", " actual False True \n", "expected \n", - "False 3 1\n", - "True 0 21\n", + "False 1 2\n", + "True 0 4\n", "# [test] normalized confusion matrix:\n", - " actual False True \n", - "expected \n", - "False 0.75 0.25\n", - "True 0.00 1.00\n" + " actual False True \n", + "expected \n", + "False 0.333333 0.666667\n", + "True 0.000000 1.000000\n" ] } ], "source": [ "# r = run_test_suite(\"./tests/public/divz0/build/\", detector_divz, verbose=True)\n", - "r = run_test_suite(\"./tests/public/infoleak0/build/\", detector_infoleak, verbose=True)\n", + "# r = run_test_suite(\"./tests/public/infoleak0/build/\", detector_infoleak, verbose=True)\n", "# r = run_test_suite(\"./tests/public/rtcnst0/build/\", detector_rtcnst, verbose=True)\n", - "# r = run_test_suite(\"./tests/public/unused0/build/\", detector_unused, verbose=True)" + "r = run_test_suite(\"./tests/public/unused0/build/\", detector_unused, verbose=True)" ] }, { diff --git a/vanguard/aleo/detectors/__init__.py b/vanguard/aleo/detectors/__init__.py index db7ea22..9bbcda0 100644 --- a/vanguard/aleo/detectors/__init__.py +++ b/vanguard/aleo/detectors/__init__.py @@ -1,4 +1,9 @@ from .divz import detector_divz from .infoleak import detector_infoleak from .rtcnst import detector_rtcnst -from .unused import detector_unused \ No newline at end of file +from .unused import detector_unused +from .emptyf import detector_emptyf +from .magicv import detector_magicv +from .susinst import detector_susinst +from .divrd import detector_divrd +from .downcast import detector_downcast \ No newline at end of file diff --git a/vanguard/aleo/detectors/divrd.py b/vanguard/aleo/detectors/divrd.py new file mode 100644 index 0000000..0515d23 --- /dev/null +++ b/vanguard/aleo/detectors/divrd.py @@ -0,0 +1,60 @@ +from ..grammar import * +from ..graphs import get_dfg_edges + +def detector_divrd(env: AleoEnvironment, pid: str, fid: str, readable=False): + # initialize + prog: AleoProgram = env.programs[pid] + func: AleoFunction = prog.functions[fid] + + dinsts = [] + + # check for statement level redundancy + all_insts = func.instructions + ([] if func.finalize is None else func.finalize.instructions) + for inst in all_insts: + match inst: + case AleoBinary() if inst.op in {AleoBinaryOp.DIV, AleoBinaryOp.DIVW}: + if isinstance(inst.operands[0], AleoLiteral) and isinstance(inst.operands[1], AleoLiteral): + # pattern 1: literal division + a = inst.operands[0].value + b = inst.operands[1].value + if a % b != 0: + dinsts.append(f"{inst}" if readable else inst) + else: + # FIXME: need to infer value, for now, just ignore + # dinsts.append(f"{inst}" if readable else inst) + pass + + case _: + # not interested + pass + + # pattern 2: division before multiplication + # FIXME: values are not tracked across transition/finalize, neither in external calls + # FIXME: data structures are not considered + div_dests = [] # tracking division destinations + div_lines = [] + for inst in func.instructions: + match inst: + case AleoBinary() if inst.op in {AleoBinaryOp.DIV, AleoBinaryOp.DIVW}: + div_dests.append(f"{inst.regacc}") + div_lines.append(inst) + case AleoBinary() if inst.op in {AleoBinaryOp.MUL, AleoBinaryOp.MULW}: + ind = None + if f"{inst.operands[0]}" in div_dests: + ind = div_dests.index(f"{inst.operands[0]}") + elif f"{inst.operands[1]}" in div_dests: + ind = div_dests.index(f"{inst.operands[1]}") + else: + # do nothing + pass + if ind is not None: + dinsts.append( + (f"{div_lines[ind]}", f"{inst}") if readable else\ + (div_lines[ind], inst) + ) + + case _: + # not interested + pass + + return (len(dinsts)>0, dinsts) \ No newline at end of file diff --git a/vanguard/aleo/detectors/downcast.py b/vanguard/aleo/detectors/downcast.py new file mode 100644 index 0000000..7f88033 --- /dev/null +++ b/vanguard/aleo/detectors/downcast.py @@ -0,0 +1,22 @@ +from ..grammar import * +from ..graphs import get_dfg_edges + +def detector_downcast(env: AleoEnvironment, pid: str, fid: str, readable=False): + # initialize + prog: AleoProgram = env.programs[pid] + func: AleoFunction = prog.functions[fid] + + dinsts = [] + + # check for statement level redundancy + all_insts = func.instructions + ([] if func.finalize is None else func.finalize.instructions) + for inst in all_insts: + match inst: + case AleoCast() if len(inst.operands) == 1: + dinsts.append(f"{inst}" if readable else inst) + + case _: + # not interested + pass + + return (len(dinsts)>0, dinsts) \ No newline at end of file diff --git a/vanguard/aleo/detectors/emptyf.py b/vanguard/aleo/detectors/emptyf.py new file mode 100644 index 0000000..9e45515 --- /dev/null +++ b/vanguard/aleo/detectors/emptyf.py @@ -0,0 +1,36 @@ +from ..grammar import * +from ..graphs import get_dfg_edges + +def detector_emptyf(env: AleoEnvironment, pid: str, fid: str, readable=False): + # initialize + prog: AleoProgram = env.programs[pid] + func: AleoFunction = prog.functions[fid] + + efs = [] + + # check for function level redundancy + if len(func.instructions) == 0: + efs.append(f"transition {func.id}") + + if func.finalize is not None: + if len(func.finalize.instructions) == 0: + efs.append(f"finalize {func.finalize.id}") + + # check for statement level redundancy + all_insts = func.instructions + ([] if func.finalize is None else func.finalize.instructions) + for inst in all_insts: + match inst: + case AleoAssert(): + g = True + for p in inst.operands: + if not isinstance(p, AleoLiteral): + g = False + break + if g: + efs.append(f"{inst}" if readable else inst) + + case _: + # not interested + pass + + return (len(efs)>0, efs) \ No newline at end of file diff --git a/vanguard/aleo/detectors/magicv.py b/vanguard/aleo/detectors/magicv.py new file mode 100644 index 0000000..d540ffc --- /dev/null +++ b/vanguard/aleo/detectors/magicv.py @@ -0,0 +1,25 @@ +from ..grammar import * +from ..graphs import get_dfg_edges + +def detector_magicv(env: AleoEnvironment, pid: str, fid: str, readable=False): + # initialize + prog: AleoProgram = env.programs[pid] + func: AleoFunction = prog.functions[fid] + + mvs = [] + + def mm(node): + match node: + case AleoAddressLiteral(): + mvs.append(f"{node}" if readable else node) + case AleoUnsignedLiteral() | AleoSignedLiteral(): + if node.value >= 2 or node.value <= -2: + mvs.append(f"{node}" if readable else node) + case _: + # not interested + pass + + # check for magic values + AleoNode.visit(func, fn_pre=mm) + + return (len(mvs)>0, mvs) \ No newline at end of file diff --git a/vanguard/aleo/detectors/susinst.py b/vanguard/aleo/detectors/susinst.py new file mode 100644 index 0000000..d512764 --- /dev/null +++ b/vanguard/aleo/detectors/susinst.py @@ -0,0 +1,40 @@ +from ..grammar import * +from ..graphs import get_dfg_edges + +def detector_susinst(env: AleoEnvironment, pid: str, fid: str, readable=False): + # initialize + prog: AleoProgram = env.programs[pid] + func: AleoFunction = prog.functions[fid] + + sinsts = [] + + # check for statement level redundancy + all_insts = func.instructions + ([] if func.finalize is None else func.finalize.instructions) + for inst in all_insts: + match inst: + case AleoAssert(): + if f"{inst.operands[0]}" == f"{inst.operands[1]}": + sinsts.append(f"{inst}" if readable else inst) + + case AleoBinary(): + match inst.op: + case AleoBinaryOp.ADD | AleoBinaryOp.ADDW: + for p in inst.operands: + if isinstance(p, AleoLiteral) and p.value == 0: + sinsts.append(f"{inst}" if readable else inst) + case AleoBinaryOp.SUB | AleoBinaryOp.SUBW: + if isinstance(inst.operands[1], AleoLiteral) and inst.operands[1].value == 0: + sinsts.append(f"{inst}" if readable else inst) + case AleoBinaryOp.MUL | AleoBinaryOp.MULW: + for p in inst.operands: + if isinstance(p, AleoLiteral) and p.value == 1: + sinsts.append(f"{inst}" if readable else inst) + case _: + # not interested + pass + + case _: + # not interested + pass + + return (len(sinsts)>0, sinsts) \ No newline at end of file diff --git a/vanguard/aleo/grammar/instructions.py b/vanguard/aleo/grammar/instructions.py index 47e3ce8..9f998e5 100644 --- a/vanguard/aleo/grammar/instructions.py +++ b/vanguard/aleo/grammar/instructions.py @@ -418,6 +418,7 @@ class AleoAssert(AleoInstruction): def from_json(node): match node: case ["assert", op, *operands, ";"]: + assert len(operands) == 2, f"Unsupported number of operands, expected: 2, got: {len(operands)}" _op = AleoAssertOp.from_json(op) _operands = [AleoOperand.from_json(p) for p in operands] return AleoAssert(_op, _operands) diff --git a/vanguard/aleo/grammar/misc.py b/vanguard/aleo/grammar/misc.py index 27f21fd..b42a962 100644 --- a/vanguard/aleo/grammar/misc.py +++ b/vanguard/aleo/grammar/misc.py @@ -3,6 +3,27 @@ # primitive type of all Aleo components class AleoNode: + + @staticmethod + def visit(node, fn_pre=None, fn_post=None): + if fn_pre is not None: fn_pre(node) + if isinstance(node, AleoNode): + for p in vars(node).values(): + AleoNode.visit(p, fn_pre=fn_pre, fn_post=fn_post) + elif isinstance(node, list): + for q in node: + AleoNode.visit(q, fn_pre=fn_pre, fn_post=fn_post) + elif isinstance(node, dict): + for k,v in node.items(): + AleoNode.visit(v, fn_pre=fn_pre, fn_post=fn_post) + elif isinstance(node, tuple): + for p in node: + AleoNode.visit(p, fn_pre=fn_pre, fn_post=fn_post) + else: + # not interested + pass + if fn_post is not None: fn_post(node) + # FIXME: prevent direct initialization that is compatible with Enum child class # NOTE: need both args and kwargs as child class also inherits Enum def __init__(self, *args, **kwargs):