diff --git a/src/fuzzing/include/fuzzing/fuzzer.hpp b/src/fuzzing/include/fuzzing/fuzzer.hpp index 9916389b..3db126f7 100644 --- a/src/fuzzing/include/fuzzing/fuzzer.hpp +++ b/src/fuzzing/include/fuzzing/fuzzer.hpp @@ -125,6 +125,7 @@ struct fuzzer final branching_node* get_best(std::unordered_map& targets, natural_32_bit max_input_width); std::unordered_set loop_heads; // Priority #1 (the highest) + // bool means whether the loop heads were collected std::unordered_map sensitive; // Priority #2 std::unordered_map untouched; // Priority #3 std::unordered_map > iid_twins; // Priority #4 @@ -197,7 +198,7 @@ struct fuzzer final { branching_node* entry; branching_node* exit; - branching_node* successor; + // branching_node* successor; }; struct iid_pivot_props @@ -310,7 +311,7 @@ struct fuzzer final primary_coverage_target_branchings primary_coverage_targets; std::unordered_map iid_pivots; - std::unordered_set coverage_failures_with_hope; + std::unordered_set coverage_failures_with_hope; // EXPLAIN STATE state; sensitivity_analysis sensitivity; diff --git a/src/fuzzing/include/fuzzing/progress_recorder.hpp b/src/fuzzing/include/fuzzing/progress_recorder.hpp index c777b6e6..b9bc9aba 100644 --- a/src/fuzzing/include/fuzzing/progress_recorder.hpp +++ b/src/fuzzing/include/fuzzing/progress_recorder.hpp @@ -10,6 +10,8 @@ # include # include # include +# include +# include namespace fuzzing { @@ -71,15 +73,50 @@ struct progress_recorder void on_trace_mapped_to_tree(branching_node* leaf_); void on_execution_results_available(); - void on_strategy_turn_primary_loop_head(); - void on_strategy_turn_primary_sensitive(); - void on_strategy_turn_primary_untouched(); - void on_strategy_turn_primary_iid_twins(); - void on_strategy_turn_monte_carlo(); - void on_strategy_turn_monte_carlo_backward(); + void on_strategy_turn_primary_loop_head(branching_node* const node); + void on_strategy_turn_primary_sensitive(branching_node* const node); + void on_strategy_turn_primary_untouched(branching_node* const node); + void on_strategy_turn_primary_iid_twins(branching_node* const node); + void on_strategy_turn_monte_carlo(branching_node* const node); + void on_strategy_turn_monte_carlo_backward(branching_node* const node); void on_post_node_closed(branching_node* node); void flush_post_data(); + enum SELECTION_REASON + { + UNKNOWN = 0, + + SENSITIVITY_PRIORITY_START = 1, + UNTOUCHED_PRIORITY_START = 2, + + PRIORITY_STEP_NO_SENSITIVITY_PERFORMED = 3, + PRIORITY_STEP_SENSITIVITY_BITS_SIZE = 4, + PRIORITY_STEP_DISTANCE_TO_WIDTH = 5, + PRIORITY_STEP_STDIN_SIZE = 6, + PRIORITY_STEP_TRACE_INDEX = 7, + PRIORITY_STEP_SUCCESOR_TRACE_INDEX = 8, + + BEST_LOOP_HEAD = 9, + BEST_SENSITIVE = 10, + BEST_UNTOUCHED = 11, + BEST_IID_TWINS = 12, + + NO_SENSITIVITY_IN_INNER_NODE = 13, + + START_MONTE_CARLO = 14, + MONTE_CARLO_STEP = 15, + BEST_MONTE_CARLO = 16, + + START_MONTE_CARLO_BACKWARD = 17, + MONTE_CARLO_STEP_BACKWARD = 18, + BEST_MONTE_CARLO_BACKWARD = 19, + + STARTUP = 20, + }; + + void flush_node_choosing_data(); + void on_node_chosen(branching_node* node, const SELECTION_REASON reason = UNKNOWN); + private: enum ANALYSIS @@ -185,6 +222,21 @@ struct progress_recorder std::unordered_set closed_node_guids; }; + struct node_chossing_data + { + node_chossing_data(); + + void on_node_chosen(branching_node* node, const SELECTION_REASON reason); + + void set_output_dir(std::filesystem::path const& dir); + void clear(); + bool empty() const; + void save() const; + + std::filesystem::path output_dir; + std::vector> node_guids; + }; + progress_recorder(); progress_recorder(progress_recorder const&) = delete; @@ -216,6 +268,7 @@ struct progress_recorder branching_node* leaf; post_analysis_data post_data; + node_chossing_data node_choosing_data; }; diff --git a/src/fuzzing/src/fuzzer.cpp b/src/fuzzing/src/fuzzer.cpp index 52364eaf..02ba7ce1 100644 --- a/src/fuzzing/src/fuzzer.cpp +++ b/src/fuzzing/src/fuzzer.cpp @@ -5,6 +5,8 @@ #include #include #include +#include +#include namespace fuzzing { @@ -165,7 +167,7 @@ branching_node* fuzzer::primary_coverage_target_branchings::get_best(natural_32 { best_node = *loop_heads.begin(); ++statistics->strategy_primary_loop_head; - recorder().on_strategy_turn_primary_loop_head(); + recorder().on_strategy_turn_primary_loop_head(best_node); return best_node; } @@ -175,7 +177,7 @@ branching_node* fuzzer::primary_coverage_target_branchings::get_best(natural_32 if (!loop_heads.empty()) return get_best(max_input_width); ++statistics->strategy_primary_sensitive; - recorder().on_strategy_turn_primary_sensitive(); + recorder().on_strategy_turn_primary_sensitive(best_node); return best_node; } @@ -184,8 +186,9 @@ branching_node* fuzzer::primary_coverage_target_branchings::get_best(natural_32 { if (!loop_heads.empty()) return get_best(max_input_width); + ++statistics->strategy_primary_untouched; - recorder().on_strategy_turn_primary_untouched(); + recorder().on_strategy_turn_primary_untouched(best_node); return best_node; } @@ -200,7 +203,7 @@ branching_node* fuzzer::primary_coverage_target_branchings::get_best(natural_32 return get_best(max_input_width); } ++statistics->strategy_primary_iid_twins; - recorder().on_strategy_turn_primary_iid_twins(); + recorder().on_strategy_turn_primary_iid_twins(it->second.first); return it->second.first; } @@ -229,26 +232,52 @@ branching_node* fuzzer::primary_coverage_target_branchings::get_best( bool operator<(branching_node_with_less_than const& other) const { if (node->sensitivity_performed && !other.node->sensitivity_performed) + { + recorder().on_node_chosen(node, fuzzing::progress_recorder::PRIORITY_STEP_NO_SENSITIVITY_PERFORMED); return true; + } if (!node->sensitivity_performed && other.node->sensitivity_performed) return false; + if (node->sensitive_stdin_bits.size() < other.node->sensitive_stdin_bits.size()) + { + recorder().on_node_chosen(node, fuzzing::progress_recorder::PRIORITY_STEP_SENSITIVITY_BITS_SIZE); return true; + } if (node->sensitive_stdin_bits.size() > other.node->sensitive_stdin_bits.size()) return false; + if (distance_to_central_input_width_class < other.distance_to_central_input_width_class) + { + recorder().on_node_chosen(node, fuzzing::progress_recorder::PRIORITY_STEP_DISTANCE_TO_WIDTH); return true; + } if (distance_to_central_input_width_class > other.distance_to_central_input_width_class) return false; + if (node->get_num_stdin_bytes() < other.node->get_num_stdin_bytes()) + { + recorder().on_node_chosen(node, fuzzing::progress_recorder::PRIORITY_STEP_STDIN_SIZE); return true; + } if (node->get_num_stdin_bytes() > other.node->get_num_stdin_bytes()) return false; + if (node->trace_index < other.node->trace_index) + { + recorder().on_node_chosen(node, fuzzing::progress_recorder::PRIORITY_STEP_TRACE_INDEX); return true; + } if (node->trace_index > other.node->trace_index) return false; - return node->max_successors_trace_index > other.node->max_successors_trace_index; + + if (node->max_successors_trace_index > other.node->max_successors_trace_index) + { + recorder().on_node_chosen(node, fuzzing::progress_recorder::PRIORITY_STEP_SUCCESOR_TRACE_INDEX); + return true; + } + return false; + // return node->max_successors_trace_index > other.node->max_successors_trace_index; } private: branching_node* node; @@ -256,6 +285,13 @@ branching_node* fuzzer::primary_coverage_target_branchings::get_best( }; branching_node_with_less_than best{ targets.begin()->first, max_input_width }; + + if (targets == sensitive) + recorder().on_node_chosen(targets.begin()->first, fuzzing::progress_recorder::SENSITIVITY_PRIORITY_START); + + if (targets == untouched) + recorder().on_node_chosen(targets.begin()->first, fuzzing::progress_recorder::UNTOUCHED_PRIORITY_START); + for (auto it = std::next(targets.begin()); it != targets.end(); ++it) { branching_node_with_less_than const current{ it->first, max_input_width }; @@ -382,7 +418,7 @@ void fuzzer::detect_loops_along_path_to_node( struct loop_exit_props { branching_node* exit; - branching_node* successor; + // branching_node* successor; natural_32_bit index; }; @@ -399,7 +435,7 @@ void fuzzer::detect_loops_along_path_to_node( if (it == pointers_to_branching_stack.end()) { pointers_to_branching_stack.insert({ node->get_location_id(), (natural_32_bit)branching_stack.size() }); - branching_stack.push_back({ node, succ_node, 0U }); + branching_stack.push_back({ node, 0U }); } else { @@ -410,7 +446,7 @@ void fuzzer::detect_loops_along_path_to_node( if (props.index == 0U) { props.index = (natural_32_bit)loops->size(); - loops->push_back({ node, props.exit, props.successor }); + loops->push_back({ node, props.exit }); } else loops->at(props.index).entry = node; @@ -452,11 +488,11 @@ void fuzzer::compute_loop_boundaries( loop_boundaries.push_back(props.entry); stored.insert(props.entry); } - if (!stored.contains(props.successor)) - { - loop_boundaries.push_back(props.successor); - stored.insert(props.successor); - } + // if (!stored.contains(props.successor)) + // { + // loop_boundaries.push_back(props.successor); + // stored.insert(props.successor); + // } } std::sort( loop_boundaries.begin(), @@ -671,6 +707,7 @@ branching_node* fuzzer::monte_carlo_search( if (successor == nullptr) break; pivot = successor; + recorder().on_node_chosen(pivot, fuzzing::progress_recorder::MONTE_CARLO_STEP); } INVARIANT(pivot != nullptr && pivot->is_open_branching()); @@ -893,7 +930,7 @@ void fuzzer::generate_next_input(vecb& stdin_bits) { case STARTUP: if (get_performed_driver_executions() == 0U) - return; + return; // Fizzer input is empty break; case SENSITIVITY: @@ -924,6 +961,7 @@ void fuzzer::generate_next_input(vecb& stdin_bits) default: { UNREACHABLE(); break; } } + recorder().flush_node_choosing_data(); do_cleanup(); select_next_state(); @@ -1451,6 +1489,7 @@ void fuzzer::select_next_state() winner = right; else break; + recorder().on_node_chosen(winner, fuzzing::progress_recorder::NO_SENSITIVITY_IN_INNER_NODE); } sensitivity.start(winner, num_driver_executions); state = SENSITIVITY; @@ -1535,7 +1574,7 @@ branching_node* fuzzer::select_iid_coverage_target() const else winner = node_and_direction.first; - recorder().on_strategy_turn_monte_carlo_backward(); + recorder().on_strategy_turn_monte_carlo_backward(winner); } else { @@ -1546,10 +1585,11 @@ branching_node* fuzzer::select_iid_coverage_target() const entry_branching ); + recorder().on_node_chosen(start_node, fuzzing::progress_recorder::START_MONTE_CARLO); winner = monte_carlo_search(start_node, histogram, generators, *random_uniform_generator); ++statistics.strategy_monte_carlo; - recorder().on_strategy_turn_monte_carlo(); + recorder().on_strategy_turn_monte_carlo(winner); } INVARIANT(winner != nullptr); diff --git a/src/fuzzing/src/progress_recorder.cpp b/src/fuzzing/src/progress_recorder.cpp index 0be10b0c..4d25b55e 100644 --- a/src/fuzzing/src/progress_recorder.cpp +++ b/src/fuzzing/src/progress_recorder.cpp @@ -62,6 +62,7 @@ progress_recorder::progress_recorder() , leaf{ nullptr } , post_data{} + , node_choosing_data{} {} @@ -100,6 +101,8 @@ void progress_recorder::start(std::filesystem::path const& path_to_client_, st leaf = nullptr; post_data.clear(); + node_choosing_data.clear(); + node_choosing_data.node_guids.push_back({1, SELECTION_REASON::STARTUP}); } @@ -141,6 +144,7 @@ void progress_recorder::stop() leaf = nullptr; post_data.clear(); + node_choosing_data.clear(); } @@ -397,11 +401,13 @@ void progress_recorder::on_analysis_start(ANALYSIS const a, analysis_common_in info.analysis_dir = output_dir / (std::to_string(counter_analysis) + '_' + analysis_name(analysis)); post_data.set_output_dir(info.analysis_dir); + node_choosing_data.set_output_dir(info.analysis_dir); } void progress_recorder::on_analysis_stop() { + if (!is_started() || analysis == NONE) return; @@ -472,6 +478,9 @@ std::unique_ptr progress_recorder::save_default_execution_result if (post_data.output_dir.empty()) post_data.output_dir = record_dir; + + if (node_choosing_data.output_dir.empty()) + node_choosing_data.output_dir = record_dir; std::ofstream& ostr{ *ostr_ptr }; @@ -711,51 +720,57 @@ void progress_recorder::bitshare_progress_info::save_info(std::ostream& ostr) } -void progress_recorder::on_strategy_turn_primary_loop_head() +void progress_recorder::on_strategy_turn_primary_loop_head(branching_node* const node) { if (!is_started()) return; post_data.on_strategy_changed(post_analysis_data::PRIMARY_LOOP_HEAD); + on_node_chosen(node, SELECTION_REASON::BEST_LOOP_HEAD); } -void progress_recorder::on_strategy_turn_primary_sensitive() +void progress_recorder::on_strategy_turn_primary_sensitive(branching_node* const node) { if (!is_started()) return; post_data.on_strategy_changed(post_analysis_data::PRIMARY_SENSITIVE); + on_node_chosen(node, SELECTION_REASON::BEST_SENSITIVE); } -void progress_recorder::on_strategy_turn_primary_untouched() +void progress_recorder::on_strategy_turn_primary_untouched(branching_node* const node) { if (!is_started()) return; post_data.on_strategy_changed(post_analysis_data::PRIMARY_UNTOUCHED); + on_node_chosen(node, SELECTION_REASON::BEST_UNTOUCHED); } -void progress_recorder::on_strategy_turn_primary_iid_twins() +void progress_recorder::on_strategy_turn_primary_iid_twins(branching_node* const node) { if (!is_started()) return; post_data.on_strategy_changed(post_analysis_data::PRIMARY_IID_TWINS); + on_node_chosen(node, SELECTION_REASON::BEST_IID_TWINS); } -void progress_recorder::on_strategy_turn_monte_carlo() +void progress_recorder::on_strategy_turn_monte_carlo(branching_node* const node) { if (!is_started()) return; post_data.on_strategy_changed(post_analysis_data::MONTE_CARLO); + on_node_chosen(node, SELECTION_REASON::BEST_MONTE_CARLO); } -void progress_recorder::on_strategy_turn_monte_carlo_backward() +void progress_recorder::on_strategy_turn_monte_carlo_backward(branching_node* const node) { if (!is_started()) return; post_data.on_strategy_changed(post_analysis_data::MONTE_CARLO_BACKWARD); + on_node_chosen(node, SELECTION_REASON::BEST_MONTE_CARLO_BACKWARD); } @@ -777,6 +792,27 @@ void progress_recorder::flush_post_data() } +void progress_recorder::flush_node_choosing_data() +{ + if (!is_started()) + return; + + if (!node_choosing_data.empty() && std::filesystem::is_directory(node_choosing_data.output_dir)) + node_choosing_data.save(); + + node_choosing_data.clear(); +} + + +void progress_recorder::on_node_chosen(branching_node* const node, const SELECTION_REASON reason) +{ + if (!is_started() || node == nullptr) + return; + + node_choosing_data.on_node_chosen(node, reason); +} + + progress_recorder::post_analysis_data::post_analysis_data() : output_dir{} , strategy{ NONE } @@ -850,4 +886,83 @@ void progress_recorder::post_analysis_data::save() const } +progress_recorder::node_chossing_data::node_chossing_data() + : output_dir{} + , node_guids{} +{ +} + + +void progress_recorder::node_chossing_data::on_node_chosen(branching_node* const node, const SELECTION_REASON reason) +{ + int node_guid = node == nullptr ? 0 : node->guid(); + node_guids.push_back({node_guid, reason}); +} + + +void progress_recorder::node_chossing_data::set_output_dir(std::filesystem::path const& dir) +{ + output_dir = dir; +} + + +void progress_recorder::node_chossing_data::clear() +{ + output_dir.clear(); + node_guids.clear(); +} + + +bool progress_recorder::node_chossing_data::empty() const +{ + return node_guids.empty(); +} + + +void progress_recorder::node_chossing_data::save() const +{ + TMPROF_BLOCK(); + + std::filesystem::path const record_pathname = output_dir / "node_choosing.json"; + std::ofstream ostr{ record_pathname.c_str(), std::ios::binary }; + if (!ostr.is_open()) + throw std::runtime_error("Cannot open file for writing: " + record_pathname.string()); + ostr << "{\n"; + + ostr << "\"node_guids\": [\n"; + for (auto it = node_guids.begin(); it != node_guids.end(); ++it) + { + ostr << "{\"guid\": " << std::get<0>(*it) << ", \"reason\": \""; + switch (std::get<1>(*it)) + { + case SELECTION_REASON::UNKNOWN: ostr << "UNKNOWN"; break; + case SELECTION_REASON::SENSITIVITY_PRIORITY_START: ostr << "SENSITIVITY_PRIORITY_START"; break; + case SELECTION_REASON::UNTOUCHED_PRIORITY_START: ostr << "UNTOUCHED_PRIORITY_START"; break; + case SELECTION_REASON::PRIORITY_STEP_NO_SENSITIVITY_PERFORMED: ostr << "PRIORITY_STEP_NO_SENSITIVITY_PERFORMED"; break; + case SELECTION_REASON::PRIORITY_STEP_SENSITIVITY_BITS_SIZE: ostr << "PRIORITY_STEP_SENSITIVITY_BITS_SIZE"; break; + case SELECTION_REASON::PRIORITY_STEP_DISTANCE_TO_WIDTH: ostr << "PRIORITY_STEP_DISTANCE_TO_WIDTH"; break; + case SELECTION_REASON::PRIORITY_STEP_STDIN_SIZE: ostr << "PRIORITY_STEP_STDIN_SIZE"; break; + case SELECTION_REASON::PRIORITY_STEP_TRACE_INDEX: ostr << "PRIORITY_STEP_TRACE_INDEX"; break; + case SELECTION_REASON::PRIORITY_STEP_SUCCESOR_TRACE_INDEX: ostr << "PRIORITY_STEP_SUCCESOR_TRACE_INDEX"; break; + case SELECTION_REASON::BEST_LOOP_HEAD: ostr << "BEST_LOOP_HEAD"; break; + case SELECTION_REASON::BEST_SENSITIVE: ostr << "BEST_SENSITIVE"; break; + case SELECTION_REASON::BEST_UNTOUCHED: ostr << "BEST_UNTOUCHED"; break; + case SELECTION_REASON::BEST_IID_TWINS: ostr << "BEST_IID_TWINS"; break; + case SELECTION_REASON::NO_SENSITIVITY_IN_INNER_NODE : ostr << "NO_SENSITIVITY_IN_INNER_NODE"; break; + case SELECTION_REASON::START_MONTE_CARLO: ostr << "START_MONTE_CARLO"; break; + case SELECTION_REASON::MONTE_CARLO_STEP: ostr << "MONTE_CARLO_STEP"; break; + case SELECTION_REASON::BEST_MONTE_CARLO: ostr << "BEST_MONTE_CARLO"; break; + case SELECTION_REASON::START_MONTE_CARLO_BACKWARD: ostr << "START_MONTE_CARLO_BACKWARD"; break; + case SELECTION_REASON::MONTE_CARLO_STEP_BACKWARD: ostr << "MONTE_CARLO_STEP_BACKWARD"; break; + case SELECTION_REASON::BEST_MONTE_CARLO_BACKWARD: ostr << "BEST_MONTE_CARLO_BACKWARD"; break; + case SELECTION_REASON::STARTUP: ostr << "STARTUP"; break; + default: UNREACHABLE(); break; + } + ostr << "\"}"; + if (std::next(it) != node_guids.end()) ostr << ",\n"; + } + ostr << "]\n"; + ostr << "}\n"; +} + }