diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -3818,6 +3818,14 @@
   let Documentation = [ReleaseHandleDocs];
 }
 
+def DiagnoseAs : InheritableAttr {
+  let Spellings = [Clang<"diagnose_as">];
+  let Args = [ExprArgument<"Function">,
+              VariadicUnsignedArgument<"ArgIndices">];
+  let LateParsed = 1;
+  let Documentation = [DiagnoseAsDocs];
+}
+
 def Builtin : InheritableAttr {
   let Spellings = [];
   let Args = [UnsignedArgument<"ID">];
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -5928,6 +5928,28 @@
   }];
 }
 
+def DiagnoseAsDocs : Documentation {
+  let Category = DocCatFunction;
+  let Content = [{
+The ``diagnose_as` attribute specifies that Fortify diagnostics are to be
+applied to the called function as if it were the function specified by the
+attribute. The function whose diagnostics to mimic as well as the correct order
+of arguments must be specified.
+
+For example, the attribute can be used as follow.
+
+  .. code-block:: c
+
+    __attribute__((diagnose_as (__builtin_memset, 3, 2, 1)))
+    void* mymemset(int n, int c, void* const s) {
+      return __builtin_memset(s, c, n);
+    }
+
+Then (when Fortify warnings are enabled) the call ``mymemset(n, c, s)`` will
+diagnose overflows as if it were the call ``memset(s, c, n)``;
+}];
+}
+
 def ArmSveVectorBitsDocs : Documentation {
   let Category = DocCatType;
   let Content = [{
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -594,20 +594,51 @@
       isConstantEvaluated())
     return;
 
-  unsigned BuiltinID = FD->getBuiltinID(/*ConsiderWrappers=*/true);
+  bool UseDAIAttr = false;
+  FunctionDecl *UseDecl = FD;
+
+  auto DAIAttr = FD->getAttr<DiagnoseAsAttr>();
+  if (DAIAttr) {
+    DeclRefExpr *F = dyn_cast_or_null<DeclRefExpr>(DAIAttr->getFunction());
+    if (!F)
+      return;
+    FunctionDecl *AttrDecl = dyn_cast_or_null<FunctionDecl>(F->getFoundDecl());
+    if (!AttrDecl)
+      return;
+    UseDecl = AttrDecl;
+    UseDAIAttr = true;
+  }
+
+  unsigned BuiltinID = UseDecl->getBuiltinID(/*ConsiderWrappers=*/true);
+
   if (!BuiltinID)
     return;
 
   const TargetInfo &TI = getASTContext().getTargetInfo();
   unsigned SizeTypeWidth = TI.getTypeWidth(TI.getSizeType());
 
+  auto TranslateIndex = [&](unsigned Index) -> Optional<unsigned> {
+    if (UseDAIAttr) {
+      if (Index >= DAIAttr->argIndices_size())
+        return llvm::None;
+      return DAIAttr->argIndices_begin()[Index];
+    }
+    return Index;
+  };
+
   auto ComputeExplicitObjectSizeArgument =
       [&](unsigned Index) -> Optional<llvm::APSInt> {
+    auto IndexOptional = TranslateIndex(Index);
+    if (!IndexOptional)
+      return llvm::None;
+    unsigned NewIndex = IndexOptional.getValue();
     Expr::EvalResult Result;
-    Expr *SizeArg = TheCall->getArg(Index);
+    Expr *SizeArg = TheCall->getArg(NewIndex);
     if (!SizeArg->EvaluateAsInt(Result, getASTContext()))
       return llvm::None;
-    return Result.Val.getInt();
+    auto Integer = Result.Val.getInt();
+    Integer.setIsUnsigned(true);
+    return Integer;
   };
 
   auto ComputeSizeArgument = [&](unsigned Index) -> Optional<llvm::APSInt> {
@@ -616,10 +647,15 @@
     // type 0.
     int BOSType = 0;
     if (const auto *POS =
-            FD->getParamDecl(Index)->getAttr<PassObjectSizeAttr>())
+            UseDecl->getParamDecl(Index)->getAttr<PassObjectSizeAttr>())
       BOSType = POS->getType();
 
-    const Expr *ObjArg = TheCall->getArg(Index);
+    auto IndexOptional = TranslateIndex(Index);
+    if (!IndexOptional)
+      return llvm::None;
+    unsigned NewIndex = IndexOptional.getValue();
+
+    const Expr *ObjArg = TheCall->getArg(NewIndex);
     uint64_t Result;
     if (!ObjArg->tryEvaluateObjectSize(Result, getASTContext(), BOSType))
       return llvm::None;
@@ -629,7 +665,12 @@
   };
 
   auto ComputeStrLenArgument = [&](unsigned Index) -> Optional<llvm::APSInt> {
-    Expr *ObjArg = TheCall->getArg(Index);
+    auto IndexOptional = TranslateIndex(Index);
+    if (!IndexOptional)
+      return llvm::None;
+    unsigned NewIndex = IndexOptional.getValue();
+
+    Expr *ObjArg = TheCall->getArg(NewIndex);
     uint64_t Result;
     if (!ObjArg->tryEvaluateStrLen(Result, getASTContext()))
       return llvm::None;
@@ -768,7 +809,8 @@
   }
 
   if (!SourceSize || !DestinationSize ||
-      SourceSize.getValue().ule(DestinationSize.getValue()))
+      llvm::APSInt::compareValues(SourceSize.getValue(),
+                                  DestinationSize.getValue()) <= 0)
     return;
 
   StringRef FunctionName = getASTContext().BuiltinInfo.getName(BuiltinID);
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -1001,6 +1001,38 @@
 };
 }
 
+static void handleDiagnoseAsAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
+  SmallVector<unsigned, 8> Indices;
+  for (unsigned I = 1; I < AL.getNumArgs(); ++I) {
+    if (!AL.isArgExpr(I)) {
+      S.Diag(AL.getLoc(), diag::err_attribute_argument_type)
+          << AL << AANT_ArgumentIntegerConstant;
+      return;
+    }
+
+    Expr *IndexExpr = AL.getArgAsExpr(I);
+    uint32_t Index;
+
+    // If the expression is not parseable as a uint32_t we have a problem.
+    if (!checkUInt32Argument(S, AL, IndexExpr, Index, UINT_MAX, false)) {
+      S.Diag(AL.getLoc(), diag::err_attribute_argument_out_of_bounds)
+          << AL << I << IndexExpr->getSourceRange();
+      return;
+    }
+
+    Indices.push_back(Index - 1);
+  }
+
+  if (!AL.isArgExpr(0)) {
+    S.Diag(AL.getLoc(), diag::err_attribute_argument_type)
+        << AL << AANT_ArgumentConstantExpr;
+    return;
+  }
+
+  D->addAttr(::new (S.Context) DiagnoseAsAttr(S.Context, AL, AL.getArgAsExpr(0),
+                                              Indices.data(), Indices.size()));
+}
+
 static void handleDiagnoseIfAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
   S.Diag(AL.getLoc(), diag::ext_clang_diagnose_if);
 
@@ -8029,6 +8061,9 @@
   case ParsedAttr::AT_DiagnoseIf:
     handleDiagnoseIfAttr(S, D, AL);
     break;
+  case ParsedAttr::AT_DiagnoseAs:
+    handleDiagnoseAsAttr(S, D, AL);
+    break;
   case ParsedAttr::AT_NoBuiltin:
     handleNoBuiltinAttr(S, D, AL);
     break;
diff --git a/clang/test/Sema/warn-fortify-source.c b/clang/test/Sema/warn-fortify-source.c
--- a/clang/test/Sema/warn-fortify-source.c
+++ b/clang/test/Sema/warn-fortify-source.c
@@ -181,6 +181,17 @@
   sprintf(buf, "5%.1e", 9.f); // expected-warning {{'sprintf' will always overflow; destination buffer has size 6, but format string expands to at least 8}}
 }
 
+void *test_memcpy(const void *src, size_t c, void *dst) __attribute__((diagnose_as(__builtin_memcpy, 3, 1, 2))) {
+  return __builtin_memcpy(dst, src, c);
+}
+
+void call_test_memcpy() {
+  char bufferA[10];
+  char bufferB[11];
+  test_memcpy(bufferB, 10, bufferA);
+  test_memcpy(bufferB, 11, bufferA); // expected-warning {{'memcpy' will always overflow; destination buffer has size 10, but size argument is 11}}
+}
+
 #ifdef __cplusplus
 template <class> struct S {
   void mf() const {