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 {...@@ -5548,7 +5548,7 @@ pub fn toPosixPath(file_path: []const u8) ![MAX_PATH_BYTES - 1:0]u8 {
5548 var path_with_null: [MAX_PATH_BYTES - 1:0]u8 = undefined;5548 var path_with_null: [MAX_PATH_BYTES - 1:0]u8 = undefined;
5549 // >= rather than > to make room for the null byte5549 // >= rather than > to make room for the null byte
5550 if (file_path.len >= MAX_PATH_BYTES) return error.NameTooLong;5550 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);
5552 path_with_null[file_path.len] = 0;5552 path_with_null[file_path.len] = 0;
5553 return path_with_null;5553 return path_with_null;
5554}5554}
src/Air.zig+2
...@@ -138,12 +138,14 @@ pub const Inst = struct {...@@ -138,12 +138,14 @@ pub const Inst = struct {
138 /// The offset is in element type units, not bytes.138 /// The offset is in element type units, not bytes.
139 /// Wrapping is undefined behavior.139 /// Wrapping is undefined behavior.
140 /// The lhs is the pointer, rhs is the offset. Result type is the same as lhs.140 /// The lhs is the pointer, rhs is the offset. Result type is the same as lhs.
141 /// The pointer may be a slice.
141 /// Uses the `ty_pl` field. Payload is `Bin`.142 /// Uses the `ty_pl` field. Payload is `Bin`.
142 ptr_add,143 ptr_add,
143 /// Subtract an offset from a pointer, returning a new pointer.144 /// Subtract an offset from a pointer, returning a new pointer.
144 /// The offset is in element type units, not bytes.145 /// The offset is in element type units, not bytes.
145 /// Wrapping is undefined behavior.146 /// Wrapping is undefined behavior.
146 /// The lhs is the pointer, rhs is the offset. Result type is the same as lhs.147 /// The lhs is the pointer, rhs is the offset. Result type is the same as lhs.
148 /// The pointer may be a slice.
147 /// Uses the `ty_pl` field. Payload is `Bin`.149 /// Uses the `ty_pl` field. Payload is `Bin`.
148 ptr_sub,150 ptr_sub,
149 /// Given two operands which can be floats, integers, or vectors, returns the151 /// 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...@@ -21950,12 +21950,19 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
21950 new_src_ptr = try upgradeToArrayPtr(sema, block, src_ptr, len);21950 new_src_ptr = try upgradeToArrayPtr(sema, block, src_ptr, len);
21951 }21951 }
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
21953 try sema.requireRuntimeBlock(block, src, runtime_src);21962 try sema.requireRuntimeBlock(block, src, runtime_src);
2195421963
21955 // Aliasing safety check.21964 // Aliasing safety check.
21956 if (block.wantSafety()) {21965 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);
21959 const len = if (len_val) |v|21966 const len = if (len_val) |v|
21960 try sema.addConstant(Type.usize, v)21967 try sema.addConstant(Type.usize, v)
21961 else if (dest_len != .none)21968 else if (dest_len != .none)
...@@ -21963,12 +21970,20 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void...@@ -21963,12 +21970,20 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
21963 else21970 else
21964 src_len;21971 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
21966 // ok1: dest >= src + len21981 // ok1: dest >= src + len
21967 // ok2: src >= dest + len21982 // ok2: src >= dest + len
21968 const src_plus_len = try block.addBinOp(.add, src_int, len);21983 const src_plus_len = try sema.analyzePtrArithmetic(block, src, new_src_ptr, len, .ptr_add, src_src, src);
21969 const dest_plus_len = try block.addBinOp(.add, dest_int, len);21984 const dest_plus_len = try sema.analyzePtrArithmetic(block, src, raw_dest_ptr, len, .ptr_add, dest_src, src);
21970 const ok1 = try block.addBinOp(.cmp_gte, dest_int, src_plus_len);21985 const ok1 = try block.addBinOp(.cmp_gte, raw_dest_ptr, src_plus_len);
21971 const ok2 = try block.addBinOp(.cmp_gte, src_int, dest_plus_len);21986 const ok2 = try block.addBinOp(.cmp_gte, new_src_ptr, dest_plus_len);
21972 const ok = try block.addBinOp(.bit_or, ok1, ok2);21987 const ok = try block.addBinOp(.bit_or, ok1, ok2);
21973 try sema.addSafetyCheck(block, ok, .memcpy_alias);21988 try sema.addSafetyCheck(block, ok, .memcpy_alias);
21974 }21989 }
src/codegen/llvm.zig+34-20
...@@ -7293,39 +7293,53 @@ pub const FuncGen = struct {...@@ -7293,39 +7293,53 @@ pub const FuncGen = struct {
7293 fn airPtrAdd(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {7293 fn airPtrAdd(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
7294 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;7294 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
7295 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;7295 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);
7297 const offset = try self.resolveInst(bin_op.rhs);7297 const offset = try self.resolveInst(bin_op.rhs);
7298 const ptr_ty = self.air.typeOf(bin_op.lhs);7298 const ptr_ty = self.air.typeOf(bin_op.lhs);
7299 const llvm_elem_ty = try self.dg.lowerPtrElemTy(ptr_ty.childType());7299 const llvm_elem_ty = try self.dg.lowerPtrElemTy(ptr_ty.childType());
7300 if (ptr_ty.ptrSize() == .One) {7300 switch (ptr_ty.ptrSize()) {
7301 // It's a pointer to an array, so according to LLVM we need an extra GEP index.7301 .One => {
7302 const indices: [2]*llvm.Value = .{7302 // It's a pointer to an array, so according to LLVM we need an extra GEP index.
7303 self.context.intType(32).constNull(), offset,7303 const indices: [2]*llvm.Value = .{ self.context.intType(32).constNull(), offset };
7304 };7304 return self.builder.buildInBoundsGEP(llvm_elem_ty, ptr, &indices, indices.len, "");
7305 return self.builder.buildInBoundsGEP(llvm_elem_ty, base_ptr, &indices, indices.len, "");7305 },
7306 } else {7306 .C, .Many => {
7307 const indices: [1]*llvm.Value = .{offset};7307 const indices: [1]*llvm.Value = .{offset};
7308 return self.builder.buildInBoundsGEP(llvm_elem_ty, base_ptr, &indices, indices.len, "");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 },
7309 }7315 }
7310 }7316 }
73117317
7312 fn airPtrSub(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {7318 fn airPtrSub(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value {
7313 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;7319 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
7314 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;7320 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);
7316 const offset = try self.resolveInst(bin_op.rhs);7322 const offset = try self.resolveInst(bin_op.rhs);
7317 const negative_offset = self.builder.buildNeg(offset, "");7323 const negative_offset = self.builder.buildNeg(offset, "");
7318 const ptr_ty = self.air.typeOf(bin_op.lhs);7324 const ptr_ty = self.air.typeOf(bin_op.lhs);
7319 const llvm_elem_ty = try self.dg.lowerPtrElemTy(ptr_ty.childType());7325 const llvm_elem_ty = try self.dg.lowerPtrElemTy(ptr_ty.childType());
7320 if (ptr_ty.ptrSize() == .One) {7326 switch (ptr_ty.ptrSize()) {
7321 // It's a pointer to an array, so according to LLVM we need an extra GEP index.7327 .One => {
7322 const indices: [2]*llvm.Value = .{7328 // It's a pointer to an array, so according to LLVM we need an extra GEP index.
7323 self.context.intType(32).constNull(), negative_offset,7329 const indices: [2]*llvm.Value = .{
7324 };7330 self.context.intType(32).constNull(), negative_offset,
7325 return self.builder.buildInBoundsGEP(llvm_elem_ty, base_ptr, &indices, indices.len, "");7331 };
7326 } else {7332 return self.builder.buildInBoundsGEP(llvm_elem_ty, ptr, &indices, indices.len, "");
7327 const indices: [1]*llvm.Value = .{negative_offset};7333 },
7328 return self.builder.buildInBoundsGEP(llvm_elem_ty, base_ptr, &indices, indices.len, "");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 },
7329 }7343 }
7330 }7344 }
73317345