Skip to content

Commit

Permalink
More fixes
Browse files Browse the repository at this point in the history
Signed-off-by: Alan Jowett <[email protected]>
  • Loading branch information
Alan Jowett committed Oct 18, 2024
1 parent 75c127f commit fe76a8d
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 64 deletions.
134 changes: 72 additions & 62 deletions libfuzzer/libfuzz_harness.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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) {
Expand Down Expand Up @@ -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<ubpf_context_t*>(context);
UNREFERENCED_PARAMETER(stack_start);
UNREFERENCED_PARAMETER(stack_length);
UNREFERENCED_PARAMETER(stack_mask);
ubpf_context_t* ubpf_context = reinterpret_cast<ubpf_context_t*>(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<std::string> 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<uint64_t>(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<int64_t>(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<std::string> 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<uint64_t>(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<int64_t>(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
}
}

/**
Expand Down Expand Up @@ -633,7 +643,7 @@ bool bounds_check(void* context, uint64_t addr, uint64_t size)
return false;
}

return false;
return true;
}

const std::set<std::string> g_error_message_to_ignore{
Expand Down
9 changes: 7 additions & 2 deletions vm/ubpf_vm.c
Original file line number Diff line number Diff line change
Expand Up @@ -493,18 +493,19 @@ 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;
}
break;
// 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;
}
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit fe76a8d

Please sign in to comment.