// Copyright 2016 6WIND S.A. // // Licensed under the Apache License, Version 2.0 or // the MIT license , at your option. This file may not be // copied, modified, or distributed except according to those terms. // This crate would be needed to load bytecode from a BPF-compiled object file. Since the crate // is not used anywhere else in the library, it is deactivated: we do not want to load and compile // it just for the tests. If you want to use it, do not forget to add the following // dependency to your Cargo.toml file: // // --- // elf = "0.0.10" // --- // // extern crate elf; // use std::path::PathBuf; extern crate byteorder; extern crate libc; extern crate gemachain_rbpf; extern crate test_utils; use gemachain_rbpf::{ fuzz::fuzz, syscalls::{BpfSyscallString, BpfSyscallU64}, user_error::UserError, verifier::check, vm::{Config, EbpfVm, Executable, SyscallObject, SyscallRegistry, TestInstructionMeter}, }; use std::{fs::File, io::Read}; // The following two examples have been compiled from C with the following command: // // ```bash // clang -O2 -emit-llvm -c -o - | llc -march=bpf -filetype=obj -o // ``` // // The C source code was the following: // // ```c // #include // #include // #include // #include // // #define ETH_ALEN 6 // #define ETH_P_IP 0x0008 /* htons(0x0800) */ // #define TCP_HDR_LEN 20 // // #define BLOCKED_TCP_PORT 0x9999 // // struct eth_hdr { // unsigned char h_dest[ETH_ALEN]; // unsigned char h_source[ETH_ALEN]; // unsigned short h_proto; // }; // // #define SEC(NAME) __attribute__((section(NAME), used)) // SEC(".classifier") // int handle_ingress(struct __sk_buff *skb) // { // void *data = (void *)(long)skb->data; // void *data_end = (void *)(long)skb->data_end; // struct eth_hdr *eth = data; // struct iphdr *iph = data + sizeof(*eth); // struct tcphdr *tcp = data + sizeof(*eth) + sizeof(*iph); // // /* single length check */ // if (data + sizeof(*eth) + sizeof(*iph) + sizeof(*tcp) > data_end) // return 0; // if (eth->h_proto != ETH_P_IP) // return 0; // if (iph->protocol != IPPROTO_TCP) // return 0; // if (tcp->source == BLOCKED_TCP_PORT || tcp->dest == BLOCKED_TCP_PORT) // return -1; // return 0; // } // char _license[] SEC(".license") = "GPL"; // ``` // // This program, once compiled, can be injected into Linux kernel, with tc for instance. Sadly, we // need to bring some modifications to the generated bytecode in order to run it: the three // instructions with opcode 0x61 load data from a packet area as 4-byte words, where we need to // load it as 8-bytes double words (0x79). The kernel does the same kind of translation before // running the program, but rbpf does not implement this. // // In addition, the offset at which the pointer to the packet data is stored must be changed: since // we use 8 bytes instead of 4 for the start and end addresses of the data packet, we cannot use // the offsets produced by clang (0x4c and 0x50), the addresses would overlap. Instead we can use, // for example, 0x40 and 0x50. See comments on the bytecode below to see the modifications. // // Once the bytecode has been (manually, in our case) edited, we can load the bytecode directly // from the ELF object file. This is easy to do, but requires the addition of two crates in the // Cargo.toml file (see comments above), so here we use just the hardcoded bytecode instructions // instead. #[test] #[ignore] fn test_fuzz_execute() { let mut file = File::open("tests/elfs/pass_stack_reference.so").expect("file open failed"); let mut elf = Vec::new(); file.read_to_end(&mut elf).unwrap(); println!("mangle the whole file"); fuzz( &elf, 1_000_000_000, 100, 0..elf.len(), 0..255, |bytes: &mut [u8]| { let mut syscall_registry = SyscallRegistry::default(); syscall_registry .register_syscall_by_name::(b"log", BpfSyscallString::call) .unwrap(); syscall_registry .register_syscall_by_name::(b"log_64", BpfSyscallU64::call) .unwrap(); if let Ok(executable) = >::from_elf( bytes, Some(check), Config::default(), syscall_registry, ) { let mut vm = EbpfVm::::new( executable.as_ref(), &mut [], &mut [], ) .unwrap(); vm.bind_syscall_context_object(Box::new(BpfSyscallString {}), None) .unwrap(); vm.bind_syscall_context_object(Box::new(BpfSyscallU64 {}), None) .unwrap(); let _ = vm.execute_program_interpreted(&mut TestInstructionMeter { remaining: 1_000_000, }); } }, ); }