Bump rBPF to v0.1.24, update rBPF/BPF Loader error handling (#9089)
This commit is contained in:
		
							
								
								
									
										7
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										7
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @@ -3740,7 +3740,7 @@ dependencies = [ | |||||||
|  "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", |  "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "solana-logger 1.1.0", |  "solana-logger 1.1.0", | ||||||
|  "solana-sdk 1.1.0", |  "solana-sdk 1.1.0", | ||||||
|  "solana_rbpf 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", |  "solana_rbpf 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "thiserror 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", |  "thiserror 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
| ] | ] | ||||||
|  |  | ||||||
| @@ -5078,7 +5078,7 @@ dependencies = [ | |||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "solana_rbpf" | name = "solana_rbpf" | ||||||
| version = "0.1.23" | version = "0.1.24" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", |  "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
| @@ -5088,6 +5088,7 @@ dependencies = [ | |||||||
|  "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", |  "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", |  "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", |  "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  |  "thiserror 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", |  "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
| ] | ] | ||||||
|  |  | ||||||
| @@ -6648,7 +6649,7 @@ dependencies = [ | |||||||
| "checksum solana_libra_vm_cache_map 0.0.1-sol4 (registry+https://github.com/rust-lang/crates.io-index)" = "37fa2e1f00a87514cd2169149a5f81a89279703b2523979688d6ef84081a4690" | "checksum solana_libra_vm_cache_map 0.0.1-sol4 (registry+https://github.com/rust-lang/crates.io-index)" = "37fa2e1f00a87514cd2169149a5f81a89279703b2523979688d6ef84081a4690" | ||||||
| "checksum solana_libra_vm_runtime 0.0.1-sol4 (registry+https://github.com/rust-lang/crates.io-index)" = "ff9f8a7b8212dc4ece5d93f2839896e633c34d7463856e4a555cbcb5c67e9c26" | "checksum solana_libra_vm_runtime 0.0.1-sol4 (registry+https://github.com/rust-lang/crates.io-index)" = "ff9f8a7b8212dc4ece5d93f2839896e633c34d7463856e4a555cbcb5c67e9c26" | ||||||
| "checksum solana_libra_vm_runtime_types 0.0.1-sol4 (registry+https://github.com/rust-lang/crates.io-index)" = "254c23c8f30e7c82ae4dc6694e743400d674c66d371b700eec03378ba994f00b" | "checksum solana_libra_vm_runtime_types 0.0.1-sol4 (registry+https://github.com/rust-lang/crates.io-index)" = "254c23c8f30e7c82ae4dc6694e743400d674c66d371b700eec03378ba994f00b" | ||||||
| "checksum solana_rbpf 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)" = "832c41d1f4184a9554c41aa29dbeb0d5f8873d72cf2be32411668bc5f047a150" | "checksum solana_rbpf 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)" = "3e73fe3cf1da0881709bf32e9a22d4894ce5fa317c9a6284c2b5d8c25be000b8" | ||||||
| "checksum sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4bf77cb82ba8453b42b6ae1d692e4cdc92f9a47beaf89a847c8be83f4e328ad3" | "checksum sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4bf77cb82ba8453b42b6ae1d692e4cdc92f9a47beaf89a847c8be83f4e328ad3" | ||||||
| "checksum spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" | "checksum spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" | ||||||
| "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" | "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" | ||||||
|   | |||||||
							
								
								
									
										9
									
								
								programs/bpf/Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										9
									
								
								programs/bpf/Cargo.lock
									
									
									
										generated
									
									
									
								
							| @@ -1736,7 +1736,7 @@ dependencies = [ | |||||||
|  "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", |  "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "solana-logger 1.1.0", |  "solana-logger 1.1.0", | ||||||
|  "solana-sdk 1.1.0", |  "solana-sdk 1.1.0", | ||||||
|  "solana_rbpf 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", |  "solana_rbpf 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "thiserror 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", |  "thiserror 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
| ] | ] | ||||||
|  |  | ||||||
| @@ -1751,7 +1751,7 @@ dependencies = [ | |||||||
|  "solana-logger 1.1.0", |  "solana-logger 1.1.0", | ||||||
|  "solana-runtime 1.1.0", |  "solana-runtime 1.1.0", | ||||||
|  "solana-sdk 1.1.0", |  "solana-sdk 1.1.0", | ||||||
|  "solana_rbpf 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", |  "solana_rbpf 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)", |  "walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
| ] | ] | ||||||
|  |  | ||||||
| @@ -2091,7 +2091,7 @@ dependencies = [ | |||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "solana_rbpf" | name = "solana_rbpf" | ||||||
| version = "0.1.23" | version = "0.1.24" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", |  "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
| @@ -2101,6 +2101,7 @@ dependencies = [ | |||||||
|  "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", |  "libc 0.2.68 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", |  "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", |  "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  |  "thiserror 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
|  "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", |  "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", | ||||||
| ] | ] | ||||||
|  |  | ||||||
| @@ -2962,7 +2963,7 @@ dependencies = [ | |||||||
| "checksum sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "27044adfd2e1f077f649f59deb9490d3941d674002f7d062870a60ebe9bd47a0" | "checksum sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "27044adfd2e1f077f649f59deb9490d3941d674002f7d062870a60ebe9bd47a0" | ||||||
| "checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" | "checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" | ||||||
| "checksum smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7" | "checksum smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7" | ||||||
| "checksum solana_rbpf 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)" = "832c41d1f4184a9554c41aa29dbeb0d5f8873d72cf2be32411668bc5f047a150" | "checksum solana_rbpf 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)" = "3e73fe3cf1da0881709bf32e9a22d4894ce5fa317c9a6284c2b5d8c25be000b8" | ||||||
| "checksum sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4bf77cb82ba8453b42b6ae1d692e4cdc92f9a47beaf89a847c8be83f4e328ad3" | "checksum sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4bf77cb82ba8453b42b6ae1d692e4cdc92f9a47beaf89a847c8be83f4e328ad3" | ||||||
| "checksum spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" | "checksum spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" | ||||||
| "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" | "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" | ||||||
|   | |||||||
| @@ -26,7 +26,7 @@ solana-bpf-loader-program = { path = "../bpf_loader", version = "1.1.0" } | |||||||
| solana-logger = { path = "../../logger", version = "1.1.0" } | solana-logger = { path = "../../logger", version = "1.1.0" } | ||||||
| solana-runtime = { path = "../../runtime", version = "1.1.0" } | solana-runtime = { path = "../../runtime", version = "1.1.0" } | ||||||
| solana-sdk = { path = "../../sdk", version = "1.1.0" } | solana-sdk = { path = "../../sdk", version = "1.1.0" } | ||||||
| solana_rbpf = "=0.1.23" | solana_rbpf = "=0.1.24" | ||||||
|  |  | ||||||
| [[bench]] | [[bench]] | ||||||
| name = "bpf_loader" | name = "bpf_loader" | ||||||
|   | |||||||
| @@ -3,8 +3,8 @@ | |||||||
| extern crate test; | extern crate test; | ||||||
|  |  | ||||||
| use byteorder::{ByteOrder, LittleEndian, WriteBytesExt}; | use byteorder::{ByteOrder, LittleEndian, WriteBytesExt}; | ||||||
| use solana_rbpf::EbpfVm; | use solana_rbpf::{EbpfVm}; | ||||||
| use std::{env, fs::File, io::Error, io::Read, mem, path::PathBuf}; | use std::{env, fs::File, io::Read, mem, path::PathBuf}; | ||||||
| use test::Bencher; | use test::Bencher; | ||||||
|  |  | ||||||
| /// BPF program file extension | /// BPF program file extension | ||||||
| @@ -21,7 +21,7 @@ fn create_bpf_path(name: &str) -> PathBuf { | |||||||
|     pathbuf |     pathbuf | ||||||
| } | } | ||||||
|  |  | ||||||
| fn empty_check(_prog: &[u8]) -> Result<(), Error> { | fn empty_check(_prog: &[u8]) -> Result<(), solana_bpf_loader_program::BPFError> { | ||||||
|     Ok(()) |     Ok(()) | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -39,7 +39,7 @@ const ARMSTRONG_EXPECTED: u64 = 5; | |||||||
| #[bench] | #[bench] | ||||||
| fn bench_program_load_elf(bencher: &mut Bencher) { | fn bench_program_load_elf(bencher: &mut Bencher) { | ||||||
|     let elf = load_elf().unwrap(); |     let elf = load_elf().unwrap(); | ||||||
|     let mut vm = EbpfVm::new(None).unwrap(); |     let mut vm = EbpfVm::<solana_bpf_loader_program::BPFError>::new(None).unwrap(); | ||||||
|     vm.set_verifier(empty_check).unwrap(); |     vm.set_verifier(empty_check).unwrap(); | ||||||
|  |  | ||||||
|     bencher.iter(|| { |     bencher.iter(|| { | ||||||
| @@ -50,7 +50,7 @@ fn bench_program_load_elf(bencher: &mut Bencher) { | |||||||
| #[bench] | #[bench] | ||||||
| fn bench_program_verify(bencher: &mut Bencher) { | fn bench_program_verify(bencher: &mut Bencher) { | ||||||
|     let elf = load_elf().unwrap(); |     let elf = load_elf().unwrap(); | ||||||
|     let mut vm = EbpfVm::new(None).unwrap(); |     let mut vm = EbpfVm::<solana_bpf_loader_program::BPFError>::new(None).unwrap(); | ||||||
|     vm.set_verifier(empty_check).unwrap(); |     vm.set_verifier(empty_check).unwrap(); | ||||||
|     vm.set_elf(&elf).unwrap(); |     vm.set_elf(&elf).unwrap(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -17,7 +17,7 @@ num-derive = { version = "0.3" } | |||||||
| num-traits = { version = "0.2" } | num-traits = { version = "0.2" } | ||||||
| solana-logger = { path = "../../logger", version = "1.1.0" } | solana-logger = { path = "../../logger", version = "1.1.0" } | ||||||
| solana-sdk = { path = "../../sdk", version = "1.1.0" } | solana-sdk = { path = "../../sdk", version = "1.1.0" } | ||||||
| solana_rbpf = "=0.1.23" | solana_rbpf = "=0.1.24" | ||||||
| thiserror = "1.0" | thiserror = "1.0" | ||||||
|  |  | ||||||
| [lib] | [lib] | ||||||
|   | |||||||
| @@ -1,111 +1,108 @@ | |||||||
|  | use crate::BPFError; | ||||||
| use solana_rbpf::ebpf; | use solana_rbpf::ebpf; | ||||||
| use std::io::{Error, ErrorKind}; | use thiserror::Error; | ||||||
|  |  | ||||||
| fn reject<S: AsRef<str>>(msg: S) -> Result<(), Error> { | #[derive(Debug, Error)] | ||||||
|     let full_msg = format!("[Verifier] Error: {}", msg.as_ref()); | pub enum VerifierError { | ||||||
|     Err(Error::new(ErrorKind::Other, full_msg)) |     #[error("program length must be a multiple of {} octets", ebpf::INSN_SIZE)] | ||||||
|  |     ProgramLengthNotMultiple, | ||||||
|  |     #[error("program too big, max {}, is {}", ebpf::PROG_MAX_INSNS, .0)] | ||||||
|  |     ProgramTooLarge(usize), | ||||||
|  |     #[error("no program set, call prog_set() to load one")] | ||||||
|  |     NoProgram, | ||||||
|  |     #[error("division by 0 (insn #{0})")] | ||||||
|  |     DivisionByZero(usize), | ||||||
|  |     #[error("unsupported argument for LE/BE (insn #{0})")] | ||||||
|  |     UnsupportedLEBEArgument(usize), | ||||||
|  |     #[error("LD_DW instruction cannot be last in program")] | ||||||
|  |     LDDWCannotBeLast, | ||||||
|  |     #[error("incomplete LD_DW instruction (insn #{0})")] | ||||||
|  |     IncompleteLDDW(usize), | ||||||
|  |     #[error("infinite loop (insn #{0})")] | ||||||
|  |     InfiniteLoop(usize), | ||||||
|  |     #[error("jump out of code to #{0} (insn #{1})")] | ||||||
|  |     JumpOutOfCode(usize, usize), | ||||||
|  |     #[error("jump to middle of LD_DW at #{0} (insn #{1})")] | ||||||
|  |     JumpToMiddleOfLDDW(usize, usize), | ||||||
|  |     #[error("invalid source register (insn #{0})")] | ||||||
|  |     InvalidSourceRegister(usize), | ||||||
|  |     #[error("cannot write into register r10 (insn #{0})")] | ||||||
|  |     CannotWriteR10(usize), | ||||||
|  |     #[error("invalid destination register (insn #{0})")] | ||||||
|  |     InvalidDestinationRegister(usize), | ||||||
|  |     #[error("unknown eBPF opcode {0:#2x} (insn #{1:?})")] | ||||||
|  |     UnknownOpCode(u8, usize), | ||||||
| } | } | ||||||
|  |  | ||||||
| fn check_prog_len(prog: &[u8]) -> Result<(), Error> { | fn check_prog_len(prog: &[u8]) -> Result<(), BPFError> { | ||||||
|     if prog.len() % ebpf::INSN_SIZE != 0 { |     if prog.len() % ebpf::INSN_SIZE != 0 { | ||||||
|         reject(format!( |         return Err(VerifierError::ProgramLengthNotMultiple.into()); | ||||||
|             "eBPF program length must be a multiple of {:?} octets", |  | ||||||
|             ebpf::INSN_SIZE |  | ||||||
|         ))?; |  | ||||||
|     } |     } | ||||||
|     if prog.len() > ebpf::PROG_MAX_SIZE { |     if prog.len() > ebpf::PROG_MAX_SIZE { | ||||||
|         reject(format!( |         return Err(VerifierError::ProgramTooLarge(prog.len() / ebpf::INSN_SIZE).into()); | ||||||
|             "eBPF program length limited to {:?}, here {:?}", |  | ||||||
|             ebpf::PROG_MAX_INSNS, |  | ||||||
|             prog.len() / ebpf::INSN_SIZE |  | ||||||
|         ))?; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if prog.is_empty() { |     if prog.is_empty() { | ||||||
|         reject("No program set, call prog_set() to load one".to_string())?; |         return Err(VerifierError::NoProgram.into()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     Ok(()) |     Ok(()) | ||||||
| } | } | ||||||
|  |  | ||||||
| fn check_imm_nonzero(insn: &ebpf::Insn, insn_ptr: usize) -> Result<(), Error> { | fn check_imm_nonzero(insn: &ebpf::Insn, insn_ptr: usize) -> Result<(), BPFError> { | ||||||
|     if insn.imm == 0 { |     if insn.imm == 0 { | ||||||
|         reject(format!("division by 0 (insn #{:?})", insn_ptr))?; |         return Err(VerifierError::DivisionByZero(insn_ptr).into()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     Ok(()) |     Ok(()) | ||||||
| } | } | ||||||
|  |  | ||||||
| fn check_imm_endian(insn: &ebpf::Insn, insn_ptr: usize) -> Result<(), Error> { | fn check_imm_endian(insn: &ebpf::Insn, insn_ptr: usize) -> Result<(), BPFError> { | ||||||
|     match insn.imm { |     match insn.imm { | ||||||
|         16 | 32 | 64 => Ok(()), |         16 | 32 | 64 => Ok(()), | ||||||
|         _ => reject(format!( |         _ => Err(VerifierError::UnsupportedLEBEArgument(insn_ptr).into()), | ||||||
|             "unsupported argument for LE/BE (insn #{:?})", |  | ||||||
|             insn_ptr |  | ||||||
|         )), |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| fn check_load_dw(prog: &[u8], insn_ptr: usize) -> Result<(), Error> { | fn check_load_dw(prog: &[u8], insn_ptr: usize) -> Result<(), BPFError> { | ||||||
|     if insn_ptr >= (prog.len() / ebpf::INSN_SIZE) { |     if insn_ptr >= (prog.len() / ebpf::INSN_SIZE) { | ||||||
|         // Last instruction cannot be LD_DW because there would be no 2nd DW |         // Last instruction cannot be LD_DW because there would be no 2nd DW | ||||||
|         reject("LD_DW instruction cannot be last in program".to_string())?; |         return Err(VerifierError::LDDWCannotBeLast.into()); | ||||||
|     } |     } | ||||||
|     let next_insn = ebpf::get_insn(prog, insn_ptr + 1); |     let next_insn = ebpf::get_insn(prog, insn_ptr + 1); | ||||||
|     if next_insn.opc != 0 { |     if next_insn.opc != 0 { | ||||||
|         reject(format!( |         return Err(VerifierError::IncompleteLDDW(insn_ptr).into()); | ||||||
|             "incomplete LD_DW instruction (insn #{:?})", |  | ||||||
|             insn_ptr |  | ||||||
|         ))?; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     Ok(()) |     Ok(()) | ||||||
| } | } | ||||||
|  |  | ||||||
| fn check_jmp_offset(prog: &[u8], insn_ptr: usize) -> Result<(), Error> { | fn check_jmp_offset(prog: &[u8], insn_ptr: usize) -> Result<(), BPFError> { | ||||||
|     let insn = ebpf::get_insn(prog, insn_ptr); |     let insn = ebpf::get_insn(prog, insn_ptr); | ||||||
|     if insn.off == -1 { |     if insn.off == -1 { | ||||||
|         reject(format!("infinite loop (insn #{:?})", insn_ptr))?; |         return Err(VerifierError::InfiniteLoop(insn_ptr).into()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     let dst_insn_ptr = insn_ptr as isize + 1 + insn.off as isize; |     let dst_insn_ptr = insn_ptr as isize + 1 + insn.off as isize; | ||||||
|     if dst_insn_ptr < 0 || dst_insn_ptr as usize >= (prog.len() / ebpf::INSN_SIZE) { |     if dst_insn_ptr < 0 || dst_insn_ptr as usize >= (prog.len() / ebpf::INSN_SIZE) { | ||||||
|         reject(format!( |         return Err(VerifierError::JumpOutOfCode(dst_insn_ptr as usize, insn_ptr).into()); | ||||||
|             "jump out of code to #{:?} (insn #{:?})", |  | ||||||
|             dst_insn_ptr, insn_ptr |  | ||||||
|         ))?; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     let dst_insn = ebpf::get_insn(prog, dst_insn_ptr as usize); |     let dst_insn = ebpf::get_insn(prog, dst_insn_ptr as usize); | ||||||
|     if dst_insn.opc == 0 { |     if dst_insn.opc == 0 { | ||||||
|         reject(format!( |         return Err(VerifierError::JumpToMiddleOfLDDW(dst_insn_ptr as usize, insn_ptr).into()); | ||||||
|             "jump to middle of LD_DW at #{:?} (insn #{:?})", |  | ||||||
|             dst_insn_ptr, insn_ptr |  | ||||||
|         ))?; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     Ok(()) |     Ok(()) | ||||||
| } | } | ||||||
|  |  | ||||||
| fn check_registers(insn: &ebpf::Insn, store: bool, insn_ptr: usize) -> Result<(), Error> { | fn check_registers(insn: &ebpf::Insn, store: bool, insn_ptr: usize) -> Result<(), BPFError> { | ||||||
|     if insn.src > 10 { |     if insn.src > 10 { | ||||||
|         reject(format!("invalid source register (insn #{:?})", insn_ptr))?; |         return Err(VerifierError::InvalidSourceRegister(insn_ptr).into()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     match (insn.dst, store) { |     match (insn.dst, store) { | ||||||
|         (0..=9, _) | (10, true) => Ok(()), |         (0..=9, _) | (10, true) => Ok(()), | ||||||
|         (10, false) => reject(format!( |         (10, false) => Err(VerifierError::CannotWriteR10(insn_ptr).into()), | ||||||
|             "cannot write into register r10 (insn #{:?})", |         (_, _) => Err(VerifierError::InvalidDestinationRegister(insn_ptr).into()), | ||||||
|             insn_ptr |  | ||||||
|         )), |  | ||||||
|         (_, _) => reject(format!( |  | ||||||
|             "invalid destination register (insn #{:?})", |  | ||||||
|             insn_ptr |  | ||||||
|         )), |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| pub fn check(prog: &[u8]) -> Result<(), Error> { | pub fn check(prog: &[u8]) -> Result<(), BPFError> { | ||||||
|     check_prog_len(prog)?; |     check_prog_len(prog)?; | ||||||
|  |  | ||||||
|     let mut insn_ptr: usize = 0; |     let mut insn_ptr: usize = 0; | ||||||
| @@ -297,10 +294,7 @@ pub fn check(prog: &[u8]) -> Result<(), Error> { | |||||||
|             ebpf::EXIT => {} |             ebpf::EXIT => {} | ||||||
|  |  | ||||||
|             _ => { |             _ => { | ||||||
|                 reject(format!( |                 return Err(VerifierError::UnknownOpCode(insn.opc, insn_ptr).into()); | ||||||
|                     "unknown eBPF opcode {:#2x} (insn #{:?})", |  | ||||||
|                     insn.opc, insn_ptr |  | ||||||
|                 ))?; |  | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -311,7 +305,7 @@ pub fn check(prog: &[u8]) -> Result<(), Error> { | |||||||
|  |  | ||||||
|     // insn_ptr should now be equal to number of instructions. |     // insn_ptr should now be equal to number of instructions. | ||||||
|     if insn_ptr != prog.len() / ebpf::INSN_SIZE { |     if insn_ptr != prog.len() / ebpf::INSN_SIZE { | ||||||
|         reject(format!("jumped out of code to #{:?}", insn_ptr))?; |         return Err(VerifierError::JumpOutOfCode(insn_ptr, insn_ptr).into()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     Ok(()) |     Ok(()) | ||||||
|   | |||||||
| @@ -1,18 +1,37 @@ | |||||||
| use crate::alloc; | use crate::{alloc, BPFError}; | ||||||
| use alloc::Alloc; | use alloc::Alloc; | ||||||
| use log::*; | use log::*; | ||||||
| use solana_rbpf::{ | use solana_rbpf::{ | ||||||
|     ebpf::{HelperObject, ELF_INSN_DUMP_OFFSET, MM_HEAP_START}, |     ebpf::{EbpfError, HelperObject, ELF_INSN_DUMP_OFFSET, MM_HEAP_START}, | ||||||
|     memory_region::{translate_addr, MemoryRegion}, |     memory_region::{translate_addr, MemoryRegion}, | ||||||
|     EbpfVm, |     EbpfVm, | ||||||
| }; | }; | ||||||
|  | use solana_sdk::instruction::InstructionError; | ||||||
| use std::{ | use std::{ | ||||||
|     alloc::Layout, |     alloc::Layout, | ||||||
|     io::{Error, ErrorKind}, |  | ||||||
|     mem::{align_of, size_of}, |     mem::{align_of, size_of}, | ||||||
|     slice::from_raw_parts_mut, |     slice::from_raw_parts_mut, | ||||||
|     str::from_utf8, |     str::{from_utf8, Utf8Error}, | ||||||
| }; | }; | ||||||
|  | use thiserror::Error as ThisError; | ||||||
|  |  | ||||||
|  | /// Error definitions | ||||||
|  | #[derive(Debug, ThisError)] | ||||||
|  | pub enum HelperError { | ||||||
|  |     #[error("{0}: {1:?}")] | ||||||
|  |     InvalidString(Utf8Error, Vec<u8>), | ||||||
|  |     #[error("BPF program called abort()!")] | ||||||
|  |     Abort, | ||||||
|  |     #[error("BPF program Panicked at {0}, {1}:{2}")] | ||||||
|  |     Panic(String, u64, u64), | ||||||
|  |     #[error("{0}")] | ||||||
|  |     InstructionError(InstructionError), | ||||||
|  | } | ||||||
|  | impl From<HelperError> for EbpfError<BPFError> { | ||||||
|  |     fn from(error: HelperError) -> Self { | ||||||
|  |         EbpfError::UserError(error.into()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| /// Program heap allocators are intended to allocate/free from a given | /// Program heap allocators are intended to allocate/free from a given | ||||||
| /// chunk of memory.  The specific allocator implementation is | /// chunk of memory.  The specific allocator implementation is | ||||||
| @@ -26,7 +45,9 @@ use crate::allocator_bump::BPFAllocator; | |||||||
| /// are expected to enforce this | /// are expected to enforce this | ||||||
| const DEFAULT_HEAP_SIZE: usize = 32 * 1024; | const DEFAULT_HEAP_SIZE: usize = 32 * 1024; | ||||||
|  |  | ||||||
| pub fn register_helpers(vm: &mut EbpfVm) -> Result<MemoryRegion, Error> { | pub fn register_helpers<'a>( | ||||||
|  |     vm: &mut EbpfVm<'a, BPFError>, | ||||||
|  | ) -> Result<MemoryRegion, EbpfError<BPFError>> { | ||||||
|     vm.register_helper_ex("abort", helper_abort)?; |     vm.register_helper_ex("abort", helper_abort)?; | ||||||
|     vm.register_helper_ex("sol_panic", helper_sol_panic)?; |     vm.register_helper_ex("sol_panic", helper_sol_panic)?; | ||||||
|     vm.register_helper_ex("sol_panic_", helper_sol_panic)?; |     vm.register_helper_ex("sol_panic_", helper_sol_panic)?; | ||||||
| @@ -74,7 +95,6 @@ macro_rules! translate_type_mut { | |||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
|  |  | ||||||
| #[macro_export] | #[macro_export] | ||||||
| macro_rules! translate_type { | macro_rules! translate_type { | ||||||
|     ($t:ty, $vm_addr:expr, $regions:expr) => { |     ($t:ty, $vm_addr:expr, $regions:expr) => { | ||||||
| @@ -95,7 +115,6 @@ macro_rules! translate_slice_mut { | |||||||
|         unsafe { from_raw_parts_mut(host_addr, $len as usize) } |         unsafe { from_raw_parts_mut(host_addr, $len as usize) } | ||||||
|     }}; |     }}; | ||||||
| } | } | ||||||
|  |  | ||||||
| #[macro_export] | #[macro_export] | ||||||
| macro_rules! translate_slice { | macro_rules! translate_slice { | ||||||
|     ($t:ty, $vm_addr:expr, $len: expr, $regions:expr) => { |     ($t:ty, $vm_addr:expr, $len: expr, $regions:expr) => { | ||||||
| @@ -109,8 +128,8 @@ fn translate_string_and_do( | |||||||
|     addr: u64, |     addr: u64, | ||||||
|     len: u64, |     len: u64, | ||||||
|     regions: &[MemoryRegion], |     regions: &[MemoryRegion], | ||||||
|     work: &dyn Fn(&str) -> Result<u64, Error>, |     work: &dyn Fn(&str) -> Result<u64, EbpfError<BPFError>>, | ||||||
| ) -> Result<u64, Error> { | ) -> Result<u64, EbpfError<BPFError>> { | ||||||
|     let buf = translate_slice!(u8, addr, len, regions); |     let buf = translate_slice!(u8, addr, len, regions); | ||||||
|     let i = match buf.iter().position(|byte| *byte == 0) { |     let i = match buf.iter().position(|byte| *byte == 0) { | ||||||
|         Some(i) => i, |         Some(i) => i, | ||||||
| @@ -118,10 +137,7 @@ fn translate_string_and_do( | |||||||
|     }; |     }; | ||||||
|     match from_utf8(&buf[..i]) { |     match from_utf8(&buf[..i]) { | ||||||
|         Ok(message) => work(message), |         Ok(message) => work(message), | ||||||
|         Err(err) => Err(Error::new( |         Err(err) => Err(HelperError::InvalidString(err, buf[..i].to_vec()).into()), | ||||||
|             ErrorKind::Other, |  | ||||||
|             format!("Error: Invalid string {:?}", err), |  | ||||||
|         )), |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -136,11 +152,8 @@ pub fn helper_abort( | |||||||
|     _arg5: u64, |     _arg5: u64, | ||||||
|     _ro_regions: &[MemoryRegion], |     _ro_regions: &[MemoryRegion], | ||||||
|     _rw_regions: &[MemoryRegion], |     _rw_regions: &[MemoryRegion], | ||||||
| ) -> Result<u64, Error> { | ) -> Result<u64, EbpfError<BPFError>> { | ||||||
|     Err(Error::new( |     Err(HelperError::Abort.into()) | ||||||
|         ErrorKind::Other, |  | ||||||
|         "Error: BPF program called abort()!", |  | ||||||
|     )) |  | ||||||
| } | } | ||||||
|  |  | ||||||
| /// Panic helper functions, called when the BPF program calls 'sol_panic_()` | /// Panic helper functions, called when the BPF program calls 'sol_panic_()` | ||||||
| @@ -154,15 +167,9 @@ pub fn helper_sol_panic( | |||||||
|     _arg5: u64, |     _arg5: u64, | ||||||
|     ro_regions: &[MemoryRegion], |     ro_regions: &[MemoryRegion], | ||||||
|     _rw_regions: &[MemoryRegion], |     _rw_regions: &[MemoryRegion], | ||||||
| ) -> Result<u64, Error> { | ) -> Result<u64, EbpfError<BPFError>> { | ||||||
|     translate_string_and_do(file, len, ro_regions, &|string: &str| { |     translate_string_and_do(file, len, ro_regions, &|string: &str| { | ||||||
|         Err(Error::new( |         Err(HelperError::Panic(string.to_string(), line, column).into()) | ||||||
|             ErrorKind::Other, |  | ||||||
|             format!( |  | ||||||
|                 "Error: BPF program Panicked at {}, {}:{}", |  | ||||||
|                 string, line, column |  | ||||||
|             ), |  | ||||||
|         )) |  | ||||||
|     }) |     }) | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -175,7 +182,7 @@ pub fn helper_sol_log( | |||||||
|     _arg5: u64, |     _arg5: u64, | ||||||
|     ro_regions: &[MemoryRegion], |     ro_regions: &[MemoryRegion], | ||||||
|     _rw_regions: &[MemoryRegion], |     _rw_regions: &[MemoryRegion], | ||||||
| ) -> Result<u64, Error> { | ) -> Result<u64, EbpfError<BPFError>> { | ||||||
|     if log_enabled!(log::Level::Info) { |     if log_enabled!(log::Level::Info) { | ||||||
|         translate_string_and_do(addr, len, ro_regions, &|string: &str| { |         translate_string_and_do(addr, len, ro_regions, &|string: &str| { | ||||||
|             info!("info!: {}", string); |             info!("info!: {}", string); | ||||||
| @@ -194,7 +201,7 @@ pub fn helper_sol_log_u64( | |||||||
|     arg5: u64, |     arg5: u64, | ||||||
|     _ro_regions: &[MemoryRegion], |     _ro_regions: &[MemoryRegion], | ||||||
|     _rw_regions: &[MemoryRegion], |     _rw_regions: &[MemoryRegion], | ||||||
| ) -> Result<u64, Error> { | ) -> Result<u64, EbpfError<BPFError>> { | ||||||
|     if log_enabled!(log::Level::Info) { |     if log_enabled!(log::Level::Info) { | ||||||
|         info!( |         info!( | ||||||
|             "info!: {:#x}, {:#x}, {:#x}, {:#x}, {:#x}", |             "info!: {:#x}, {:#x}, {:#x}, {:#x}, {:#x}", | ||||||
| @@ -213,7 +220,7 @@ pub fn helper_sol_log_u64( | |||||||
| pub struct HelperSolAllocFree { | pub struct HelperSolAllocFree { | ||||||
|     allocator: BPFAllocator, |     allocator: BPFAllocator, | ||||||
| } | } | ||||||
| impl HelperObject for HelperSolAllocFree { | impl HelperObject<BPFError> for HelperSolAllocFree { | ||||||
|     fn call( |     fn call( | ||||||
|         &mut self, |         &mut self, | ||||||
|         size: u64, |         size: u64, | ||||||
| @@ -223,7 +230,7 @@ impl HelperObject for HelperSolAllocFree { | |||||||
|         _arg5: u64, |         _arg5: u64, | ||||||
|         _ro_regions: &[MemoryRegion], |         _ro_regions: &[MemoryRegion], | ||||||
|         _rw_regions: &[MemoryRegion], |         _rw_regions: &[MemoryRegion], | ||||||
|     ) -> Result<u64, Error> { |     ) -> Result<u64, EbpfError<BPFError>> { | ||||||
|         let layout = Layout::from_size_align(size as usize, align_of::<u8>()).unwrap(); |         let layout = Layout::from_size_align(size as usize, align_of::<u8>()).unwrap(); | ||||||
|         if free_addr == 0 { |         if free_addr == 0 { | ||||||
|             match self.allocator.alloc(layout) { |             match self.allocator.alloc(layout) { | ||||||
|   | |||||||
| @@ -3,10 +3,15 @@ pub mod allocator_bump; | |||||||
| pub mod bpf_verifier; | pub mod bpf_verifier; | ||||||
| pub mod helpers; | pub mod helpers; | ||||||
|  |  | ||||||
|  | use crate::{bpf_verifier::VerifierError, helpers::HelperError}; | ||||||
| use byteorder::{ByteOrder, LittleEndian, WriteBytesExt}; | use byteorder::{ByteOrder, LittleEndian, WriteBytesExt}; | ||||||
| use log::*; | use log::*; | ||||||
| use num_derive::{FromPrimitive, ToPrimitive}; | use num_derive::{FromPrimitive, ToPrimitive}; | ||||||
| use solana_rbpf::{memory_region::MemoryRegion, EbpfVm}; | use solana_rbpf::{ | ||||||
|  |     ebpf::{EbpfError, UserDefinedError}, | ||||||
|  |     memory_region::MemoryRegion, | ||||||
|  |     EbpfVm, | ||||||
|  | }; | ||||||
| use solana_sdk::{ | use solana_sdk::{ | ||||||
|     account::KeyedAccount, |     account::KeyedAccount, | ||||||
|     bpf_loader, |     bpf_loader, | ||||||
| @@ -18,10 +23,7 @@ use solana_sdk::{ | |||||||
|     pubkey::Pubkey, |     pubkey::Pubkey, | ||||||
|     sysvar::rent, |     sysvar::rent, | ||||||
| }; | }; | ||||||
| use std::{ | use std::{io::prelude::*, mem}; | ||||||
|     io::{prelude::*, Error}, |  | ||||||
|     mem, |  | ||||||
| }; |  | ||||||
| use thiserror::Error; | use thiserror::Error; | ||||||
|  |  | ||||||
| solana_sdk::declare_program!( | solana_sdk::declare_program!( | ||||||
| @@ -32,19 +34,28 @@ solana_sdk::declare_program!( | |||||||
|  |  | ||||||
| #[derive(Error, Debug, Clone, PartialEq, FromPrimitive, ToPrimitive)] | #[derive(Error, Debug, Clone, PartialEq, FromPrimitive, ToPrimitive)] | ||||||
| pub enum BPFLoaderError { | pub enum BPFLoaderError { | ||||||
|     #[error("Failed to create virtual machine")] |     #[error("failed to create virtual machine")] | ||||||
|     VirtualMachineCreationFailed, |     VirtualMachineCreationFailed = 0x0b9f_0001, | ||||||
|     #[error("Virtual machine failed to run the program to completion")] |     #[error("virtual machine failed to run the program to completion")] | ||||||
|     VirtualMachineFailedToRunProgram, |     VirtualMachineFailedToRunProgram = 0x0b9f_0002, | ||||||
| } | } | ||||||
|  |  | ||||||
| impl<E> DecodeError<E> for BPFLoaderError { | impl<E> DecodeError<E> for BPFLoaderError { | ||||||
|     fn type_of() -> &'static str { |     fn type_of() -> &'static str { | ||||||
|         "BPFLoaderError" |         "BPFLoaderError" | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| pub fn create_vm(prog: &[u8]) -> Result<(EbpfVm, MemoryRegion), Error> { | /// Errors returned by functions the BPF Loader registers with the vM | ||||||
|  | #[derive(Debug, Error)] | ||||||
|  | pub enum BPFError { | ||||||
|  |     #[error("{0}")] | ||||||
|  |     VerifierError(#[from] VerifierError), | ||||||
|  |     #[error("{0}")] | ||||||
|  |     HelperError(#[from] HelperError), | ||||||
|  | } | ||||||
|  | impl UserDefinedError for BPFError {} | ||||||
|  |  | ||||||
|  | pub fn create_vm(prog: &[u8]) -> Result<(EbpfVm<BPFError>, MemoryRegion), EbpfError<BPFError>> { | ||||||
|     let mut vm = EbpfVm::new(None)?; |     let mut vm = EbpfVm::new(None)?; | ||||||
|     vm.set_verifier(bpf_verifier::check)?; |     vm.set_verifier(bpf_verifier::check)?; | ||||||
|     vm.set_max_instruction_count(100_000)?; |     vm.set_max_instruction_count(100_000)?; | ||||||
| @@ -55,7 +66,7 @@ pub fn create_vm(prog: &[u8]) -> Result<(EbpfVm, MemoryRegion), Error> { | |||||||
|     Ok((vm, heap_region)) |     Ok((vm, heap_region)) | ||||||
| } | } | ||||||
|  |  | ||||||
| pub fn check_elf(prog: &[u8]) -> Result<(), Error> { | pub fn check_elf(prog: &[u8]) -> Result<(), EbpfError<BPFError>> { | ||||||
|     let mut vm = EbpfVm::new(None)?; |     let mut vm = EbpfVm::new(None)?; | ||||||
|     vm.set_verifier(bpf_verifier::check)?; |     vm.set_verifier(bpf_verifier::check)?; | ||||||
|     vm.set_elf(&prog)?; |     vm.set_elf(&prog)?; | ||||||
| @@ -180,13 +191,18 @@ pub fn process_instruction( | |||||||
|                 Ok(status) => { |                 Ok(status) => { | ||||||
|                     if status != SUCCESS { |                     if status != SUCCESS { | ||||||
|                         let error: InstructionError = status.into(); |                         let error: InstructionError = status.into(); | ||||||
|                         warn!("BPF program failed: {:?}", error); |                         warn!("BPF program failed: {}", error); | ||||||
|                         return Err(error); |                         return Err(error); | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|                 Err(e) => { |                 Err(error) => { | ||||||
|                     warn!("BPF VM failed to run program: {}", e); |                     warn!("BPF program failed: {}", error); | ||||||
|                     return Err(BPFLoaderError::VirtualMachineFailedToRunProgram.into()); |                     return match error { | ||||||
|  |                         EbpfError::UserError(BPFError::HelperError( | ||||||
|  |                             HelperError::InstructionError(error), | ||||||
|  |                         )) => Err(error), | ||||||
|  |                         _ => Err(BPFLoaderError::VirtualMachineFailedToRunProgram.into()), | ||||||
|  |                     }; | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @@ -221,7 +237,7 @@ pub fn process_instruction( | |||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 if let Err(e) = check_elf(&program.try_account_ref()?.data) { |                 if let Err(e) = check_elf(&program.try_account_ref()?.data) { | ||||||
|                     warn!("Invalid ELF: {}", e); |                     warn!("{}", e); | ||||||
|                     return Err(InstructionError::InvalidAccountData); |                     return Err(InstructionError::InvalidAccountData); | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
| @@ -242,7 +258,7 @@ mod tests { | |||||||
|     use std::{cell::RefCell, fs::File, io::Read}; |     use std::{cell::RefCell, fs::File, io::Read}; | ||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     #[should_panic(expected = "Error: Exceeded maximum number of instructions allowed")] |     #[should_panic(expected = "ExceededMaxInstructions(10)")] | ||||||
|     fn test_bpf_loader_non_terminating_program() { |     fn test_bpf_loader_non_terminating_program() { | ||||||
|         #[rustfmt::skip] |         #[rustfmt::skip] | ||||||
|         let program = &[ |         let program = &[ | ||||||
| @@ -252,7 +268,7 @@ mod tests { | |||||||
|         ]; |         ]; | ||||||
|         let input = &mut [0x00]; |         let input = &mut [0x00]; | ||||||
|  |  | ||||||
|         let mut vm = EbpfVm::new(None).unwrap(); |         let mut vm = EbpfVm::<BPFError>::new(None).unwrap(); | ||||||
|         vm.set_verifier(bpf_verifier::check).unwrap(); |         vm.set_verifier(bpf_verifier::check).unwrap(); | ||||||
|         vm.set_max_instruction_count(10).unwrap(); |         vm.set_max_instruction_count(10).unwrap(); | ||||||
|         vm.set_program(program).unwrap(); |         vm.set_program(program).unwrap(); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user