diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..bd1298c --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,13 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added + +- Initial Ruzzy implementation +- Support for fuzzing Ruby C extensions diff --git a/ext/cruzzy/extconf.rb b/ext/cruzzy/extconf.rb index 0db675e..54d4b10 100644 --- a/ext/cruzzy/extconf.rb +++ b/ext/cruzzy/extconf.rb @@ -4,22 +4,36 @@ require 'open3' require 'tempfile' -CC = 'clang' -CXX = 'clang++' +# These ENV variables really shouldn't be used because we don't support +# compilers other than clang, like gcc, etc. Instead prefer to properly include +# clang in your PATH. But they're here if you really need them. Also note that +# *technically* Ruby does not support C extensions compiled with a different +# compiler than Ruby itself was compiled with. So we're on somewhat shaky +# ground here. For more information see: +# https://github.com/rubygems/rubygems/issues/1508 +CC = ENV.fetch('CC', 'clang') +CXX = ENV.fetch('CXX', 'clang++') FUZZER_NO_MAIN_LIB_ENV = 'FUZZER_NO_MAIN_LIB' find_executable(CC) +find_executable(CXX) def get_clang_file_name(file_name) + puts("Searching for #{file_name} using #{CC}") stdout, status = Open3.capture2(CC, '--print-file-name', file_name) + puts("Search command succeeded: #{status.success?}") + puts("Search file exists: #{File.exist?(stdout.strip)}") if status.success? status.success? && File.exist?(stdout.strip) ? stdout.strip : false end def merge_asan_libfuzzer_lib(asan_lib, fuzzer_no_main_lib) + merged_output = 'asan_with_fuzzer.so' + # https://github.com/google/atheris/blob/master/native_extension_fuzzing.md#why-this-is-necessary Tempfile.create do |file| file.write(File.open(asan_lib).read) + puts("Creating ASAN archive at #{file.path}") _, status = Open3.capture2( 'ar', 'd', @@ -32,6 +46,7 @@ def merge_asan_libfuzzer_lib(asan_lib, fuzzer_no_main_lib) exit(1) end + puts("Merging ASAN at #{file.path} and libFuzzer at #{fuzzer_no_main_lib} to #{merged_output}") _, status = Open3.capture2( CXX, '-Wl,--whole-archive', @@ -42,7 +57,7 @@ def merge_asan_libfuzzer_lib(asan_lib, fuzzer_no_main_lib) '-ldl', '-shared', '-o', - 'asan_with_fuzzer.so' + merged_output ) unless status.success? puts("The 'clang' shared object merging command failed.") @@ -68,11 +83,6 @@ def merge_asan_libfuzzer_lib(asan_lib, fuzzer_no_main_lib) end end -# The LOCAL_LIBS variable allows linking arbitrary libraries into Ruby C -# extensions. It is supported by the Ruby mkmf library and C extension Makefile. -# For more information, see https://github.com/ruby/ruby/blob/master/lib/mkmf.rb. -$LOCAL_LIBS = fuzzer_no_main_lib - asan_libs = [ 'libclang_rt.asan.a', 'libclang_rt.asan-aarch64.a', @@ -87,4 +97,9 @@ def merge_asan_libfuzzer_lib(asan_lib, fuzzer_no_main_lib) merge_asan_libfuzzer_lib(asan_lib, fuzzer_no_main_lib) +# The LOCAL_LIBS variable allows linking arbitrary libraries into Ruby C +# extensions. It is supported by the Ruby mkmf library and C extension Makefile. +# For more information, see https://github.com/ruby/ruby/blob/master/lib/mkmf.rb. +$LOCAL_LIBS = fuzzer_no_main_lib + create_makefile('cruzzy/cruzzy') diff --git a/test/test_ruzzy.rb b/test/test_ruzzy.rb index 5c96bdc..12ef9a2 100644 --- a/test/test_ruzzy.rb +++ b/test/test_ruzzy.rb @@ -47,6 +47,22 @@ def test_fuzz_without_proc end end + def test_fuzz_without_args + dummy_test_one_input = ->(data) { Ruzzy.dummy_test_one_input(data) } + + assert_raise(RuntimeError) do + Ruzzy.fuzz(dummy_test_one_input, []) + end + end + + def test_fuzz_with_too_many_args + dummy_test_one_input = ->(data) { Ruzzy.dummy_test_one_input(data) } + + assert_raise(RuntimeError) do + Ruzzy.fuzz(dummy_test_one_input, Array.new(128, 'test')) + end + end + def test_ext_path assert(Ruzzy.ext_path) end