hgarrereyn / GraphFuzz

GraphFuzz is an experimental framework for building structure-aware, library API fuzzers.
https://hgarrereyn.github.io/GraphFuzz
MIT License
249 stars 25 forks source link

To test the assimp library on GraphFuzz. #19

Open aT0ngMu opened 1 year ago

aT0ngMu commented 1 year ago

Hi, GraphFuzz is a very effective testing library API's work!!!! However, when I tested the assimp library on GraphFuzz, I encountered some issues.

Here are my steps: 1. git clone --depth=1 https:github.com/assimp/assimp.git 2. mkdir assimp_output 3. gfuzz doxygen --inputs ./sourceCode/assimp --output ./assimp_output 4. gfuzz schema infer ./assimp_output/xml ./assimp_output/schema.yaml 5. gfuzz gen cpp ./assimp_output/schema.yaml .

When I ran the fifth command, I received the following error message:

[!] Error while parsing: AC3DImporter() 'NoneType' object has no attribute 'base_type' [!] Error while parsing: ~AC3DImporter() override 'NoneType' object has no attribute 'base_type' [!] Error while parsing: Material() 'NoneType' object has no attribute 'base_type' [!] Error while parsing: Object() 'NoneType' object has no attribute 'base_type' [!] Error while parsing: Surface() 'NoneType' object has no attribute 'base_type' [!] Error while parsing: ~AMFImporter() override 'NoneType' object has no attribute 'base_type' [!] Error while parsing: BaseNode(Type _mType, const std::string &name) 'NoneType' object has no attribute 'base_type' [!] Error while parsing: Bone(const std::string &name) 'NoneType' object has no attribute 'base_type' [!] Error while parsing: Camera(const std::string &name) 'NoneType' object has no attribute 'base_type' [!] Error while parsing: Light(const std::string &name) 'NoneType' object has no attribute 'base_type' [!] Error while parsing: Material(const std::string &name) 'NoneType' object has no attribute 'base_type' [!] Error while parsing: Mesh(const std::string &name) 'NoneType' object has no attribute 'base_type' [!] Error while parsing: Parser(const char szFile, unsigned int fileFormatDefault) 'NoneType' object has no attribute 'base_type' [!] Error while parsing: ASEImporter() 'NoneType' object has no attribute 'base_type' [!] Error while parsing: AttachmentInfo() 'NoneType' object has no attribute 'base_type' [!] Error while parsing: AttachmentInfo(aiScene _scene, aiNode *_attachToNode) 'NoneType' object has no attribute 'base_type' [!] Error while parsing: ~B3DImporter() override 'NoneType' object has no attribute 'base_type' [!] Error while parsing: BVHLoader() 'NoneType' object has no attribute 'base_type' [!] Error while parsing: ~BVHLoader() 'NoneType' object has no attribute 'base_type' [!] Error while parsing: ~BaseImporter() 'NoneType' object has no attribute 'base_type' [!] Error while parsing: ~BaseProcess() 'NoneType' object has no attribute 'base_type' [!] Error while parsing: ~BatchLoader() 'NoneType' object has no attribute 'base_type' [!] Error while parsing: Base() 'NoneType' object has no attribute 'base_type' [!] Error while parsing: ConversionData(const FileDatabase &db) 'NoneType' object has no attribute 'base_type' [!] Error while parsing: CustomDataLayer() 'NoneType' object has no attribute 'base_type' [!] Error while parsing: DNAParser(FileDatabase &db) 'NoneType' object has no attribute 'base_type' [!] Error while parsing: ElemBase() 'NoneType' object has no attribute 'base_type' [!] Error while parsing: FileDatabase() 'NoneType' object has no attribute 'base_type' [!] Error while parsing: FileOffset() 'NoneType' object has no attribute 'base_type' [!] Error while parsing: MTFace() 'NoneType' object has no attribute 'base_type' [!] Error while parsing: MVert() 'NoneType' object has no attribute 'base_type' [!] Error while parsing: Object() 'NoneType' object has no attribute 'base_type' [!] Error while parsing: ObjectCache(const FileDatabase &db) 'NoneType' object has no attribute 'base_type' [!] Error while parsing: ObjectCache(const FileDatabase &) 'NoneType' object has no attribute 'base_type' Traceback (most recent call last): File "/home/server1/.local/bin/gfuzz", line 8, in sys.exit(main()) File "/home/server1/.local/lib/python3.8/site-packages/gfuzz/cli.py", line 23, in main options[args.mode].execute(args) File "/home/server1/.local/lib/python3.8/site-packages/gfuzz/commands/cliopt.py", line 17, in execute res = self._execute_fn(args) File "/home/server1/.local/lib/python3.8/site-packages/gfuzz/commands/gen/gen.py", line 24, in execute OPTIONS[args.gen_mode].execute(args) File "/home/server1/.local/lib/python3.8/site-packages/gfuzz/commands/cliopt.py", line 17, in execute res = self._execute_fn(args) File "/home/server1/.local/lib/python3.8/site-packages/gfuzz/commands/gen/cpp/gen_cpp.py", line 1328, in execute generate_harness( File "/home/server1/.local/lib/python3.8/site-packages/gfuzz/commands/gen/cpp/gen_cpp.py", line 1247, in generate_harness raw_scopes, max_num, errors = collect_scopes(schema, ignore_keywords, generate_dry) File "/home/server1/.local/lib/python3.8/site-packages/gfuzz/commands/gen/cpp/gen_cpp.py", line 1026, in collect_scopes scopes.append(CPPScope.destructor_for(schema, obj)) File "/home/server1/.local/lib/python3.8/site-packages/gfuzz/commands/gen/cpp/gen_cpp.py", line 319, in destructor_for return CPPScope.from_signature(schema, parent, sig, False) File "/home/server1/.local/lib/python3.8/site-packages/gfuzz/commands/gen/cpp/gen_cpp.py", line 337, in from_signature scope_type = _classify(parent, sig, is_static) File "/home/server1/.local/lib/python3.8/site-packages/gfuzz/commands/gen/cpp/gen_cpp.py", line 251, in _classify if sig.func_name.name == obj_name and not is_static: AttributeError: 'NoneType' object has no attribute 'func_name'

Is it due to my improper usage or some other reason? Looking forward to your assistance and your message!!! Thanks!!

hgarrereyn commented 1 year ago

Hi, unfortunately the doxygen tool is still very experimental -- there are some edge cases that it does not properly handle, particularly with namespaces.

The issue with:

[!] Error while parsing: AC3DImporter()
'NoneType' object has no attribute 'base_type'

is caused by this struct definition:

struct_Assimp::AC3DImporter:
  default_destructor: true
  headers:
  - ACLoader.h
  methods:
  - AC3DImporter()
  - ~AC3DImporter() override
  - bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const
    override
  name: Assimp::AC3DImporter
  static_methods: []
  type: struct

In order to find the constructor and destructor, the gfuzz tool expects the function name to match either <struct_name> or ~<struct_name>. Since the name is defined with a namespace as Assimp::AC3DImporter, it expects the constructor to look like: Assimp::AC3DImporter()

Unfortunately C++ is complex enough that there are still a fair number of edge cases like this in graphfuzz.


In general, I wouldn't recommend trying to harness the entire API at once. Rather, the strategy I used was to use the doxygen tool to generate a helpful starting point, then I copy and pasted the structs/classes I was interested in fuzzing in a new schema.

It definitely looks like Assimp could benefit from API fuzzing. I would recommend starting with one or two core structs and try to get those working in an isolated harness. Then you can try to expand the harness with new structs.

Let me know if you run into any specific issues trying to do this!

aT0ngMu commented 1 year ago

Hi, unfortunately the doxygen tool is still very experimental -- there are some edge cases that it does not properly handle, particularly with namespaces.

The issue with:

[!] Error while parsing: AC3DImporter()
'NoneType' object has no attribute 'base_type'

is caused by this struct definition:

struct_Assimp::AC3DImporter:
  default_destructor: true
  headers:
  - ACLoader.h
  methods:
  - AC3DImporter()
  - ~AC3DImporter() override
  - bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const
    override
  name: Assimp::AC3DImporter
  static_methods: []
  type: struct

In order to find the constructor and destructor, the gfuzz tool expects the function name to match either <struct_name> or ~<struct_name>. Since the name is defined with a namespace as Assimp::AC3DImporter, it expects the constructor to look like: Assimp::AC3DImporter()

Unfortunately C++ is complex enough that there are still a fair number of edge cases like this in graphfuzz.

In general, I wouldn't recommend trying to harness the entire API at once. Rather, the strategy I used was to use the doxygen tool to generate a helpful starting point, then I copy and pasted the structs/classes I was interested in fuzzing in a new schema.

It definitely looks like Assimp could benefit from API fuzzing. I would recommend starting with one or two core structs and try to get those working in an isolated harness. Then you can try to expand the harness with new structs.

Let me know if you run into any specific issues trying to do this!

Thank you very much for your explanation!! But I'm still encountering some issues.

Here are my steps:

  1. Generate a schema.yaml for the entire source code of the assimp library. gfuzz doxygen --inputs ./sourceCode/assimp --output ./assimp_output gfuzz schema infer ./assimp_output/xml ./assimp_output/schema.yaml gfuzz gen cpp ./assimp_output/schema.yaml .
  2. Select some structures from schema.yaml and paste them into custom.yaml.
  3. Generate a harness for custom.yaml. gfuzz gen cpp ./custom.yaml .
  4. Compile and run the generated harness. clang++ -I ../assimp/include -I ../assimp/code/Common -I ../assimp/include/assimp fuzz_exec.cpp ../assimp/lib/libassimp.a -o fuzz_exec -lz -lgraphfuzz -lprotobuf -fsanitize=address,fuzzer ./fuzz_exec

The error message after running the harness is as follows: [] Loading: schema.json [] GraphFuzz: loading trees from cache... [!] No initializer tree for type: "aiScene" (type: 87) [] After validation: total scopes: 19 [] After validation: usable scopes: 18 [!] Schema is invalid. (Run with "--graphfuzz_ignore_invalid" to continue with usable scopes).

I looked at the source code of GraphFuzz, and it seems that if initializer or finalizer for a struct is empty, it cannot pass validation. I'm not sure how to construct schema.yaml to make initializers or finalizers valid. Do you have any better suggestions?

hgarrereyn commented 1 year ago

Hi, could you share your custom.yaml file? The error message No initializer tree for type: "aiScene" means that GraphFuzz could not figure out a way to construct the struct aiScene.

aT0ngMu commented 1 year ago

I'm very sorry, I forgot to upload. The compressed file contains the relevant files. assimp_output.tar.gz