diff --git a/crates/polkavm/src/compiler/amd64.rs b/crates/polkavm/src/compiler/amd64.rs index 644aab3c..10944079 100644 --- a/crates/polkavm/src/compiler/amd64.rs +++ b/crates/polkavm/src/compiler/amd64.rs @@ -1319,7 +1319,6 @@ where )); let count = conv_reg(Reg::A2.into()); - self.asm.push(mov(RegSize::R32, count, count)); match self.gas_metering { None => { diff --git a/crates/polkavm/src/interpreter.rs b/crates/polkavm/src/interpreter.rs index a6d0f1f2..0b742f7e 100644 --- a/crates/polkavm/src/interpreter.rs +++ b/crates/polkavm/src/interpreter.rs @@ -3153,8 +3153,9 @@ define_interpreter! { let mut result = next_instruction; let value = visitor.get32::(Reg::A1); - let mut dst = visitor.get32::(Reg::A0); - let mut count = visitor.get64::(Reg::A2); + let a0 = visitor.get64::(Reg::A0); + let mut dst = cast(a0).truncate_to_u32(); + let mut count = u64::from(visitor.get32::(Reg::A2)); while count > 0 { if gas_metering_enabled && visitor.gas == 0 { result = not_enough_gas_impl::(visitor, compiled_offset, program_counter, 0); @@ -3174,7 +3175,7 @@ define_interpreter! { count -= 1; } - visitor.set64::(Reg::A0, u64::from(dst)); + visitor.set64::(Reg::A0, a0.wrapping_add(u64::from(dst.wrapping_sub(cast(a0).truncate_to_u32())))); visitor.set64::(Reg::A2, count); result diff --git a/crates/polkavm/src/tests.rs b/crates/polkavm/src/tests.rs index 27c476c9..f94a7ecf 100644 --- a/crates/polkavm/src/tests.rs +++ b/crates/polkavm/src/tests.rs @@ -4571,6 +4571,32 @@ fn memset_with_dynamic_paging(mut config: Config) { assert_eq!(instance.gas(), 95); } +fn memset_preserves_a0_and_a2(config: Config) { + let _ = env_logger::try_init(); + + // Memset must not truncate A0 or A2. With count=0, memset is a no-op + // and both registers must pass through with their upper 32 bits intact. + let mut builder = ProgramBlobBuilder::new(InstructionSetKind::Latest64); + builder.add_export_by_basic_block(0, b"main"); + builder.set_code(&[asm::memset(), asm::ret()], &[]); + + let blob = ProgramBlob::parse(builder.into_vec().unwrap().into()).unwrap(); + let engine = Engine::new(&config).unwrap(); + let module = Module::from_blob(&engine, &ModuleConfig::new(), blob).unwrap(); + + let mut instance = module.instantiate().unwrap(); + instance.set_reg(Reg::A0, 0x0000000100000000); + instance.set_reg(Reg::A1, 0); + instance.set_reg(Reg::A2, 0); + instance.set_reg(Reg::A3, 0xffffffffff08bdbd); + instance.set_reg(Reg::RA, crate::RETURN_TO_HOST); + instance.set_next_program_counter(ProgramCounter(0)); + assert!(matches!(instance.run().unwrap(), InterruptKind::Finished)); + assert_eq!(instance.reg(Reg::A0), 0x0000000100000000, "memset truncated A0"); + assert_eq!(instance.reg(Reg::A2), 0); + assert_eq!(instance.reg(Reg::A3), 0xffffffffff08bdbd); +} + fn count_leading_zero_bits_32_with_zero_input(config: Config) { let _ = env_logger::try_init(); @@ -5245,6 +5271,7 @@ run_tests! { count_trailing_zero_bits_32_with_zero_input count_trailing_zero_bits_64_with_zero_input count_trailing_zero_bits_64_with_ffff0000 + memset_preserves_a0_and_a2 } run_test_blob_tests! {