jfb created this revision. Herald added subscribers: cfe-commits, ributzka, dexonsmith, jkorous, JDevlieghere. Herald added a project: clang.
These builtins are often used (or should be used) in places where time-of-check time-of-use security issues are important (e.g. copying from untrusted buffers). They don't accept volatile pointee parameters in C++, and merely warn about such parameters in C, which leads to confusion. In these settings, it's useful to overload the builtin and permit volatile pointee parameters. The code generation then directly emits the existing volatile variant of the mem* builtin function call, which ensures that the affected memory location is only accessed once (thereby preventing double-reads under an adversatial memory mapping). Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D79279 Files: clang/include/clang/Basic/Builtins.def clang/lib/CodeGen/CGBuiltin.cpp clang/test/CodeGen/builtin-memfns.c clang/test/CodeGen/builtin-volatile-memfns.c
Index: clang/test/CodeGen/builtin-volatile-memfns.c =================================================================== --- /dev/null +++ clang/test/CodeGen/builtin-volatile-memfns.c @@ -0,0 +1,68 @@ +// RUN: %clang_cc1 -triple arm64-unknown-unknown -emit-llvm < %s| FileCheck %s + +typedef __SIZE_TYPE__ size_t; + +// Regular char* parameters + +// CHECK-LABEL: dst_cpy( +// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64({{.*}}, i1 true) +void dst_cpy(volatile char *dst, const char *src, size_t size) { __builtin_memcpy(dst, src, size); } + +// CHECK-LABEL: src_cpy( +// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64({{.*}}, i1 true) +void src_cpy(char *dst, volatile const char *src, size_t size) { __builtin_memcpy(dst, src, size); } + +// CHECK-LABEL: dstsrc_cpy( +// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64({{.*}}, i1 true) +void dstsrc_cpy(volatile char *dst, volatile const char *src, size_t size) { __builtin_memcpy(dst, src, size); } + +// CHECK-LABEL: dst_move( +// CHECK: call void @llvm.memmove.p0i8.p0i8.i64({{.*}}, i1 true) +void dst_move(volatile char *dst, const char *src, size_t size) { __builtin_memmove(dst, src, size); } + +// CHECK-LABEL: src_move( +// CHECK: call void @llvm.memmove.p0i8.p0i8.i64({{.*}}, i1 true) +void src_move(char *dst, volatile const char *src, size_t size) { __builtin_memmove(dst, src, size); } + +// CHECK-LABEL: dstsrc_move( +// CHECK: call void @llvm.memmove.p0i8.p0i8.i64({{.*}}, i1 true) +void dstsrc_move(volatile char *dst, volatile const char *src, size_t size) { __builtin_memmove(dst, src, size); } + +// CHECK-LABEL: set( +// CHECK: call void @llvm.memset.p0i8.i64({{.*}}, i1 true) +void set(volatile char *dst, char value, size_t size) { __builtin_memset(dst, value, size); } + +// Arrays + +extern const char asrc[512]; +extern const volatile char avsrc[512]; +extern char adst[512]; +extern volatile char avdst[512]; + +// CHECK-LABEL: array_dst_cpy( +// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64({{.*}}, i1 true) +void array_dst_cpy(size_t size) { __builtin_memcpy(avdst, asrc, size); } + +// CHECK-LABEL: array_src_cpy( +// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64({{.*}}, i1 true) +void array_src_cpy(size_t size) { __builtin_memcpy(adst, avsrc, size); } + +// CHECK-LABEL: array_dstsrc_cpy( +// CHECK: call void @llvm.memcpy.p0i8.p0i8.i64({{.*}}, i1 true) +void array_dstsrc_cpy(size_t size) { __builtin_memcpy(avdst, avsrc, size); } + +// CHECK-LABEL: array_dst_move( +// CHECK: call void @llvm.memmove.p0i8.p0i8.i64({{.*}}, i1 true) +void array_dst_move(size_t size) { __builtin_memmove(avdst, asrc, size); } + +// CHECK-LABEL: array_src_move( +// CHECK: call void @llvm.memmove.p0i8.p0i8.i64({{.*}}, i1 true) +void array_src_move(size_t size) { __builtin_memmove(adst, avsrc, size); } + +// CHECK-LABEL: array_dstsrc_move( +// CHECK: call void @llvm.memmove.p0i8.p0i8.i64({{.*}}, i1 true) +void array_dstsrc_move(size_t size) { __builtin_memmove(avdst, avsrc, size); } + +// CHECK-LABEL: array_set( +// CHECK: call void @llvm.memset.p0i8.i64({{.*}}, i1 true) +void array_set(char value, size_t size) { __builtin_memset(avdst, value, size); } Index: clang/test/CodeGen/builtin-memfns.c =================================================================== --- clang/test/CodeGen/builtin-memfns.c +++ clang/test/CodeGen/builtin-memfns.c @@ -7,10 +7,10 @@ void *memccpy(void *, void const *, int, size_t); // CHECK: @test1 -// CHECK: call void @llvm.memset.p0i8.i32 -// CHECK: call void @llvm.memset.p0i8.i32 -// CHECK: call void @llvm.memcpy.p0i8.p0i8.i32 -// CHECK: call void @llvm.memmove.p0i8.p0i8.i32 +// CHECK: call void @llvm.memset.p0i8.i32({{.*}}, i1 false) +// CHECK: call void @llvm.memset.p0i8.i32({{.*}}, i1 false) +// CHECK: call void @llvm.memcpy.p0i8.p0i8.i32({{.*}}, i1 false) +// CHECK: call void @llvm.memmove.p0i8.p0i8.i32({{.*}}, i1 false) // CHECK-NOT: __builtin // CHECK: ret int test1(int argc, char **argv) { @@ -26,31 +26,31 @@ // rdar://9289468 // CHECK: @test2 -// CHECK: call void @llvm.memcpy.p0i8.p0i8.i32 +// CHECK: call void @llvm.memcpy.p0i8.p0i8.i32({{.*}}, i1 false) char* test2(char* a, char* b) { return __builtin_memcpy(a, b, 4); } // CHECK: @test3 -// CHECK: call void @llvm.memset +// CHECK: call void @llvm.memset{{.*}}, i1 false) void test3(char *P) { __builtin___memset_chk(P, 42, 128, 128); } // CHECK: @test4 -// CHECK: call void @llvm.memcpy +// CHECK: call void @llvm.memcpy{{.*}}, i1 false) void test4(char *P, char *Q) { __builtin___memcpy_chk(P, Q, 128, 128); } // CHECK: @test5 -// CHECK: call void @llvm.memmove +// CHECK: call void @llvm.memmove{{.*}}, i1 false) void test5(char *P, char *Q) { __builtin___memmove_chk(P, Q, 128, 128); } // CHECK: @test6 -// CHECK: call void @llvm.memcpy +// CHECK: call void @llvm.memcpy{{.*}}, i1 false) int test6(char *X) { return __builtin___memcpy_chk(X, X, 42, 42) != 0; } @@ -108,7 +108,7 @@ typedef struct { int a; } b; int d; b e; - // CHECK: call void @llvm.memcpy{{.*}}( + // CHECK: call void @llvm.memcpy{{.*}}, i1 false) memcpy(&d, (char *)&e.a, sizeof(e)); } @@ -116,7 +116,7 @@ extern char dest_array[]; extern char src_array[]; void test12() { - // CHECK: call void @llvm.memcpy{{.*}}( + // CHECK: call void @llvm.memcpy{{.*}}, i1 false) memcpy(&dest_array, &dest_array, 2); } Index: clang/lib/CodeGen/CGBuiltin.cpp =================================================================== --- clang/lib/CodeGen/CGBuiltin.cpp +++ clang/lib/CodeGen/CGBuiltin.cpp @@ -619,6 +619,19 @@ return {Width, Signed}; } +static bool isPtrArgVolatile(CodeGenModule &CGM, const CallExpr *E, + unsigned ArgNo) { + QualType ArgTy = E->getArg(ArgNo)->IgnoreImpCasts()->getType(); + if (ArgTy->isArrayType()) + return CGM.getContext() + .getAsArrayType(ArgTy) + ->getElementType() + .isVolatileQualified(); + return ArgTy->castAs<clang::PointerType>() + ->getPointeeType() + .isVolatileQualified(); +} + Value *CodeGenFunction::EmitVAStartEnd(Value *ArgValue, bool IsStart) { llvm::Type *DestType = Int8PtrTy; if (ArgValue->getType() != DestType) @@ -2529,12 +2542,14 @@ case Builtin::BI__builtin_mempcpy: { Address Dest = EmitPointerWithAlignment(E->getArg(0)); Address Src = EmitPointerWithAlignment(E->getArg(1)); + bool isVolatile = + isPtrArgVolatile(CGM, E, 0) || isPtrArgVolatile(CGM, E, 1); Value *SizeVal = EmitScalarExpr(E->getArg(2)); EmitNonNullArgCheck(RValue::get(Dest.getPointer()), E->getArg(0)->getType(), E->getArg(0)->getExprLoc(), FD, 0); EmitNonNullArgCheck(RValue::get(Src.getPointer()), E->getArg(1)->getType(), E->getArg(1)->getExprLoc(), FD, 1); - Builder.CreateMemCpy(Dest, Src, SizeVal, false); + Builder.CreateMemCpy(Dest, Src, SizeVal, isVolatile); if (BuiltinID == Builtin::BImempcpy || BuiltinID == Builtin::BI__builtin_mempcpy) return RValue::get(Builder.CreateInBoundsGEP(Dest.getPointer(), SizeVal)); @@ -2571,8 +2586,10 @@ break; Address Dest = EmitPointerWithAlignment(E->getArg(0)); Address Src = EmitPointerWithAlignment(E->getArg(1)); + bool isVolatile = + isPtrArgVolatile(CGM, E, 0) || isPtrArgVolatile(CGM, E, 1); Value *SizeVal = llvm::ConstantInt::get(Builder.getContext(), Size); - Builder.CreateMemCpy(Dest, Src, SizeVal, false); + Builder.CreateMemCpy(Dest, Src, SizeVal, isVolatile); return RValue::get(Dest.getPointer()); } @@ -2597,8 +2614,10 @@ break; Address Dest = EmitPointerWithAlignment(E->getArg(0)); Address Src = EmitPointerWithAlignment(E->getArg(1)); + bool isVolatile = + isPtrArgVolatile(CGM, E, 0) || isPtrArgVolatile(CGM, E, 1); Value *SizeVal = llvm::ConstantInt::get(Builder.getContext(), Size); - Builder.CreateMemMove(Dest, Src, SizeVal, false); + Builder.CreateMemMove(Dest, Src, SizeVal, isVolatile); return RValue::get(Dest.getPointer()); } @@ -2606,23 +2625,26 @@ case Builtin::BI__builtin_memmove: { Address Dest = EmitPointerWithAlignment(E->getArg(0)); Address Src = EmitPointerWithAlignment(E->getArg(1)); + bool isVolatile = + isPtrArgVolatile(CGM, E, 0) || isPtrArgVolatile(CGM, E, 1); Value *SizeVal = EmitScalarExpr(E->getArg(2)); EmitNonNullArgCheck(RValue::get(Dest.getPointer()), E->getArg(0)->getType(), E->getArg(0)->getExprLoc(), FD, 0); EmitNonNullArgCheck(RValue::get(Src.getPointer()), E->getArg(1)->getType(), E->getArg(1)->getExprLoc(), FD, 1); - Builder.CreateMemMove(Dest, Src, SizeVal, false); + Builder.CreateMemMove(Dest, Src, SizeVal, isVolatile); return RValue::get(Dest.getPointer()); } case Builtin::BImemset: case Builtin::BI__builtin_memset: { Address Dest = EmitPointerWithAlignment(E->getArg(0)); + bool isVolatile = isPtrArgVolatile(CGM, E, 0); Value *ByteVal = Builder.CreateTrunc(EmitScalarExpr(E->getArg(1)), Builder.getInt8Ty()); Value *SizeVal = EmitScalarExpr(E->getArg(2)); EmitNonNullArgCheck(RValue::get(Dest.getPointer()), E->getArg(0)->getType(), E->getArg(0)->getExprLoc(), FD, 0); - Builder.CreateMemSet(Dest, ByteVal, SizeVal, false); + Builder.CreateMemSet(Dest, ByteVal, SizeVal, isVolatile); return RValue::get(Dest.getPointer()); } case Builtin::BI__builtin___memset_chk: { @@ -2636,10 +2658,11 @@ if (Size.ugt(DstSize)) break; Address Dest = EmitPointerWithAlignment(E->getArg(0)); + bool isVolatile = isPtrArgVolatile(CGM, E, 0); Value *ByteVal = Builder.CreateTrunc(EmitScalarExpr(E->getArg(1)), Builder.getInt8Ty()); Value *SizeVal = llvm::ConstantInt::get(Builder.getContext(), Size); - Builder.CreateMemSet(Dest, ByteVal, SizeVal, false); + Builder.CreateMemSet(Dest, ByteVal, SizeVal, isVolatile); return RValue::get(Dest.getPointer()); } case Builtin::BI__builtin_wmemcmp: { Index: clang/include/clang/Basic/Builtins.def =================================================================== --- clang/include/clang/Basic/Builtins.def +++ clang/include/clang/Basic/Builtins.def @@ -480,11 +480,11 @@ BUILTIN(__builtin_fprintf, "iP*cC*.", "Fp:1:") BUILTIN(__builtin_memchr, "v*vC*iz", "nF") BUILTIN(__builtin_memcmp, "ivC*vC*z", "nF") -BUILTIN(__builtin_memcpy, "v*v*vC*z", "nF") +BUILTIN(__builtin_memcpy, "v*vD*vCD*z", "nF") BUILTIN(__builtin_memcpy_inline, "vv*vC*Iz", "nt") -BUILTIN(__builtin_memmove, "v*v*vC*z", "nF") +BUILTIN(__builtin_memmove, "v*vD*vCD*z", "nF") BUILTIN(__builtin_mempcpy, "v*v*vC*z", "nF") -BUILTIN(__builtin_memset, "v*v*iz", "nF") +BUILTIN(__builtin_memset, "v*vD*iz", "nF") BUILTIN(__builtin_printf, "icC*.", "Fp:0:") BUILTIN(__builtin_stpcpy, "c*c*cC*", "nF") BUILTIN(__builtin_stpncpy, "c*c*cC*z", "nF")
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits