diff --git a/libfuzzer/libfuzz_harness.cc b/libfuzzer/libfuzz_harness.cc index 7359c0a3b..a0fffb516 100644 --- a/libfuzzer/libfuzz_harness.cc +++ b/libfuzzer/libfuzz_harness.cc @@ -345,8 +345,8 @@ try { // Enable termination checking and pre-invariant storage. options.check_termination = true; - options.assume_assertions = true; options.print_invariants = g_ubpf_fuzzer_options.get("UBPF_FUZZER_PRINT_VERIFIER_REPORT"); + options.print_failures = g_ubpf_fuzzer_options.get("UBPF_FUZZER_PRINT_VERIFIER_REPORT"); #if defined(HAVE_EBPF_VERIFIER_CHECK_CONSTRAINTS_AT_LABEL) options.store_pre_invariants = g_ubpf_fuzzer_options.get("UBPF_FUZZER_CONSTRAINT_CHECK"); #endif @@ -361,7 +361,14 @@ try { // Verify the program. This will return false or throw an exception if the program is invalid. bool result = ebpf_verify_program(error_stream, prog, raw_prog.info, &options, &stats); - g_verifier_report = error_stream.str(); + if (g_ubpf_fuzzer_options.get("UBPF_FUZZER_PRINT_VERIFIER_REPORT")) { + std::cout << "verifier stats:" << std::endl; + std::cout << "total_unreachable: " << stats.total_unreachable << std::endl; + std::cout << "total_warnings: " << stats.total_warnings << std::endl; + std::cout << "max_loop_count: " << stats.max_loop_count << std::endl; + std::cout << "result: " << result << std::endl; + std::cout << error_stream.str() << std::endl; + } return result; } catch (const std::exception& ex) { @@ -507,75 +514,78 @@ ubpf_debug_function( std::cout << std::endl; } + if (g_ubpf_fuzzer_options.get("UBPF_FUZZER_CONSTRAINT_CHECK")) { #if defined(HAVE_EBPF_VERIFIER_CHECK_CONSTRAINTS_AT_LABEL) - ubpf_context_t* ubpf_context = reinterpret_cast(context); - UNREFERENCED_PARAMETER(stack_start); - UNREFERENCED_PARAMETER(stack_length); - UNREFERENCED_PARAMETER(stack_mask); + ubpf_context_t* ubpf_context = reinterpret_cast(context); + UNREFERENCED_PARAMETER(stack_start); + UNREFERENCED_PARAMETER(stack_length); + UNREFERENCED_PARAMETER(stack_mask); - std::string label = std::to_string(program_counter) + ":-1"; - - if (program_counter == 0) { - return; - } + std::string label = std::to_string(program_counter) + ":-1"; - // Build set of string constraints from the register values. - std::set constraints; - for (int i = 0; i < 10; i++) { - if ((register_mask & (1 << i)) == 0) { - continue; + if (program_counter == 0) { + return; } - uint64_t reg = registers[i]; - std::string register_name = "r" + std::to_string(i); - - // Given the register value, classify it as packet, context, stack, or unknown and add the appropriate - // constraint. - address_type_t type = ubpf_classify_address(ubpf_context, reg); - switch (type) { - case address_type_t::Packet: - constraints.insert(register_name + ".type=packet"); - constraints.insert(register_name + ".packet_offset=" + std::to_string(reg - ubpf_context->data)); - constraints.insert( - register_name + ".packet_size=" + std::to_string(ubpf_context->data_end - ubpf_context->data)); - break; - - case address_type_t::Context: - constraints.insert(register_name + ".type=ctx"); - constraints.insert( - register_name + ".ctx_offset=" + std::to_string(reg - reinterpret_cast(ubpf_context))); - break; - - case address_type_t::Stack: - constraints.insert(register_name + ".type=stack"); - constraints.insert(register_name + ".stack_offset=" + std::to_string(reg - ubpf_context->stack_start)); - break; - - case address_type_t::Unknown: - constraints.insert("r" + std::to_string(i) + ".uvalue=" + std::to_string(registers[i])); - constraints.insert( - "r" + std::to_string(i) + ".svalue=" + std::to_string(static_cast(registers[i]))); - break; - case address_type_t::Map: - constraints.insert(register_name + ".type=shared"); - break; + + // Build set of string constraints from the register values. + std::set constraints; + for (int i = 0; i < 10; i++) { + if ((register_mask & (1 << i)) == 0) { + continue; + } + uint64_t reg = registers[i]; + std::string register_name = "r" + std::to_string(i); + + // Given the register value, classify it as packet, context, stack, or unknown and add the appropriate + // constraint. + address_type_t type = ubpf_classify_address(ubpf_context, reg); + switch (type) { + case address_type_t::Packet: + constraints.insert(register_name + ".type=packet"); + constraints.insert(register_name + ".packet_offset=" + std::to_string(reg - ubpf_context->data)); + constraints.insert( + register_name + ".packet_size=" + std::to_string(ubpf_context->data_end - ubpf_context->data)); + break; + + case address_type_t::Context: + constraints.insert(register_name + ".type=ctx"); + constraints.insert( + register_name + ".ctx_offset=" + std::to_string(reg - reinterpret_cast(ubpf_context))); + break; + + case address_type_t::Stack: + constraints.insert(register_name + ".type=stack"); + constraints.insert(register_name + ".stack_offset=" + std::to_string(reg - ubpf_context->stack_start)); + break; + + case address_type_t::Unknown: + constraints.insert("r" + std::to_string(i) + ".uvalue=" + std::to_string(registers[i])); + constraints.insert( + "r" + std::to_string(i) + ".svalue=" + std::to_string(static_cast(registers[i]))); + break; + case address_type_t::Map: + constraints.insert(register_name + ".type=shared"); + break; + } } - } - // Call ebpf_check_constraints_at_label with the set of string constraints at this label. + // Call ebpf_check_constraints_at_label with the set of string constraints at this label. - std::ostringstream os; + std::ostringstream os; - if (!ebpf_check_constraints_at_label(os, label, constraints)) { - std::cerr << "Label: " << label << std::endl; - std::cerr << os.str() << std::endl; - throw std::runtime_error("ebpf_check_constraints_at_label failed"); - } + if (!ebpf_check_constraints_at_label(os, label, constraints)) { + std::cerr << "Label: " << label << std::endl; + std::cerr << os.str() << std::endl; + throw std::runtime_error("ebpf_check_constraints_at_label failed"); + } #else - UNREFERENCED_PARAMETER(context); - UNREFERENCED_PARAMETER(stack_start); - UNREFERENCED_PARAMETER(stack_length); - UNREFERENCED_PARAMETER(stack_mask); + throw std::runtime_error("ebpf_check_constraints_at_label not supported"); + UNREFERENCED_PARAMETER(context); + UNREFERENCED_PARAMETER(stack_start); + UNREFERENCED_PARAMETER(stack_length); + UNREFERENCED_PARAMETER(stack_mask); #endif + } } /** @@ -633,7 +643,7 @@ bool bounds_check(void* context, uint64_t addr, uint64_t size) return false; } - return false; + return true; } const std::set g_error_message_to_ignore{ diff --git a/vm/ubpf_vm.c b/vm/ubpf_vm.c index a83098111..d56954b4b 100644 --- a/vm/ubpf_vm.c +++ b/vm/ubpf_vm.c @@ -493,10 +493,11 @@ ubpf_validate_shadow_register(const struct ubpf_vm* vm, uint32_t pc, uint16_t* s vm->error_printf(stderr, "Error: %d: Source register r%d is not initialized.\n", pc, inst.src); return false; } + destination_register_valid_after_instruction = true; break; // Store indirect instructions require the destination register to be initialized, but has no source register. case EBPF_CLS_ST: - if (!destination_register_valid_before_instruction) { + if (inst.dst != BPF_REG_10 && !destination_register_valid_before_instruction) { vm->error_printf(stderr, "Error: %d: Destination register r%d is not initialized.\n", pc, inst.dst); return false; } @@ -504,7 +505,7 @@ ubpf_validate_shadow_register(const struct ubpf_vm* vm, uint32_t pc, uint16_t* s // Store indirect instructions require both the source and destination registers to be initialized, except for // writes to the stack. case EBPF_CLS_STX: - if (!source_register_valid_before_instruction) { + if (inst.dst != BPF_REG_10 && !source_register_valid_before_instruction) { vm->error_printf(stderr, "Error: %d: Source register r%d is not initialized.\n", pc, inst.src); return false; } @@ -569,6 +570,10 @@ ubpf_validate_shadow_register(const struct ubpf_vm* vm, uint32_t pc, uint16_t* s case EBPF_MODE_JLE: case EBPF_MODE_JSLT: case EBPF_MODE_JSLE: + // If the jump offset is 0, then this is a no-op. + if (inst.offset == 0) { + break; + } if (!destination_register_valid_before_instruction) { vm->error_printf(stderr, "Error: %d: Destination register r%d is not initialized.\n", pc, inst.dst); return false;