Skip to content
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
21 changes: 21 additions & 0 deletions executor/programs/asm/misalign_ld.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
.attribute 5, "rv64i2p1_m2p0"
.globl main
main:
# Misaligned LD: 8-byte load at address 33.
li t0, 32
li t1, 0x80FF1234
sw t1, 0(t0)
li t1, 0xDEADBEEF
sw t1, 4(t0)
li t1, 0x000000AA
sw t1, 8(t0)
ld a0, 1(t0)
li t2, 0xAADEADBEEF80FF12
bne a0, t2, .Lfail
li a0, 0
li a7, 93
ecall
.Lfail:
li a0, 1
li a7, 93
ecall
19 changes: 19 additions & 0 deletions executor/programs/asm/misalign_lh.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.attribute 5, "rv64i2p1_m2p0"
.globl main
main:
# Misaligned LH: 2-byte load at address 35.
li t0, 32
li t1, 0x3412FF80
sw t1, 0(t0)
li t1, 0x000000AA
sw t1, 4(t0)
lh a0, 3(t0)
li t2, 0xFFFFFFFFFFFFAA34
bne a0, t2, .Lfail
li a0, 0
li a7, 93
ecall
.Lfail:
li a0, 1
li a7, 93
ecall
19 changes: 19 additions & 0 deletions executor/programs/asm/misalign_lhu.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.attribute 5, "rv64i2p1_m2p0"
.globl main
main:
# Misaligned LHU: 2-byte load at address 35.
li t0, 32
li t1, 0x3412FF80
sw t1, 0(t0)
li t1, 0x000000AA
sw t1, 4(t0)
lhu a0, 3(t0)
li t2, 0xAA34
bne a0, t2, .Lfail
li a0, 0
li a7, 93
ecall
.Lfail:
li a0, 1
li a7, 93
ecall
19 changes: 19 additions & 0 deletions executor/programs/asm/misalign_lw.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.attribute 5, "rv64i2p1_m2p0"
.globl main
main:
# Misaligned LW: 4-byte load at address 33.
li t0, 32
li t1, 0x80FF1234
sw t1, 0(t0)
li t1, 0x000000AA
sw t1, 4(t0)
lw a0, 1(t0)
li t2, 0xFFFFFFFFAA80FF12
bne a0, t2, .Lfail
li a0, 0
li a7, 93
Comment thread
gabrielbosio marked this conversation as resolved.
ecall
.Lfail:
li a0, 1
li a7, 93
ecall
19 changes: 19 additions & 0 deletions executor/programs/asm/misalign_lwu.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.attribute 5, "rv64i2p1_m2p0"
.globl main
main:
# Misaligned LWU: 4-byte load at address 33.
li t0, 32
li t1, 0x80FF1234
sw t1, 0(t0)
li t1, 0x000000AA
sw t1, 4(t0)
lwu a0, 1(t0)
li t2, 0xAA80FF12
bne a0, t2, .Lfail
li a0, 0
li a7, 93
ecall
.Lfail:
li a0, 1
li a7, 93
ecall
23 changes: 23 additions & 0 deletions executor/programs/asm/misalign_sd.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
.attribute 5, "rv64i2p1_m2p0"
.globl main
main:
# Misaligned SD: 8-byte store at address 65.
li t0, 64
li t1, 0x123456789ABCDEF0
sd t1, 1(t0)
lwu a1, 0(t0)
li t2, 0xBCDEF000
bne a1, t2, .Lfail
lwu a1, 4(t0)
li t2, 0x3456789A
bne a1, t2, .Lfail
lwu a1, 8(t0)
li t2, 0x00000012
bne a1, t2, .Lfail
li a0, 0
li a7, 93
ecall
Comment thread
gabrielbosio marked this conversation as resolved.
.Lfail:
li a0, 1
li a7, 93
ecall
20 changes: 20 additions & 0 deletions executor/programs/asm/misalign_sh.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.attribute 5, "rv64i2p1_m2p0"
.globl main
main:
# Misaligned SH: 2-byte store at address 67.
li t0, 64
li t1, 0xAA12
sh t1, 3(t0)
lwu a1, 0(t0)
li t2, 0x12000000
bne a1, t2, .Lfail
lwu a1, 4(t0)
li t2, 0x000000AA
bne a1, t2, .Lfail
li a0, 0
li a7, 93
ecall
.Lfail:
li a0, 1
li a7, 93
ecall
20 changes: 20 additions & 0 deletions executor/programs/asm/misalign_sw.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.attribute 5, "rv64i2p1_m2p0"
.globl main
main:
# Misaligned SW: 4-byte store at address 65.
li t0, 64
li t1, 0xDEADBEEF
sw t1, 1(t0)
Comment thread
gabrielbosio marked this conversation as resolved.
lwu a1, 0(t0)
li t2, 0xADBEEF00
bne a1, t2, .Lfail
lwu a1, 4(t0)
li t2, 0x000000DE
bne a1, t2, .Lfail
li a0, 0
li a7, 93
ecall
.Lfail:
li a0, 1
li a7, 93
ecall
8 changes: 8 additions & 0 deletions executor/programs/asm/misaligned_pc.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.attribute 5, "rv64i2p1_m2p0"
.globl main
main:
# Jump to PC = 2 (not 4-aligned).
jalr zero, zero, 2
li a0, 0
li a7, 93
ecall
30 changes: 30 additions & 0 deletions executor/src/tests/memory_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,33 @@ fn test_commit_public_output_total_cap() {
let err = memory.commit_public_output(0x1_0000, 1).unwrap_err();
assert!(matches!(err, MemoryError::CommitSizeExceeded));
}

#[test]
fn test_misaligned_load_store_overflow_errors() {
let mut memory = Memory::default();

assert!(matches!(
memory.load_half(u64::MAX).unwrap_err(),
MemoryError::AddressOverflow
));
assert!(matches!(
memory.store_half(u64::MAX, 0).unwrap_err(),
MemoryError::AddressOverflow
));
assert!(matches!(
memory.load_word(u64::MAX - 1).unwrap_err(),
MemoryError::AddressOverflow
));
assert!(matches!(
memory.store_word(u64::MAX - 1, 0).unwrap_err(),
MemoryError::AddressOverflow
));
assert!(matches!(
memory.load_doubleword(u64::MAX - 6).unwrap_err(),
MemoryError::AddressOverflow
));
assert!(matches!(
memory.store_doubleword(u64::MAX - 6, 0).unwrap_err(),
MemoryError::AddressOverflow
));
}
5 changes: 5 additions & 0 deletions executor/src/vm/execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ impl Executor {
self.logs.clear();

while self.pc != 0 && self.logs.len() < CHUNK_SIZE {
if !self.pc.is_multiple_of(4) {
return Err(ExecutorError::InstructionAddressMisaligned(self.pc));
}
let instruction = match self.instructions.get(self.pc) {
Some(&instr) => instr,
None => {
Expand Down Expand Up @@ -252,4 +255,6 @@ pub enum ExecutorError {
ExecutionError(#[from] ExecutionError),
#[error("Memory error: {0}")]
MemoryError(#[from] MemoryError),
#[error("Instruction address misaligned: {0:#018x}")]
InstructionAddressMisaligned(u64),
}
122 changes: 74 additions & 48 deletions executor/src/vm/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,77 +81,105 @@ impl Memory {
}

pub fn load_word(&self, address: u64) -> Result<u32, MemoryError> {
if !address.is_multiple_of(4) {
return Err(MemoryError::UnalignedAccess);
if address.is_multiple_of(4) {
let bytes = self.cells.get(&address).cloned().unwrap_or_default();
Ok(u32::from_le_bytes(bytes))
} else {
address.checked_add(3).ok_or(MemoryError::AddressOverflow)?;
Ok(u32::from_le_bytes([
self.load_byte(address),
self.load_byte(address + 1),
self.load_byte(address + 2),
self.load_byte(address + 3),
]))
}
let bytes = self.cells.get(&address).cloned().unwrap_or_default();
Ok(u32::from_le_bytes(bytes))
}

pub fn store_word(&mut self, address: u64, value: u32) -> Result<(), MemoryError> {
if !address.is_multiple_of(4) {
return Err(MemoryError::UnalignedAccess);
}
let bytes = value.to_le_bytes();
self.cells.insert(address, bytes);
if address.is_multiple_of(4) {
self.cells.insert(address, bytes);
} else {
address.checked_add(3).ok_or(MemoryError::AddressOverflow)?;
for (i, b) in bytes.iter().enumerate() {
self.store_byte(address + i as u64, *b);
}
}
Ok(())
}

/// Load a doubleword (64-bit) from memory - for LD instruction
pub fn load_doubleword(&self, address: u64) -> Result<u64, MemoryError> {
Comment thread
gabrielbosio marked this conversation as resolved.
if !address.is_multiple_of(8) {
return Err(MemoryError::UnalignedAccess);
if address.is_multiple_of(8) {
// 8-alignment bounds `address` to `u64::MAX - 7`, so `address + 4` can't overflow.
let low_bytes = self.cells.get(&address).cloned().unwrap_or_default();
let high_bytes = self.cells.get(&(address + 4)).cloned().unwrap_or_default();
let low = u32::from_le_bytes(low_bytes) as u64;
let high = u32::from_le_bytes(high_bytes) as u64;
Ok(low | (high << 32))
} else {
address.checked_add(7).ok_or(MemoryError::AddressOverflow)?;
let mut bytes = [0u8; 8];
for (i, b) in bytes.iter_mut().enumerate() {
*b = self.load_byte(address + i as u64);
}
Ok(u64::from_le_bytes(bytes))
}
let low_bytes = self.cells.get(&address).cloned().unwrap_or_default();
let high_bytes = self.cells.get(&(address + 4)).cloned().unwrap_or_default();
let low = u32::from_le_bytes(low_bytes) as u64;
let high = u32::from_le_bytes(high_bytes) as u64;
Ok(low | (high << 32))
}

/// Store a doubleword (64-bit) to memory - for SD instruction
pub fn store_doubleword(&mut self, address: u64, value: u64) -> Result<(), MemoryError> {
if !address.is_multiple_of(8) {
return Err(MemoryError::UnalignedAccess);
if address.is_multiple_of(8) {
let low = (value & 0xFFFFFFFF) as u32;
let high = (value >> 32) as u32;
// 8-alignment bounds `address` to `u64::MAX - 7`, so `address + 4` can't overflow.
self.cells.insert(address, low.to_le_bytes());
self.cells.insert(address + 4, high.to_le_bytes());
} else {
address.checked_add(7).ok_or(MemoryError::AddressOverflow)?;
let bytes = value.to_le_bytes();
for (i, b) in bytes.iter().enumerate() {
self.store_byte(address + i as u64, *b);
}
}
let low = (value & 0xFFFFFFFF) as u32;
let high = (value >> 32) as u32;
self.cells.insert(address, low.to_le_bytes());
self.cells.insert(address + 4, high.to_le_bytes());
Ok(())
}

pub fn load_half(&self, address: u64) -> Result<u16, MemoryError> {
if !address.is_multiple_of(2) {
unimplemented!(
"Unaligned load half memory access at address 0x{:016x}",
address
);
if address.is_multiple_of(2) {
let aligned_address = address - address % 4;
let bytes = self
.cells
.get(&aligned_address)
.cloned()
.unwrap_or_default();
let offset = (address % 4) as usize;
Ok(u16::from_le_bytes([bytes[offset], bytes[offset + 1]]))
} else {
address.checked_add(1).ok_or(MemoryError::AddressOverflow)?;
Ok(u16::from_le_bytes([
self.load_byte(address),
self.load_byte(address + 1),
]))
}
let aligned_address = address - address % 4;
let bytes = self
.cells
.get(&aligned_address)
.cloned()
.unwrap_or_default();
let value = &bytes[(address % 4) as usize..(address % 4) as usize + 2];
Ok(u16::from_le_bytes(
value.try_into().map_err(|_| MemoryError::LoadHalf)?,
))
}

pub fn store_half(&mut self, address: u64, value: u16) -> Result<(), MemoryError> {
if !address.is_multiple_of(2) {
return Err(MemoryError::UnalignedAccess);
}
let aligned_address = address - address % 4;
let entry = self
.cells
.entry(aligned_address)
.or_insert_with(|| [0, 0, 0, 0]);
let bytes = value.to_le_bytes();
entry[(address % 4) as usize] = bytes[0];
entry[(address % 4) as usize + 1] = bytes[1];
if address.is_multiple_of(2) {
let aligned_address = address - address % 4;
let entry = self
.cells
.entry(aligned_address)
.or_insert_with(|| [0, 0, 0, 0]);
let offset = (address % 4) as usize;
entry[offset] = bytes[0];
entry[offset + 1] = bytes[1];
} else {
address.checked_add(1).ok_or(MemoryError::AddressOverflow)?;
self.store_byte(address, bytes[0]);
self.store_byte(address + 1, bytes[1]);
}
Ok(())
}

Expand Down Expand Up @@ -233,8 +261,6 @@ impl Memory {

#[derive(thiserror::Error, Debug)]
pub enum MemoryError {
#[error("Failed to convert bytes to u16")]
LoadHalf,
#[error("Unaligned memory access")]
UnalignedAccess,
#[error("Public output commit size exceeded")]
Expand Down
Loading
Loading