Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Various places where panic might happen in case of malicious data #78

Merged
merged 2 commits into from
Jul 20, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/bits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
pub fn roundup(x: u64, round: u64) -> u64 {
debug_assert!(round.is_power_of_two());
// x + (((!x) + 1) & (round - 1))
x + ((!x).wrapping_add(1) & (round.wrapping_sub(1)))
x.wrapping_add((!x).wrapping_add(1) & (round.wrapping_sub(1)))
}

#[inline(always)]
Expand All @@ -25,6 +25,7 @@ mod tests {
assert_eq!(16, roundup(16, 16));
assert_eq!(32, roundup(17, 16));
assert_eq!(u64::max_value() - 15, roundup(u64::max_value() - 15, 16));
assert_eq!(0, roundup(u64::max_value(), 16));
}

#[test]
Expand Down
16 changes: 9 additions & 7 deletions src/instructions/rvc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -308,15 +308,17 @@ pub fn factory<R: Register>(instruction_bits: u32) -> Option<Instruction> {
// Invalid instruction
(0b_00_000_00000_00, 0) => None,
// SRLI
(0b_00_000_00000_00, uimm) => {
Some(Itype::new(insts::OP_RVC_SRLI, rd, rd, uimm).0)
}
(0b_00_000_00000_00, uimm) => Some(
Itype::new(insts::OP_RVC_SRLI, rd, rd, uimm & u32::from(R::SHIFT_MASK))
.0,
),
// Invalid instruction
(0b_01_000_00000_00, 0) => None,
// SRAI
(0b_01_000_00000_00, uimm) => {
Some(Itype::new(insts::OP_RVC_SRAI, rd, rd, uimm).0)
}
(0b_01_000_00000_00, uimm) => Some(
Itype::new(insts::OP_RVC_SRAI, rd, rd, uimm & u32::from(R::SHIFT_MASK))
.0,
),
// ANDI
(0b_10_000_00000_00, _) => Some(
Itype::new_s(insts::OP_RVC_ANDI, rd, rd, immediate(instruction_bits)).0,
Expand Down Expand Up @@ -355,7 +357,7 @@ pub fn factory<R: Register>(instruction_bits: u32) -> Option<Instruction> {
// Reserved
None
} else if uimm != 0 {
Some(Itype::new(insts::OP_RVC_SLLI, rd, rd, uimm).0)
Some(Itype::new(insts::OP_RVC_SLLI, rd, rd, uimm & u32::from(R::SHIFT_MASK)).0)
} else {
Some(blank_instruction(insts::OP_RVC_SLLI64))
}
Expand Down
8 changes: 5 additions & 3 deletions src/machine/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,12 @@ pub trait SupportMachine: CoreMachine {
for program_header in &elf.program_headers {
if program_header.p_type == PT_LOAD {
let aligned_start = round_page_down(program_header.p_vaddr);
let padding_start = program_header.p_vaddr - aligned_start;
let size = round_page_up(program_header.p_memsz + padding_start);
let padding_start = program_header.p_vaddr.wrapping_sub(aligned_start);
let size = round_page_up(program_header.p_memsz.wrapping_add(padding_start));
let slice_start = program_header.p_offset;
let slice_end = program_header.p_offset + program_header.p_filesz;
let slice_end = program_header
.p_offset
.wrapping_add(program_header.p_filesz);
if slice_start > slice_end || slice_end > program.len() as u64 {
return Err(Error::OutOfBound);
}
Expand Down
7 changes: 6 additions & 1 deletion src/memory/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,12 @@ pub fn check_permission<R: Register>(
size: u64,
flag: u8,
) -> Result<(), Error> {
let e = addr + size;
// fetch_flag below will check if requested memory is within bound. Here
// we only need to test for overflow first
let (e, overflowed) = addr.overflowing_add(size);
if overflowed {
return Err(Error::OutOfBound);
}
let mut current_addr = round_page_down(addr);
while current_addr < e {
let page = current_addr / RISCV_PAGESIZE as u64;
Expand Down
Binary file added tests/programs/load_elf_crash_64
Binary file not shown.
Binary file added tests/programs/op_rvc_slli_crash_32
Binary file not shown.
Binary file added tests/programs/op_rvc_srai_crash_32
Binary file not shown.
Binary file added tests/programs/op_rvc_srli_crash_32
Binary file not shown.
Binary file added tests/programs/wxorx_crash_64
Binary file not shown.
34 changes: 34 additions & 0 deletions tests/test_aot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,3 +233,37 @@ pub fn test_aot_invalid_read64() {
assert!(result.is_err());
assert_eq!(result.err(), Some(Error::OutOfBound));
}

#[test]
pub fn test_aot_load_elf_crash_64() {
let mut file = File::open("tests/programs/load_elf_crash_64").unwrap();
let mut buffer = Vec::new();
file.read_to_end(&mut buffer).unwrap();
let buffer: Bytes = buffer.into();

let mut aot_machine = AotCompilingMachine::load(&buffer.clone(), None).unwrap();
let code = aot_machine.compile().unwrap();
let mut machine = AsmMachine::default_with_aot_code(&code);
machine
.load_program(&buffer, &vec!["load_elf_crash_64".into()])
.unwrap();
let result = machine.run();
assert_eq!(result.err(), Some(Error::InvalidPermission));
}

#[test]
pub fn test_aot_wxorx_crash_64() {
let mut file = File::open("tests/programs/wxorx_crash_64").unwrap();
let mut buffer = Vec::new();
file.read_to_end(&mut buffer).unwrap();
let buffer: Bytes = buffer.into();

let mut aot_machine = AotCompilingMachine::load(&buffer.clone(), None).unwrap();
let code = aot_machine.compile().unwrap();
let mut machine = AsmMachine::default_with_aot_code(&code);
machine
.load_program(&buffer, &vec!["wxorx_crash_64".into()])
.unwrap();
let result = machine.run();
assert_eq!(result.err(), Some(Error::OutOfBound));
}
30 changes: 30 additions & 0 deletions tests/test_asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,3 +208,33 @@ pub fn test_invalid_read64() {
assert!(result.is_err());
assert_eq!(result.err(), Some(Error::OutOfBound));
}

#[test]
pub fn test_asm_load_elf_crash_64() {
let mut file = File::open("tests/programs/load_elf_crash_64").unwrap();
let mut buffer = Vec::new();
file.read_to_end(&mut buffer).unwrap();
let buffer: Bytes = buffer.into();

let mut machine = AsmMachine::default();
machine
.load_program(&buffer, &vec!["load_elf_crash_64".into()])
.unwrap();
let result = machine.run();
assert_eq!(result.err(), Some(Error::InvalidPermission));
}

#[test]
pub fn test_asm_wxorx_crash_64() {
let mut file = File::open("tests/programs/wxorx_crash_64").unwrap();
let mut buffer = Vec::new();
file.read_to_end(&mut buffer).unwrap();
let buffer: Bytes = buffer.into();

let mut machine = AsmMachine::default();
machine
.load_program(&buffer, &vec!["wxorx_crash_64".into()])
.unwrap();
let result = machine.run();
assert_eq!(result.err(), Some(Error::OutOfBound));
}
55 changes: 55 additions & 0 deletions tests/test_misc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,58 @@ pub fn test_invalid_file_offset64() {
let result = run::<u64, SparseMemory<u64>>(&buffer, &vec!["invalid_file_offset64".into()]);
assert_eq!(result.err(), Some(Error::OutOfBound));
}

#[test]
pub fn test_op_rvc_srli_crash_32() {
let mut file = File::open("tests/programs/op_rvc_srli_crash_32").unwrap();
let mut buffer = Vec::new();
file.read_to_end(&mut buffer).unwrap();
let buffer: Bytes = buffer.into();

let result = run::<u32, SparseMemory<u32>>(&buffer, &vec!["op_rvc_srli_crash_32".into()]);
assert_eq!(result.err(), Some(Error::InvalidPermission));
}

#[test]
pub fn test_op_rvc_srai_crash_32() {
let mut file = File::open("tests/programs/op_rvc_srai_crash_32").unwrap();
let mut buffer = Vec::new();
file.read_to_end(&mut buffer).unwrap();
let buffer: Bytes = buffer.into();

let result = run::<u32, SparseMemory<u32>>(&buffer, &vec!["op_rvc_srai_crash_32".into()]);
assert!(result.is_ok());
}

#[test]
pub fn test_op_rvc_slli_crash_32() {
let mut file = File::open("tests/programs/op_rvc_slli_crash_32").unwrap();
let mut buffer = Vec::new();
file.read_to_end(&mut buffer).unwrap();
let buffer: Bytes = buffer.into();

let result = run::<u32, SparseMemory<u32>>(&buffer, &vec!["op_rvc_slli_crash_32".into()]);
assert!(result.is_ok());
}

#[test]
pub fn test_load_elf_crash_64() {
let mut file = File::open("tests/programs/load_elf_crash_64").unwrap();
let mut buffer = Vec::new();
file.read_to_end(&mut buffer).unwrap();
let buffer: Bytes = buffer.into();

let result = run::<u64, SparseMemory<u64>>(&buffer, &vec!["load_elf_crash_64".into()]);
assert_eq!(result.err(), Some(Error::InvalidPermission));
}

#[test]
pub fn test_wxorx_crash_64() {
let mut file = File::open("tests/programs/wxorx_crash_64").unwrap();
let mut buffer = Vec::new();
file.read_to_end(&mut buffer).unwrap();
let buffer: Bytes = buffer.into();

let result = run::<u64, SparseMemory<u64>>(&buffer, &vec!["wxorx_crash_64".into()]);
assert_eq!(result.err(), Some(Error::OutOfBound));
}