authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-04-24 21:39:03-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-04-25 11:23:41-07:00
log792bbfa301436fc0ce31bc4072b4765ba1408a55
tree4e249d92a5ab600e4ba67893e10ce392b7e506cb
parent5378fdffdcc60a5273021bc9cfc5be917e87c992

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.

4 files changed, 58 insertions(+), 27 deletions(-)

lib/std/os.zig+1-1
......@@ -5548,7 +5548,7 @@ pub fn toPosixPath(file_path: []const u8) ![MAX_PATH_BYTES - 1:0]u8 {
55485548 var path_with_null: [MAX_PATH_BYTES - 1:0]u8 = undefined;
55495549 // >= rather than > to make room for the null byte
55505550 if (file_path.len >= MAX_PATH_BYTES) return error.NameTooLong;
5551 mem.copy(u8, &path_with_null, file_path);
5551 @memcpy(path_with_null[0..file_path.len], file_path);
55525552 path_with_null[file_path.len] = 0;
55535553 return path_with_null;
55545554}
src/Air.zig+2
......@@ -138,12 +138,14 @@ pub const Inst = struct {
138138 /// The offset is in element type units, not bytes.
139139 /// Wrapping is undefined behavior.
140140 /// The lhs is the pointer, rhs is the offset. Result type is the same as lhs.
141 /// The pointer may be a slice.
141142 /// Uses the `ty_pl` field. Payload is `Bin`.
142143 ptr_add,
143144 /// Subtract an offset from a pointer, returning a new pointer.
144145 /// The offset is in element type units, not bytes.
145146 /// Wrapping is undefined behavior.
146147 /// The lhs is the pointer, rhs is the offset. Result type is the same as lhs.
148 /// The pointer may be a slice.
147149 /// Uses the `ty_pl` field. Payload is `Bin`.
148150 ptr_sub,
149151 /// Given two operands which can be floats, integers, or vectors, returns the
src/Sema.zig+21-6
......@@ -21950,12 +21950,19 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
2195021950 new_src_ptr = try upgradeToArrayPtr(sema, block, src_ptr, len);
2195121951 }
2195221952
21953 if (dest_len != .none) {
21954 // Change the src from slice to a many pointer, to avoid multiple ptr
21955 // slice extractions in AIR instructions.
21956 const new_src_ptr_ty = sema.typeOf(new_src_ptr);
21957 if (new_src_ptr_ty.isSlice()) {
21958 new_src_ptr = try sema.analyzeSlicePtr(block, src_src, new_src_ptr, new_src_ptr_ty);
21959 }
21960 }
21961
2195321962 try sema.requireRuntimeBlock(block, src, runtime_src);
2195421963
2195521964 // Aliasing safety check.
2195621965 if (block.wantSafety()) {
21957 const dest_int = try block.addUnOp(.ptrtoint, new_dest_ptr);
21958 const src_int = try block.addUnOp(.ptrtoint, new_src_ptr);
2195921966 const len = if (len_val) |v|
2196021967 try sema.addConstant(Type.usize, v)
2196121968 else if (dest_len != .none)
......@@ -21963,12 +21970,20 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
2196321970 else
2196421971 src_len;
2196521972
21973 // Extract raw pointer from dest slice. The AIR instructions could support them, but
21974 // it would cause redundant machine code instructions.
21975 const new_dest_ptr_ty = sema.typeOf(new_dest_ptr);
21976 const raw_dest_ptr = if (new_dest_ptr_ty.isSlice())
21977 try sema.analyzeSlicePtr(block, dest_src, new_dest_ptr, new_dest_ptr_ty)
21978 else
21979 new_dest_ptr;
21980
2196621981 // ok1: dest >= src + len
2196721982 // ok2: src >= dest + len
21968 const src_plus_len = try block.addBinOp(.add, src_int, len);
21969 const dest_plus_len = try block.addBinOp(.add, dest_int, len);
21970 const ok1 = try block.addBinOp(.cmp_gte, dest_int, src_plus_len);
21971 const ok2 = try block.addBinOp(.cmp_gte, src_int, dest_plus_len);
21983 const src_plus_len = try sema.analyzePtrArithmetic(block, src, new_src_ptr, len, .ptr_add, src_src, src);
21984 const dest_plus_len = try sema.analyzePtrArithmetic(block, src, raw_dest_ptr, len, .ptr_add, dest_src, src);
21985 const ok1 = try block.addBinOp(.cmp_gte, raw_dest_ptr, src_plus_len);
21986 const ok2 = try block.addBinOp(.cmp_gte, new_src_ptr, dest_plus_len);
2197221987 const ok = try block.addBinOp(.bit_or, ok1, ok2);
2197321988 try sema.addSafetyCheck(block, ok, .memcpy_alias);
2197421989 }
src/codegen/llvm.zig+34-20
......@@ -7293,39 +7293,53 @@ pub const FuncGen = struct {
72937293 fn airPtrAdd(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
72947294 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
72957295 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
7296 const base_ptr = try self.resolveInst(bin_op.lhs);
7296 const ptr = try self.resolveInst(bin_op.lhs);
72977297 const offset = try self.resolveInst(bin_op.rhs);
72987298 const ptr_ty = self.air.typeOf(bin_op.lhs);
72997299 const llvm_elem_ty = try self.dg.lowerPtrElemTy(ptr_ty.childType());
7300 if (ptr_ty.ptrSize() == .One) {
7301 // It's a pointer to an array, so according to LLVM we need an extra GEP index.
7302 const indices: [2]*llvm.Value = .{
7303 self.context.intType(32).constNull(), offset,
7304 };
7305 return self.builder.buildInBoundsGEP(llvm_elem_ty, base_ptr, &indices, indices.len, "");
7306 } else {
7307 const indices: [1]*llvm.Value = .{offset};
7308 return self.builder.buildInBoundsGEP(llvm_elem_ty, base_ptr, &indices, indices.len, "");
7300 switch (ptr_ty.ptrSize()) {
7301 .One => {
7302 // It's a pointer to an array, so according to LLVM we need an extra GEP index.
7303 const indices: [2]*llvm.Value = .{ self.context.intType(32).constNull(), offset };
7304 return self.builder.buildInBoundsGEP(llvm_elem_ty, ptr, &indices, indices.len, "");
7305 },
7306 .C, .Many => {
7307 const indices: [1]*llvm.Value = .{offset};
7308 return self.builder.buildInBoundsGEP(llvm_elem_ty, ptr, &indices, indices.len, "");
7309 },
7310 .Slice => {
7311 const base = self.builder.buildExtractValue(ptr, 0, "");
7312 const indices: [1]*llvm.Value = .{offset};
7313 return self.builder.buildInBoundsGEP(llvm_elem_ty, base, &indices, indices.len, "");
7314 },
73097315 }
73107316 }
73117317
73127318 fn airPtrSub(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
73137319 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
73147320 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
7315 const base_ptr = try self.resolveInst(bin_op.lhs);
7321 const ptr = try self.resolveInst(bin_op.lhs);
73167322 const offset = try self.resolveInst(bin_op.rhs);
73177323 const negative_offset = self.builder.buildNeg(offset, "");
73187324 const ptr_ty = self.air.typeOf(bin_op.lhs);
73197325 const llvm_elem_ty = try self.dg.lowerPtrElemTy(ptr_ty.childType());
7320 if (ptr_ty.ptrSize() == .One) {
7321 // It's a pointer to an array, so according to LLVM we need an extra GEP index.
7322 const indices: [2]*llvm.Value = .{
7323 self.context.intType(32).constNull(), negative_offset,
7324 };
7325 return self.builder.buildInBoundsGEP(llvm_elem_ty, base_ptr, &indices, indices.len, "");
7326 } else {
7327 const indices: [1]*llvm.Value = .{negative_offset};
7328 return self.builder.buildInBoundsGEP(llvm_elem_ty, base_ptr, &indices, indices.len, "");
7326 switch (ptr_ty.ptrSize()) {
7327 .One => {
7328 // It's a pointer to an array, so according to LLVM we need an extra GEP index.
7329 const indices: [2]*llvm.Value = .{
7330 self.context.intType(32).constNull(), negative_offset,
7331 };
7332 return self.builder.buildInBoundsGEP(llvm_elem_ty, ptr, &indices, indices.len, "");
7333 },
7334 .C, .Many => {
7335 const indices: [1]*llvm.Value = .{negative_offset};
7336 return self.builder.buildInBoundsGEP(llvm_elem_ty, ptr, &indices, indices.len, "");
7337 },
7338 .Slice => {
7339 const base = self.builder.buildExtractValue(ptr, 0, "");
7340 const indices: [1]*llvm.Value = .{negative_offset};
7341 return self.builder.buildInBoundsGEP(llvm_elem_ty, base, &indices, indices.len, "");
7342 },
73297343 }
73307344 }
73317345