CauldronDevelopmentLLC / CAMotics

Open-Source Simulation & Computer Aided Machining - A 3-axis CNC GCode simulator
Other
602 stars 138 forks source link

Cannot compile Fuzz Target linking to libGCode.a #374

Closed tiivolution closed 10 months ago

tiivolution commented 1 year ago

I'm currently working on a project regarding fuzzing a few selected pieces of gcode parsing software.

As a fuzzing environment I'm using google's oss-fuzz but I keep struggling to link my fuzz.cpp against the libGCode.a library built inside the docker container.

My setup is as follows:

  1. Dockerfilebuilds cbang and camotics inside $SRC/camotics ( see: https://github.com/toombaumarkt/oss-fuzz-gcode/blob/master/projects/camotics/Dockerfile )
  2. Build.sh file tries to build my fuzz target by linking to $SRC/camotics/build/libGCode.a ( see: https://github.com/toombaumarkt/oss-fuzz-gcode/blob/master/projects/camotics/build.sh ). With all the build flags inserted by oss-fuzz, the command translates to: clang++ -O1 -fno-omit-frame-pointer -gline-tables-only -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -fsanitize=address -fsanitize-address-use-after-scope -fsanitize=fuzzer-no-link -stdlib=libc++ -I/src/camotics/src -I/src/camotics/cbang/src -Wall /src/parser/wrapper.cpp -o /out/camotics_libfuzzer -fsanitize=fuzzer /src/camotics/build/libGCode.a -L/src/camotics/cbang/lib -pthread

I keep getting a hundreds of errors like:

  1. /usr/bin/ld: /tmp/wrapper-3ade21.o: in function LLVMFuzzerTestOneInput: /src/parser/wrapper.cpp:25: undefined reference to cb::InputSource::InputSource(char const*, long, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
  2. /usr/bin/ld: /src/camotics/build/libGCode.a(NamedReference.o): in function _GLOBAL__sub_I_NamedReference.cpp: NamedReference.cpp:(.text.startup._GLOBAL__sub_I_NamedReference.cpp+0x10): undefined reference to std::ios_base::Init::Init()
  3. /usr/bin/ld: OCode.cpp:(.text._ZNK5GCode5OCode5printERSo+0xc5): undefined reference to std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
  4. /usr/bin/ld: BinaryOp.cpp:(.text._ZN5GCode8BinaryOpC2EN2cb11EnumerationINS_19OperatorEnumerationEEERKNS1_12SmartPointerINS_6EntityENS1_10DeallocNewIS6_EENS1_14RefCounterImplIS6_S8_EEEESD_+0x643): undefined reference to std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_create(unsigned long&, unsigned long)

I read that this can be due to the library being build with a different c++ standard, but adding -std=c++11 as a flag in build.sh doesn't help either.

I hope someone has an idea about what's going on here :D

jcoffland commented 1 year ago

This does indeed look like a problem with building with different C++ libraries. The errors are all related to the std namespace. Are you building part of it with g++ and part with clang?

tiivolution commented 1 year ago

Yep, it seems like camotics is being built with gcc and g++. How can I tell scons to use the compiler and corresponding flags set by oss-fuzz in the environment variables CC, CXX and CXXFLAGS? I already tried to change line 14 of the SConstruct file like that but this doesn't change anything and gcc / g++ keep getting used instead.

jcoffland commented 1 year ago

You can build both C! and CAMotics like this:

scons compiler=clang
tiivolution commented 1 year ago

Alright, that worked! But the remaining problem is: how do I get scons to use the CFLAGS / CXXFLAGS provided by Oss-Fuzz. The environment variables look like this:

CFLAGS=-O1 -fno-omit-frame-pointer -gline-tables-only -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -fsanitize=address -fsanitize-address-use-after-scope -fsanitize=fuzzer-no-link

CXXFLAGS=-O1 -fno-omit-frame-pointer -gline-tables-only -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -fsanitize=address -fsanitize-address-use-after-scope -fsanitize=fuzzer-no-link -stdlib=libc++

I tried inserting them like this into the SConstruct file:

# Setup
env = Environment(ENV = os.environ,
           CFLAGS = '-O1 -fno-omit-frame-pointer -gline-tables-only -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -fsanitize=address -fsanitize-address-use-after-scope -fsanitize=fuzzer-no-link',
          CXXFLAGS = '-O1 -fno-omit-frame-pointer -gline-tables-only -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -fsanitize=address -fsanitize-address-use-after-scope -fsanitize=fuzzer-no-link -stdlib=libc++',
                  TARGET_ARCH = os.environ.get('TARGET_ARCH', 'x86'))

But then these flags end up inside quotes like this in the command line and thus being ignored because I still get those std errors: clang++ -o build/boost/libs/iostreams/src/file_descriptor.o -c "-O1 -fno-omit-frame-pointer -gline-tables-only -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -fsanitize=address -fsanitize-address-use-after-scope -fsanitize=fuzzer-no-link -stdlib=libc++" -faligned-new -std=c++11 -fsigned-char -O3 -funroll-loops -fno-pie -fPIC -DNDEBUG -D_REENTRANT -DUSING_CBANG -I/usr/include/v8 -Iinclude -Isrc -Isrc/boost src/boost/libs/iostreams/src/file_descriptor.cpp

When just inserting -stdlib=libc++ as CXXFLAGS it does work for C! and I don't get these std related errors anymore. Doing the same for the SConstruct file for CAMotics then leads to an undefined reference to v8::platform::NewDefaultPlatform.

What I just ended up doing is compiling C! like mentioned above and isolating the GCode parsing code from CAMotics to another folder and ignoring the rest. I then compile only these files while linking against the C! libs. In this manner I can also easily insert the CXXFLAGS by oss-fuzz. Though it would be more elegant to be able to integrate building a fuzz target by just linking the libGCode file.

Anyway, thank you!

jcoffland commented 1 year ago
scons compiler=clang ccflags="-O1 -fno-omit-frame-pointer -gline-tables-only -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -fsanitize=address -fsanitize-address-use-after-scope -fsanitize=fuzzer-no-link" cxxflags="-stdlib=libc++"
tiivolution commented 1 year ago

This sounds promising! But unfortunately scons now can't find any libraries apart from pthread interestingly :thinking:

Checking for C library pthread... yes
Checking for C header file zlib.h... no
Need header zlib.h(cached) error: no result
Checking for C header file bzlib.h... no
Need header bzlib.h(cached) error: no result
Checking for C header file expat.h... no
Need C header expat.h(cached) error: no result
Checking for C header file pthread.h... yes
Checking for C library pthread... no
Need library pthread(cached) error: no result
Checking for C header file yaml.h... no
Need header yaml.h(cached) error: no result
Checking for C library snappy... no
Need library snappy(cached) error: no result
Checking for C header file mysql/mysql.h... no
Checking for C library lz4... no
Checking for C function clock_gettime()... no
Checking for C library rt... no
Exception: Need library rt:
jcoffland commented 1 year ago

The compiler must not like those options. Look in config.log for the errors. Also, in order to disable optimizations you'll need to pass optimize=0 to scons instead of the -O1 option.

tiivolution commented 1 year ago

Scons appears to be using these flags also to perform these configuration steps prior to compiling the project itself.

scons: Configure: Checking for C library z...
.sconf_temp/conftest_2.c <-
  |
  |
  |
  |int
  |main() {
  |  
  |return 0;
  |}
  |
clang -o .sconf_temp/conftest_2.o -c -std=c99 -fsigned-char -O3 -funroll-loops -fno-pie -O1 -fno-omit-frame-pointer -gline-tables-only -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -fsanitize=address -fsanitize-address-use-after-scope -fsanitize=fuzzer-no-link -DNDEBUG -D_REENTRANT .sconf_temp/conftest_2.c
clang -o .sconf_temp/conftest_2 -Wl,-x -pthread .sconf_temp/conftest_2.o -lpthread -lz
/usr/bin/ld: .sconf_temp/conftest_2.o: in function asan.module_ctor:
conftest_2.c:(.text+0x2): undefined reference to __asan_init'
/usr/bin/ld: conftest_2.c:(.text+0x7): undefined reference to __asan_version_mismatch_check_v8
/usr/bin/ld: .sconf_temp/conftest_2.o: in function sancov.module_ctor_8bit_counters:
conftest_2.c:(.text.sancov.module_ctor_8bit_counters[sancov.module_ctor_8bit_counters]+0xc): undefined reference to __sanitizer_cov_8bit_counters_init
/usr/bin/ld: conftest_2.c:(.text.sancov.module_ctor_8bit_counters[sancov.module_ctor_8bit_counters]+0x1b): undefined reference to __sanitizer_cov_pcs_init
clang: error: linker command failed with exit code 1 (use -v to see invocation)
scons: Configure: no

After compiling with the -fsanitizer=address flag, the compiler does not link -fsanitize=address in the next step resulting in an undefined reference. Is it possible to tell scons to use these provided flags only for the project it's compiling?

jcoffland commented 1 year ago

You need to add linkflags. Something like this:

scons compiler=clang optimize=0 ccflags="-fno-omit-frame-pointer -gline-tables-only -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -fsanitize=address -fsanitize-address-use-after-scope -fsanitize=fuzzer-no-link" cxxflags="-stdlib=libc++" linkflags="-fsanitize=address -fsanitize-address-use-after-scope -fsanitize=fuzzer-no-link"