Sema: fix memcpy alias safety incorrect math

Previously it was not multiplying by the element ABI size. Now, it uses
ptr_add instructions which do math based on the element type.
This commit is contained in:
Andrew Kelley 2023-04-24 21:39:03 -07:00
parent 5378fdffdc
commit 792bbfa301
4 changed files with 58 additions and 27 deletions

View File

@ -5548,7 +5548,7 @@ pub fn toPosixPath(file_path: []const u8) ![MAX_PATH_BYTES - 1:0]u8 {
var path_with_null: [MAX_PATH_BYTES - 1:0]u8 = undefined;
// >= rather than > to make room for the null byte
if (file_path.len >= MAX_PATH_BYTES) return error.NameTooLong;
mem.copy(u8, &path_with_null, file_path);
@memcpy(path_with_null[0..file_path.len], file_path);
path_with_null[file_path.len] = 0;
return path_with_null;
}

View File

@ -138,12 +138,14 @@ pub const Inst = struct {
/// The offset is in element type units, not bytes.
/// Wrapping is undefined behavior.
/// The lhs is the pointer, rhs is the offset. Result type is the same as lhs.
/// The pointer may be a slice.
/// Uses the `ty_pl` field. Payload is `Bin`.
ptr_add,
/// Subtract an offset from a pointer, returning a new pointer.
/// The offset is in element type units, not bytes.
/// Wrapping is undefined behavior.
/// The lhs is the pointer, rhs is the offset. Result type is the same as lhs.
/// The pointer may be a slice.
/// Uses the `ty_pl` field. Payload is `Bin`.
ptr_sub,
/// Given two operands which can be floats, integers, or vectors, returns the

View File

@ -21950,12 +21950,19 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
new_src_ptr = try upgradeToArrayPtr(sema, block, src_ptr, len);
}
if (dest_len != .none) {
// Change the src from slice to a many pointer, to avoid multiple ptr
// slice extractions in AIR instructions.
const new_src_ptr_ty = sema.typeOf(new_src_ptr);
if (new_src_ptr_ty.isSlice()) {
new_src_ptr = try sema.analyzeSlicePtr(block, src_src, new_src_ptr, new_src_ptr_ty);
}
}
try sema.requireRuntimeBlock(block, src, runtime_src);
// Aliasing safety check.
if (block.wantSafety()) {
const dest_int = try block.addUnOp(.ptrtoint, new_dest_ptr);
const src_int = try block.addUnOp(.ptrtoint, new_src_ptr);
const len = if (len_val) |v|
try sema.addConstant(Type.usize, v)
else if (dest_len != .none)
@ -21963,12 +21970,20 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
else
src_len;
// Extract raw pointer from dest slice. The AIR instructions could support them, but
// it would cause redundant machine code instructions.
const new_dest_ptr_ty = sema.typeOf(new_dest_ptr);
const raw_dest_ptr = if (new_dest_ptr_ty.isSlice())
try sema.analyzeSlicePtr(block, dest_src, new_dest_ptr, new_dest_ptr_ty)
else
new_dest_ptr;
// ok1: dest >= src + len
// ok2: src >= dest + len
const src_plus_len = try block.addBinOp(.add, src_int, len);
const dest_plus_len = try block.addBinOp(.add, dest_int, len);
const ok1 = try block.addBinOp(.cmp_gte, dest_int, src_plus_len);
const ok2 = try block.addBinOp(.cmp_gte, src_int, dest_plus_len);
const src_plus_len = try sema.analyzePtrArithmetic(block, src, new_src_ptr, len, .ptr_add, src_src, src);
const dest_plus_len = try sema.analyzePtrArithmetic(block, src, raw_dest_ptr, len, .ptr_add, dest_src, src);
const ok1 = try block.addBinOp(.cmp_gte, raw_dest_ptr, src_plus_len);
const ok2 = try block.addBinOp(.cmp_gte, new_src_ptr, dest_plus_len);
const ok = try block.addBinOp(.bit_or, ok1, ok2);
try sema.addSafetyCheck(block, ok, .memcpy_alias);
}

View File

@ -7293,39 +7293,53 @@ pub const FuncGen = struct {
fn airPtrAdd(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
const base_ptr = try self.resolveInst(bin_op.lhs);
const ptr = try self.resolveInst(bin_op.lhs);
const offset = try self.resolveInst(bin_op.rhs);
const ptr_ty = self.air.typeOf(bin_op.lhs);
const llvm_elem_ty = try self.dg.lowerPtrElemTy(ptr_ty.childType());
if (ptr_ty.ptrSize() == .One) {
// It's a pointer to an array, so according to LLVM we need an extra GEP index.
const indices: [2]*llvm.Value = .{
self.context.intType(32).constNull(), offset,
};
return self.builder.buildInBoundsGEP(llvm_elem_ty, base_ptr, &indices, indices.len, "");
} else {
const indices: [1]*llvm.Value = .{offset};
return self.builder.buildInBoundsGEP(llvm_elem_ty, base_ptr, &indices, indices.len, "");
switch (ptr_ty.ptrSize()) {
.One => {
// It's a pointer to an array, so according to LLVM we need an extra GEP index.
const indices: [2]*llvm.Value = .{ self.context.intType(32).constNull(), offset };
return self.builder.buildInBoundsGEP(llvm_elem_ty, ptr, &indices, indices.len, "");
},
.C, .Many => {
const indices: [1]*llvm.Value = .{offset};
return self.builder.buildInBoundsGEP(llvm_elem_ty, ptr, &indices, indices.len, "");
},
.Slice => {
const base = self.builder.buildExtractValue(ptr, 0, "");
const indices: [1]*llvm.Value = .{offset};
return self.builder.buildInBoundsGEP(llvm_elem_ty, base, &indices, indices.len, "");
},
}
}
fn airPtrSub(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
const base_ptr = try self.resolveInst(bin_op.lhs);
const ptr = try self.resolveInst(bin_op.lhs);
const offset = try self.resolveInst(bin_op.rhs);
const negative_offset = self.builder.buildNeg(offset, "");
const ptr_ty = self.air.typeOf(bin_op.lhs);
const llvm_elem_ty = try self.dg.lowerPtrElemTy(ptr_ty.childType());
if (ptr_ty.ptrSize() == .One) {
// It's a pointer to an array, so according to LLVM we need an extra GEP index.
const indices: [2]*llvm.Value = .{
self.context.intType(32).constNull(), negative_offset,
};
return self.builder.buildInBoundsGEP(llvm_elem_ty, base_ptr, &indices, indices.len, "");
} else {
const indices: [1]*llvm.Value = .{negative_offset};
return self.builder.buildInBoundsGEP(llvm_elem_ty, base_ptr, &indices, indices.len, "");
switch (ptr_ty.ptrSize()) {
.One => {
// It's a pointer to an array, so according to LLVM we need an extra GEP index.
const indices: [2]*llvm.Value = .{
self.context.intType(32).constNull(), negative_offset,
};
return self.builder.buildInBoundsGEP(llvm_elem_ty, ptr, &indices, indices.len, "");
},
.C, .Many => {
const indices: [1]*llvm.Value = .{negative_offset};
return self.builder.buildInBoundsGEP(llvm_elem_ty, ptr, &indices, indices.len, "");
},
.Slice => {
const base = self.builder.buildExtractValue(ptr, 0, "");
const indices: [1]*llvm.Value = .{negative_offset};
return self.builder.buildInBoundsGEP(llvm_elem_ty, base, &indices, indices.len, "");
},
}
}