authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-07-03 20:08:13+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2023-07-08 17:45:04+02:00
log836f9fceab03c7de56eba7a9c2e810206e7e8469
tree216e3c7486d9c2f3aae0ab14848ad1c0cf94201e
parent89396ff02ba235592641fb388e3958c2c047e728
signaturelock-open Commit is signed but in an unrecognized format.

llvm: add safety-check for Wasm memcpy

When lowering the `memcpy` instruction, LLVM will lower it to WebAssembly's `memory.copy` instruction when the bulk-memory feature is enabled. This instruction will trap when the destination or source pointer is out-of-bounds. By Zig's semantics, it is valid to have an invalid pointer when the length is 0. To prevent runtimes from trapping, we add a safety-check for slices to only lower to a memcpy instruction when the length is larger than 0.

1 files changed, 35 insertions(+), 0 deletions(-)

src/codegen/llvm.zig+35
......@@ -8634,6 +8634,41 @@ pub const FuncGen = struct {
86348634 const len = self.sliceOrArrayLenInBytes(dest_slice, dest_ptr_ty);
86358635 const dest_ptr = self.sliceOrArrayPtr(dest_slice, dest_ptr_ty);
86368636 const is_volatile = src_ptr_ty.isVolatilePtr(mod) or dest_ptr_ty.isVolatilePtr(mod);
8637
8638 // When bulk-memory is enabled, this will be lowered to WebAssembly's memory.copy instruction.
8639 // This instruction will trap on an invalid address, regardless of the length.
8640 // For this reason we must add a safety-check for 0-sized slices as its pointer field can be undefined.
8641 // We only have to do this for slices as arrays will have a valid pointer.
8642 if (o.target.isWasm() and
8643 std.Target.wasm.featureSetHas(o.target.cpu.features, .bulk_memory) and
8644 (src_ptr_ty.isSlice(mod) or dest_ptr_ty.isSlice(mod)))
8645 {
8646 const parent_block = self.context.createBasicBlock("Block");
8647
8648 const llvm_usize_ty = self.context.intType(o.target.ptrBitWidth());
8649 const cond = try self.cmp(len, llvm_usize_ty.constInt(0, .False), Type.usize, .eq);
8650 const then_block = self.context.appendBasicBlock(self.llvm_func, "Then");
8651 const else_block = self.context.appendBasicBlock(self.llvm_func, "Else");
8652 _ = self.builder.buildCondBr(cond, then_block, else_block);
8653
8654 self.builder.positionBuilderAtEnd(then_block);
8655 _ = self.builder.buildBr(parent_block);
8656
8657 self.builder.positionBuilderAtEnd(else_block);
8658 _ = self.builder.buildMemCpy(
8659 dest_ptr,
8660 dest_ptr_ty.ptrAlignment(mod),
8661 src_ptr,
8662 src_ptr_ty.ptrAlignment(mod),
8663 len,
8664 is_volatile,
8665 );
8666 _ = self.builder.buildBr(parent_block);
8667 self.llvm_func.appendExistingBasicBlock(parent_block);
8668 self.builder.positionBuilderAtEnd(parent_block);
8669 return null;
8670 }
8671
86378672 _ = self.builder.buildMemCpy(
86388673 dest_ptr,
86398674 dest_ptr_ty.ptrAlignment(mod),