Clip OOB values in call data load and copy (#448)

The OOB logic is implemented in the runtime but truncation circumvented
it.

Closes #263

---------

Signed-off-by: xermicus <cyrill@parity.io>
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
This commit is contained in:
xermicus authored and GitHub committed 2026-01-13 10:15:31 +01:00
1 parent c1ce984ca2
commit a1a4f8e6ea
5 files changed
+68 -12

No files matched your search

@@ -1054,6 +1054,28 @@ impl<'ctx> Context<'ctx> {
.into_int_value())
}
/// Clip a memory offset to the maximum value that fits into a register.
pub fn clip_to_xlen(
&self,
value: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::IntValue<'ctx>> {
let clipped = self.xlen_type().const_all_ones();
let is_overflow = self.builder().build_int_compare(
inkwell::IntPredicate::UGT,
value,
self.builder()
.build_int_z_extend(clipped, self.word_type(), "value_clipped")?,
"is_value_overflow",
)?;
let truncated =
self.builder()
.build_int_truncate(value, self.xlen_type(), "value_truncated")?;
Ok(self
.builder()
.build_select(is_overflow, clipped, truncated, "value")?
.into_int_value())
}
/// Build a call to PolkaVM `sbrk` for extending the heap from offset by `size`.
/// The allocation is aligned to 32 bytes.
///
@@ -8,7 +8,7 @@ pub fn load<'ctx>(
offset: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
let output_pointer = context.build_alloca_at_entry(context.word_type(), "call_data_output");
let offset = context.safe_truncate_int_to_xlen(offset)?;
let offset = context.clip_to_xlen(offset)?;
context.build_runtime_call(
revive_runtime_api::polkavm_imports::CALL_DATA_LOAD,
@@ -40,9 +40,9 @@ pub fn copy<'ctx>(
source_offset: inkwell::values::IntValue<'ctx>,
size: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<()> {
let source_offset = context.safe_truncate_int_to_xlen(source_offset)?;
let size = context.safe_truncate_int_to_xlen(size)?;
let destination_offset = context.safe_truncate_int_to_xlen(destination_offset)?;
let source_offset = context.clip_to_xlen(source_offset)?;
let size = context.clip_to_xlen(size)?;
let destination_offset = context.clip_to_xlen(destination_offset)?;
let output_pointer = context.build_heap_gep(destination_offset, size)?;
context.build_runtime_call(