mehdi_amini updated this revision to Diff 75524.
mehdi_amini added a comment.

Address David's feedback. Thanks David!


https://reviews.llvm.org/D25888

Files:
  clang/include/clang/Analysis/Analyses/FormatString.h
  clang/include/clang/Analysis/Analyses/OSLog.h
  clang/include/clang/Basic/Builtins.def
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/include/clang/Sema/Sema.h
  clang/lib/Analysis/CMakeLists.txt
  clang/lib/Analysis/FormatString.cpp
  clang/lib/Analysis/OSLog.cpp
  clang/lib/Analysis/PrintfFormatString.cpp
  clang/lib/CodeGen/CGBuiltin.cpp
  clang/lib/Sema/SemaChecking.cpp
  clang/lib/Sema/SemaDeclAttr.cpp
  clang/test/CodeGen/builtins.c
  clang/test/CodeGenObjC/os_log.m
  clang/test/SemaObjC/format-strings-oslog.m

Index: clang/test/SemaObjC/format-strings-oslog.m
===================================================================
--- /dev/null
+++ clang/test/SemaObjC/format-strings-oslog.m
@@ -0,0 +1,62 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+
+#include <stdarg.h>
+#include <stddef.h>
+#define __need_wint_t
+#include <stddef.h> // For wint_t and wchar_t
+
+int printf(const char *restrict, ...);
+
+@interface NSString
+@end
+
+void test_os_log_format(const char *pc, int i, void *p, void *buf) {
+  __builtin_os_log_format(buf, "");
+  __builtin_os_log_format(buf, "%d"); // expected-warning {{more '%' conversions than data arguments}}
+  __builtin_os_log_format(buf, "%d", i);
+  __builtin_os_log_format(buf, "%P", p); // expected-warning {{using '%P' format specifier without precision}}
+  __builtin_os_log_format(buf, "%.10P", p);
+  __builtin_os_log_format(buf, "%.*P", p); // expected-warning {{field precision should have type 'int', but argument has type 'void *'}}
+  __builtin_os_log_format(buf, "%.*P", i, p);
+  __builtin_os_log_format(buf, "%.*P", i, i); // expected-warning {{format specifies type 'void *' but the argument has type 'int'}}
+  __builtin_os_log_format(buf, "%n");         // expected-error {{os_log() '%n' format specifier is not allowed}}
+  __builtin_os_log_format(buf, pc);           // expected-error {{os_log() format argument is not a string constant}}
+
+  printf("%{private}s", pc); // expected-warning {{using 'private' format specifier annotation outside of os_log()/os_trace()}}
+  __builtin_os_log_format(buf, "%{private}s", pc);
+
+  // <rdar://problem/23835805>
+  __builtin_os_log_format_buffer_size("no-args");
+  __builtin_os_log_format(buf, "%s", "hi");
+
+  // <rdar://problem/24828090>
+  wchar_t wc = 'a';
+  __builtin_os_log_format(buf, "%C", wc);
+  printf("%C", wc);
+  wchar_t wcs[] = {'a', 0};
+  __builtin_os_log_format(buf, "%S", wcs);
+  printf("%S", wcs);
+}
+
+// Test os_log_format primitive with ObjC string literal format argument.
+void test_objc(const char *pc, int i, void *p, void *buf, NSString *nss) {
+  __builtin_os_log_format(buf, @"");
+  __builtin_os_log_format(buf, @"%d"); // expected-warning {{more '%' conversions than data arguments}}
+  __builtin_os_log_format(buf, @"%d", i);
+  __builtin_os_log_format(buf, @"%P", p); // expected-warning {{using '%P' format specifier without precision}}
+  __builtin_os_log_format(buf, @"%.10P", p);
+  __builtin_os_log_format(buf, @"%.*P", p); // expected-warning {{field precision should have type 'int', but argument has type 'void *'}}
+  __builtin_os_log_format(buf, @"%.*P", i, p);
+  __builtin_os_log_format(buf, @"%.*P", i, i); // expected-warning {{format specifies type 'void *' but the argument has type 'int'}}
+
+  __builtin_os_log_format(buf, @"%{private}s", pc);
+  __builtin_os_log_format(buf, @"%@", nss);
+}
+
+// Test the os_log format attribute.
+void MyOSLog(const char *format, ...) __attribute__((format(os_log, 1, 2)));
+void test_attribute(void *p) {
+  MyOSLog("%s\n", "Hello");
+  MyOSLog("%d");    // expected-warning {{more '%' conversions than data arguments}}
+  MyOSLog("%P", p); // expected-warning {{using '%P' format specifier without precision}}
+}
Index: clang/test/CodeGenObjC/os_log.m
===================================================================
--- /dev/null
+++ clang/test/CodeGenObjC/os_log.m
@@ -0,0 +1,39 @@
+// RUN: %clang_cc1 %s -emit-llvm -o - -triple x86_64-darwin-apple -fobjc-arc -O2 | FileCheck %s
+
+// Make sure we emit clang.arc.use before calling objc_release as part of the
+// cleanup. This way we make sure the object will not be released until the
+// end of the full expression.
+
+// rdar://problem/24528966
+
+@class NSString;
+extern __attribute__((visibility("default"))) NSString *GenString();
+
+// Behavior of __builtin_os_log differs between platforms, so only test on X86
+#ifdef __x86_64__
+// CHECK-LABEL: define i8* @test_builtin_os_log
+void *test_builtin_os_log(void *buf) {
+  return __builtin_os_log_format(buf, "capabilities: %@", GenString());
+
+  // CHECK: store i8 2, i8*
+  // CHECK: [[NUM_ARGS:%.*]] = getelementptr i8, i8* {{.*}}, i64 1
+  // CHECK: store i8 1, i8* [[NUM_ARGS]]
+  //
+  // CHECK: [[ARG1_DESC:%.*]] = getelementptr i8, i8* {{.*}}, i64 2
+  // CHECK: store i8 64, i8* [[ARG1_DESC]]
+  // CHECK: [[ARG1_SIZE:%.*]] = getelementptr i8, i8* {{.*}}, i64 3
+  // CHECK: store i8 8, i8* [[ARG1_SIZE]]
+  // CHECK: [[ARG1:%.*]] = getelementptr i8, i8* {{.*}}, i64 4
+  // CHECK: [[ARG1_CAST:%.*]] = bitcast i8* [[ARG1]] to
+
+  // CHECK: [[STRING:%.*]] = {{.*}} call {{.*}} @GenString()
+  // CHECK: [[STRING_CAST:%.*]] = bitcast {{.*}} [[STRING]] to
+  // CHECK: call {{.*}} @objc_retainAutoreleasedReturnValue(i8* [[STRING_CAST]])
+  // CHECK: store {{.*}} [[STRING]], {{.*}} [[ARG1_CAST]]
+
+  // CHECK: call void (...) @clang.arc.use({{.*}} [[STRING]])
+  // CHECK: call void @objc_release(i8* [[STRING_CAST]])
+  // CHECK: ret i8*
+}
+
+#endif
Index: clang/test/CodeGen/builtins.c
===================================================================
--- clang/test/CodeGen/builtins.c
+++ clang/test/CodeGen/builtins.c
@@ -371,3 +371,145 @@
   // CHECK: call i64 @llvm.readcyclecounter()
   return __builtin_readcyclecounter();
 }
+
+// Behavior of __builtin_os_log differs between platforms, so only test on X86
+#ifdef __x86_64__
+// CHECK-LABEL: define void @test_builtin_os_log
+// CHECK: (i8* [[BUF:%.*]], i32 [[I:%.*]], i8* [[DATA:%.*]])
+void test_builtin_os_log(void *buf, int i, const char *data) {
+  volatile int len;
+  // CHECK: store i8* [[BUF]], i8** [[BUF_ADDR:%.*]], align 8
+  // CHECK: store i32 [[I]], i32* [[I_ADDR:%.*]], align 4
+  // CHECK: store i8* [[DATA]], i8** [[DATA_ADDR:%.*]], align 8
+
+  // CHECK: store volatile i32 34
+  len = __builtin_os_log_format_buffer_size("%d %{public}s %{private}.16P", i, data, data);
+
+  // CHECK: [[BUF2:%.*]] = load i8*, i8** [[BUF_ADDR]]
+  // CHECK: [[SUMMARY:%.*]] = getelementptr i8, i8* [[BUF2]], i64 0
+  // CHECK: store i8 3, i8* [[SUMMARY]]
+  // CHECK: [[NUM_ARGS:%.*]] = getelementptr i8, i8* [[BUF2]], i64 1
+  // CHECK: store i8 4, i8* [[NUM_ARGS]]
+  //
+  // CHECK: [[ARG1_DESC:%.*]] = getelementptr i8, i8* [[BUF2]], i64 2
+  // CHECK: store i8 0, i8* [[ARG1_DESC]]
+  // CHECK: [[ARG1_SIZE:%.*]] = getelementptr i8, i8* [[BUF2]], i64 3
+  // CHECK: store i8 4, i8* [[ARG1_SIZE]]
+  // CHECK: [[ARG1:%.*]] = getelementptr i8, i8* [[BUF2]], i64 4
+  // CHECK: [[ARG1_INT:%.*]] = bitcast i8* [[ARG1]] to i32*
+  // CHECK: [[I2:%.*]] = load i32, i32* [[I_ADDR]]
+  // CHECK: store i32 [[I2]], i32* [[ARG1_INT]]
+
+  // CHECK: [[ARG2_DESC:%.*]] = getelementptr i8, i8* [[BUF2]], i64 8
+  // CHECK: store i8 34, i8* [[ARG2_DESC]]
+  // CHECK: [[ARG2_SIZE:%.*]] = getelementptr i8, i8* [[BUF2]], i64 9
+  // CHECK: store i8 8, i8* [[ARG2_SIZE]]
+  // CHECK: [[ARG2:%.*]] = getelementptr i8, i8* [[BUF2]], i64 10
+  // CHECK: [[ARG2_PTR:%.*]] = bitcast i8* [[ARG2]] to i8**
+  // CHECK: [[DATA2:%.*]] = load i8*, i8** [[DATA_ADDR]]
+  // CHECK: store i8* [[DATA2]], i8** [[ARG2_PTR]]
+
+  // CHECK: [[ARG3_DESC:%.*]] = getelementptr i8, i8* [[BUF2]], i64 18
+  // CHECK: store i8 17, i8* [[ARG3_DESC]]
+  // CHECK: [[ARG3_SIZE:%.*]] = getelementptr i8, i8* [[BUF2]], i64 19
+  // CHECK: store i8 4, i8* [[ARG3_SIZE]]
+  // CHECK: [[ARG3:%.*]] = getelementptr i8, i8* [[BUF2]], i64 20
+  // CHECK: [[ARG3_INT:%.*]] = bitcast i8* [[ARG3]] to i32*
+  // CHECK: store i32 16, i32* [[ARG3_INT]]
+
+  // CHECK: [[ARG4_DESC:%.*]] = getelementptr i8, i8* [[BUF2]], i64 24
+  // CHECK: store i8 49, i8* [[ARG4_DESC]]
+  // CHECK: [[ARG4_SIZE:%.*]] = getelementptr i8, i8* [[BUF2]], i64 25
+  // CHECK: store i8 8, i8* [[ARG4_SIZE]]
+  // CHECK: [[ARG4:%.*]] = getelementptr i8, i8* [[BUF2]], i64 26
+  // CHECK: [[ARG4_PTR:%.*]] = bitcast i8* [[ARG4]] to i8**
+  // CHECK: [[DATA3:%.*]] = load i8*, i8** [[DATA_ADDR]]
+  // CHECK: store i8* [[DATA3]], i8** [[ARG4_PTR]]
+
+  __builtin_os_log_format(buf, "%d %{public}s %{private}.16P", i, data, data);
+}
+
+// CHECK-LABEL: define void @test_builtin_os_log_errno
+// CHECK: (i8* [[BUF:%.*]], i8* [[DATA:%.*]])
+void test_builtin_os_log_errno(void *buf, const char *data) {
+  volatile int len;
+  // CHECK: store i8* [[BUF]], i8** [[BUF_ADDR:%.*]], align 8
+  // CHECK: store i8* [[DATA]], i8** [[DATA_ADDR:%.*]], align 8
+
+  // CHECK: store volatile i32 2
+  len = __builtin_os_log_format_buffer_size("%S");
+
+  // CHECK: [[BUF2:%.*]] = load i8*, i8** [[BUF_ADDR]]
+  // CHECK: [[SUMMARY:%.*]] = getelementptr i8, i8* [[BUF2]], i64 0
+  // CHECK: store i8 2, i8* [[SUMMARY]]
+  // CHECK: [[NUM_ARGS:%.*]] = getelementptr i8, i8* [[BUF2]], i64 1
+  // CHECK: store i8 1, i8* [[NUM_ARGS]]
+
+  // CHECK: [[ARG1_DESC:%.*]] = getelementptr i8, i8* [[BUF2]], i64 2
+  // CHECK: store i8 96, i8* [[ARG1_DESC]]
+  // CHECK: [[ARG1_SIZE:%.*]] = getelementptr i8, i8* [[BUF2]], i64 3
+  // CHECK: store i8 0, i8* [[ARG1_SIZE]]
+  // CHECK: [[ARG1:%.*]] = getelementptr i8, i8* [[BUF2]], i64 4
+  // CHECK: [[ARG1_INT:%.*]] = bitcast i8* [[ARG1]] to i32*
+  // CHECK: store i32 0, i32* [[ARG1_INT]]
+
+  __builtin_os_log_format(buf, "%m");
+}
+
+// CHECK-LABEL: define void @test_builtin_os_log_wide
+// CHECK: (i8* [[BUF:%.*]], i8* [[DATA:%.*]], i32* [[STR:%.*]])
+typedef int wchar_t;
+void test_builtin_os_log_wide(void *buf, const char *data, wchar_t *str) {
+  volatile int len;
+  // CHECK: store i8* [[BUF]], i8** [[BUF_ADDR:%.*]], align 8
+  // CHECK: store i8* [[DATA]], i8** [[DATA_ADDR:%.*]], align 8
+  // CHECK: store i32* [[STR]], i32** [[STR_ADDR:%.*]],
+
+  // CHECK: store volatile i32 12
+  len = __builtin_os_log_format_buffer_size("%S", str);
+
+  // CHECK: [[BUF2:%.*]] = load i8*, i8** [[BUF_ADDR]]
+  // CHECK: [[SUMMARY:%.*]] = getelementptr i8, i8* [[BUF2]], i64 0
+  // CHECK: store i8 2, i8* [[SUMMARY]]
+  // CHECK: [[NUM_ARGS:%.*]] = getelementptr i8, i8* [[BUF2]], i64 1
+  // CHECK: store i8 1, i8* [[NUM_ARGS]]
+
+  // CHECK: [[ARG1_DESC:%.*]] = getelementptr i8, i8* [[BUF2]], i64 2
+  // CHECK: store i8 80, i8* [[ARG1_DESC]]
+  // CHECK: [[ARG1_SIZE:%.*]] = getelementptr i8, i8* [[BUF2]], i64 3
+  // CHECK: store i8 8, i8* [[ARG1_SIZE]]
+  // CHECK: [[ARG1:%.*]] = getelementptr i8, i8* [[BUF2]], i64 4
+  // CHECK: [[ARG1_PTR:%.*]] = bitcast i8* [[ARG1]] to i32**
+  // CHECK: [[STR2:%.*]] = load i32*, i32** [[STR_ADDR]]
+  // CHECK: store i32* [[STR2]], i32** [[ARG1_PTR]]
+
+  __builtin_os_log_format(buf, "%S", str);
+}
+
+// CHECK-LABEL: define void @test_builtin_os_log_percent
+// Check that the %% which does not consume any argument is correctly handled
+void test_builtin_os_log_percent(void *buf, const char *data) {
+  volatile int len;
+  // CHECK: store i8* [[BUF]], i8** [[BUF_ADDR:%.*]], align 8
+  // CHECK: store i8* [[DATA]], i8** [[DATA_ADDR:%.*]], align 8
+  // CHECK: store volatile i32 12
+  len = __builtin_os_log_format_buffer_size("%s %%", data);
+
+  // CHECK: [[BUF2:%.*]] = load i8*, i8** [[BUF_ADDR]]
+  // CHECK: [[SUMMARY:%.*]] = getelementptr i8, i8* [[BUF2]], i64 0
+  // CHECK: store i8 2, i8* [[SUMMARY]]
+  // CHECK: [[NUM_ARGS:%.*]] = getelementptr i8, i8* [[BUF2]], i64 1
+  // CHECK: store i8 1, i8* [[NUM_ARGS]]
+  //
+  // CHECK: [[ARG1_DESC:%.*]] = getelementptr i8, i8* [[BUF2]], i64 2
+  // CHECK: store i8 32, i8* [[ARG1_DESC]]
+  // CHECK: [[ARG1_SIZE:%.*]] = getelementptr i8, i8* [[BUF2]], i64 3
+  // CHECK: store i8 8, i8* [[ARG1_SIZE]]
+  // CHECK: [[ARG1:%.*]] = getelementptr i8, i8* [[BUF2]], i64 4
+  // CHECK: [[ARG1_PTR:%.*]] = bitcast i8* [[ARG1]] to i8**
+  // CHECK: [[DATA2:%.*]] = load i8*, i8** [[DATA_ADDR]]
+  // CHECK: store i8* [[DATA2]], i8** [[ARG1_PTR]]
+  __builtin_os_log_format(buf, "%s %%", data);
+}
+
+#endif
Index: clang/lib/Sema/SemaDeclAttr.cpp
===================================================================
--- clang/lib/Sema/SemaDeclAttr.cpp
+++ clang/lib/Sema/SemaDeclAttr.cpp
@@ -2804,20 +2804,21 @@
 /// types.
 static FormatAttrKind getFormatAttrKind(StringRef Format) {
   return llvm::StringSwitch<FormatAttrKind>(Format)
-    // Check for formats that get handled specially.
-    .Case("NSString", NSStringFormat)
-    .Case("CFString", CFStringFormat)
-    .Case("strftime", StrftimeFormat)
-
-    // Otherwise, check for supported formats.
-    .Cases("scanf", "printf", "printf0", "strfmon", SupportedFormat)
-    .Cases("cmn_err", "vcmn_err", "zcmn_err", SupportedFormat)
-    .Case("kprintf", SupportedFormat) // OpenBSD.
-    .Case("freebsd_kprintf", SupportedFormat) // FreeBSD.
-    .Case("os_trace", SupportedFormat)
-
-    .Cases("gcc_diag", "gcc_cdiag", "gcc_cxxdiag", "gcc_tdiag", IgnoredFormat)
-    .Default(InvalidFormat);
+      // Check for formats that get handled specially.
+      .Case("NSString", NSStringFormat)
+      .Case("CFString", CFStringFormat)
+      .Case("strftime", StrftimeFormat)
+
+      // Otherwise, check for supported formats.
+      .Cases("scanf", "printf", "printf0", "strfmon", SupportedFormat)
+      .Cases("cmn_err", "vcmn_err", "zcmn_err", SupportedFormat)
+      .Case("kprintf", SupportedFormat)         // OpenBSD.
+      .Case("freebsd_kprintf", SupportedFormat) // FreeBSD.
+      .Case("os_trace", SupportedFormat)
+      .Case("os_log", SupportedFormat)
+
+      .Cases("gcc_diag", "gcc_cdiag", "gcc_cxxdiag", "gcc_tdiag", IgnoredFormat)
+      .Default(InvalidFormat);
 }
 
 /// Handle __attribute__((init_priority(priority))) attributes based on
Index: clang/lib/Sema/SemaChecking.cpp
===================================================================
--- clang/lib/Sema/SemaChecking.cpp
+++ clang/lib/Sema/SemaChecking.cpp
@@ -1065,6 +1065,12 @@
   case Builtin::BIget_kernel_preferred_work_group_size_multiple:
     if (SemaOpenCLBuiltinKernelWorkGroupSize(*this, TheCall))
       return ExprError();
+  case Builtin::BI__builtin_os_log_format:
+  case Builtin::BI__builtin_os_log_format_buffer_size:
+    if (SemaBuiltinOSLogFormat(TheCall)) {
+      return ExprError();
+    }
+    break;
   }
 
   // Since the target specific builtins for each arch overlap, only check those
@@ -3480,6 +3486,31 @@
   return false;
 }
 
+/// CheckObjCString - Checks that the format string argument to the os_log()
+/// and os_trace() functions is correct, and converts it to const char *.
+ExprResult Sema::CheckOSLogFormatStringArg(Expr *Arg) {
+  Arg = Arg->IgnoreParenCasts();
+  auto *Literal = dyn_cast<StringLiteral>(Arg);
+  if (!Literal) {
+    if (auto *ObjcLiteral = dyn_cast<ObjCStringLiteral>(Arg)) {
+      Literal = ObjcLiteral->getString();
+    }
+  }
+
+  if (!Literal || (!Literal->isAscii() && !Literal->isUTF8())) {
+    return ExprError(
+        Diag(Arg->getLocStart(), diag::err_os_log_format_not_string_constant)
+        << Arg->getSourceRange());
+  }
+
+  ExprResult Result(Literal);
+  QualType ResultTy = Context.getPointerType(Context.CharTy.withConst());
+  InitializedEntity Entity =
+      InitializedEntity::InitializeParameter(Context, ResultTy, false);
+  Result = PerformCopyInitialization(Entity, SourceLocation(), Result);
+  return Result;
+}
+
 /// Check the arguments to '__builtin_va_start' or '__builtin_ms_va_start'
 /// for validity.  Emit an error and return true on failure; return false
 /// on success.
@@ -3941,6 +3972,86 @@
   return false;
 }
 
+bool Sema::SemaBuiltinOSLogFormat(CallExpr *TheCall) {
+  unsigned BuiltinID =
+      cast<FunctionDecl>(TheCall->getCalleeDecl())->getBuiltinID();
+  bool IsSizeCall = BuiltinID == Builtin::BI__builtin_os_log_format_buffer_size;
+
+  unsigned NumArgs = TheCall->getNumArgs();
+  unsigned NumRequiredArgs = IsSizeCall ? 1 : 2;
+  if (NumArgs < NumRequiredArgs) {
+    return Diag(TheCall->getLocEnd(), diag::err_typecheck_call_too_few_args)
+           << 0 /* function call */ << NumRequiredArgs << NumArgs
+           << TheCall->getSourceRange();
+  }
+  if (NumArgs >= NumRequiredArgs + 0x100) {
+    return Diag(TheCall->getLocEnd(),
+                diag::err_typecheck_call_too_many_args_at_most)
+           << 0 /* function call */ << (NumRequiredArgs + 0xff) << NumArgs
+           << TheCall->getSourceRange();
+  }
+  unsigned i = 0;
+
+  // For formatting call, check buffer arg.
+  if (!IsSizeCall) {
+    ExprResult Arg(TheCall->getArg(i));
+    InitializedEntity Entity = InitializedEntity::InitializeParameter(
+        Context, Context.VoidPtrTy, false);
+    Arg = PerformCopyInitialization(Entity, SourceLocation(), Arg);
+    if (Arg.isInvalid())
+      return true;
+    TheCall->setArg(i, Arg.get());
+    i++;
+  }
+
+  // Check string literal arg.
+  unsigned FormatIdx = i;
+  {
+    ExprResult Arg = CheckOSLogFormatStringArg(TheCall->getArg(i));
+    if (Arg.isInvalid())
+      return true;
+    TheCall->setArg(i, Arg.get());
+    i++;
+  }
+
+  // Make sure variadic args are scalar.
+  unsigned FirstDataArg = i;
+  while (i < NumArgs) {
+    ExprResult Arg = DefaultVariadicArgumentPromotion(
+        TheCall->getArg(i), VariadicFunction, nullptr);
+    if (Arg.isInvalid())
+      return true;
+    CharUnits ArgSize = Context.getTypeSizeInChars(Arg.get()->getType());
+    if (ArgSize.getQuantity() >= 0x100) {
+      return Diag(Arg.get()->getLocEnd(), diag::err_os_log_argument_too_big)
+             << i << (int)ArgSize.getQuantity() << 0xff
+             << TheCall->getSourceRange();
+    }
+    TheCall->setArg(i, Arg.get());
+    i++;
+  }
+
+  // Check formatting specifiers. NOTE: We're only doing this for the non-size
+  // call to avoid duplicate diagnostics.
+  if (!IsSizeCall) {
+    llvm::SmallBitVector CheckedVarArgs(NumArgs, false);
+    ArrayRef<const Expr *> Args(TheCall->getArgs(), TheCall->getNumArgs());
+    bool Success = CheckFormatArguments(
+        Args, /*HasVAListArg*/ false, FormatIdx, FirstDataArg, FST_OSLog,
+        VariadicFunction, TheCall->getLocStart(), SourceRange(),
+        CheckedVarArgs);
+    if (!Success)
+      return true;
+  }
+
+  if (IsSizeCall) {
+    TheCall->setType(Context.getSizeType());
+  } else {
+    TheCall->setType(Context.VoidPtrTy);
+  }
+  return false;
+}
+
 /// SemaBuiltinConstantArg - Handle a check if argument ArgNum of CallExpr
 /// TheCall is a constant expression.
 bool Sema::SemaBuiltinConstantArg(CallExpr *TheCall, int ArgNum,
@@ -4557,15 +4668,16 @@
 
 Sema::FormatStringType Sema::GetFormatStringType(const FormatAttr *Format) {
   return llvm::StringSwitch<FormatStringType>(Format->getType()->getName())
-  .Case("scanf", FST_Scanf)
-  .Cases("printf", "printf0", FST_Printf)
-  .Cases("NSString", "CFString", FST_NSString)
-  .Case("strftime", FST_Strftime)
-  .Case("strfmon", FST_Strfmon)
-  .Cases("kprintf", "cmn_err", "vcmn_err", "zcmn_err", FST_Kprintf)
-  .Case("freebsd_kprintf", FST_FreeBSDKPrintf)
-  .Case("os_trace", FST_OSTrace)
-  .Default(FST_Unknown);
+      .Case("scanf", FST_Scanf)
+      .Cases("printf", "printf0", FST_Printf)
+      .Cases("NSString", "CFString", FST_NSString)
+      .Case("strftime", FST_Strftime)
+      .Case("strfmon", FST_Strfmon)
+      .Cases("kprintf", "cmn_err", "vcmn_err", "zcmn_err", FST_Kprintf)
+      .Case("freebsd_kprintf", FST_FreeBSDKPrintf)
+      .Case("os_trace", FST_OSLog)
+      .Case("os_log", FST_OSLog)
+      .Default(FST_Unknown);
 }
 
 /// CheckFormatArguments - Check calls to printf and scanf (and similar
@@ -4675,6 +4787,7 @@
   Sema &S;
   const FormatStringLiteral *FExpr;
   const Expr *OrigFormatExpr;
+  const Sema::FormatStringType FSType;
   const unsigned FirstDataArg;
   const unsigned NumDataArgs;
   const char *Beg; // Start of format string.
@@ -4691,20 +4804,19 @@
 
 public:
   CheckFormatHandler(Sema &s, const FormatStringLiteral *fexpr,
-                     const Expr *origFormatExpr, unsigned firstDataArg,
+                     const Expr *origFormatExpr,
+                     const Sema::FormatStringType type, unsigned firstDataArg,
                      unsigned numDataArgs, const char *beg, bool hasVAListArg,
-                     ArrayRef<const Expr *> Args,
-                     unsigned formatIdx, bool inFunctionCall,
-                     Sema::VariadicCallType callType,
+                     ArrayRef<const Expr *> Args, unsigned formatIdx,
+                     bool inFunctionCall, Sema::VariadicCallType callType,
                      llvm::SmallBitVector &CheckedVarArgs,
                      UncoveredArgHandler &UncoveredArg)
-    : S(s), FExpr(fexpr), OrigFormatExpr(origFormatExpr),
-      FirstDataArg(firstDataArg), NumDataArgs(numDataArgs),
-      Beg(beg), HasVAListArg(hasVAListArg),
-      Args(Args), FormatIdx(formatIdx),
-      usesPositionalArgs(false), atFirstArg(true),
-      inFunctionCall(inFunctionCall), CallType(callType),
-      CheckedVarArgs(CheckedVarArgs), UncoveredArg(UncoveredArg) {
+      : S(s), FExpr(fexpr), OrigFormatExpr(origFormatExpr), FSType(type),
+        FirstDataArg(firstDataArg), NumDataArgs(numDataArgs), Beg(beg),
+        HasVAListArg(hasVAListArg), Args(Args), FormatIdx(formatIdx),
+        usesPositionalArgs(false), atFirstArg(true),
+        inFunctionCall(inFunctionCall), CallType(callType),
+        CheckedVarArgs(CheckedVarArgs), UncoveredArg(UncoveredArg) {
     CoveredArgs.resize(numDataArgs);
     CoveredArgs.reset();
   }
@@ -5127,24 +5239,28 @@
 
 namespace {
 class CheckPrintfHandler : public CheckFormatHandler {
-  bool ObjCContext;
-
 public:
   CheckPrintfHandler(Sema &s, const FormatStringLiteral *fexpr,
-                     const Expr *origFormatExpr, unsigned firstDataArg,
-                     unsigned numDataArgs, bool isObjC,
-                     const char *beg, bool hasVAListArg,
-                     ArrayRef<const Expr *> Args,
+                     const Expr *origFormatExpr,
+                     const Sema::FormatStringType type, unsigned firstDataArg,
+                     unsigned numDataArgs, bool isObjC, const char *beg,
+                     bool hasVAListArg, ArrayRef<const Expr *> Args,
                      unsigned formatIdx, bool inFunctionCall,
                      Sema::VariadicCallType CallType,
                      llvm::SmallBitVector &CheckedVarArgs,
                      UncoveredArgHandler &UncoveredArg)
-    : CheckFormatHandler(s, fexpr, origFormatExpr, firstDataArg,
-                         numDataArgs, beg, hasVAListArg, Args,
-                         formatIdx, inFunctionCall, CallType, CheckedVarArgs,
-                         UncoveredArg),
-      ObjCContext(isObjC)
-  {}
+      : CheckFormatHandler(s, fexpr, origFormatExpr, type, firstDataArg,
+                           numDataArgs, beg, hasVAListArg, Args, formatIdx,
+                           inFunctionCall, CallType, CheckedVarArgs,
+                           UncoveredArg) {}
+
+  bool isObjCContext() const { return FSType == Sema::FST_NSString; }
+
+  /// Returns true if '%@' specifiers are allowed in the format string.
+  bool allowsObjCArg() const {
+    return FSType == Sema::FST_NSString || FSType == Sema::FST_OSLog ||
+           FSType == Sema::FST_OSTrace;
+  }
 
   bool HandleInvalidPrintfConversionSpecifier(
                                       const analyze_printf::PrintfSpecifier &FS,
@@ -5498,11 +5614,54 @@
 
   // Check for using an Objective-C specific conversion specifier
   // in a non-ObjC literal.
-  if (!ObjCContext && CS.isObjCArg()) {
+  if (!allowsObjCArg() && CS.isObjCArg()) {
+    return HandleInvalidPrintfConversionSpecifier(FS, startSpecifier,
+                                                  specifierLen);
+  }
+
+  // %P can only be used with os_log.
+  if (FSType != Sema::FST_OSLog && CS.getKind() == ConversionSpecifier::PArg) {
     return HandleInvalidPrintfConversionSpecifier(FS, startSpecifier,
                                                   specifierLen);
   }
 
+  // %n is not allowed with os_log.
+  if (FSType == Sema::FST_OSLog && CS.getKind() == ConversionSpecifier::nArg) {
+    EmitFormatDiagnostic(S.PDiag(diag::warn_os_log_format_narg),
+                         getLocationOfByte(CS.getStart()),
+                         /*IsStringLocation*/ false,
+                         getSpecifierRange(startSpecifier, specifierLen));
+
+    return true;
+  }
+
+  // Only scalars are allowed for os_trace.
+  if (FSType == Sema::FST_OSTrace &&
+      (CS.getKind() == ConversionSpecifier::PArg ||
+       CS.getKind() == ConversionSpecifier::sArg ||
+       CS.getKind() == ConversionSpecifier::ObjCObjArg)) {
+    return HandleInvalidPrintfConversionSpecifier(FS, startSpecifier,
+                                                  specifierLen);
+  }
+
+  // Check for use of public/private annotation outside of os_log().
+  if (FSType != Sema::FST_OSLog) {
+    if (FS.isPublic().isSet()) {
+      EmitFormatDiagnostic(S.PDiag(diag::warn_format_invalid_annotation)
+                               << "public",
+                           getLocationOfByte(FS.isPublic().getPosition()),
+                           /*IsStringLocation*/ false,
+                           getSpecifierRange(startSpecifier, specifierLen));
+    }
+    if (FS.isPrivate().isSet()) {
+      EmitFormatDiagnostic(S.PDiag(diag::warn_format_invalid_annotation)
+                               << "private",
+                           getLocationOfByte(FS.isPrivate().getPosition()),
+                           /*IsStringLocation*/ false,
+                           getSpecifierRange(startSpecifier, specifierLen));
+    }
+  }
+
   // Check for invalid use of field width
   if (!FS.hasValidFieldWidth()) {
     HandleInvalidAmount(FS, FS.getFieldWidth(), /* field width */ 0,
@@ -5515,6 +5674,15 @@
         startSpecifier, specifierLen);
   }
 
+  // Precision is mandatory for %P specifier.
+  if (CS.getKind() == ConversionSpecifier::PArg &&
+      FS.getPrecision().getHowSpecified() == OptionalAmount::NotSpecified) {
+    EmitFormatDiagnostic(S.PDiag(diag::warn_format_P_no_precision),
+                         getLocationOfByte(startSpecifier),
+                         /*IsStringLocation*/ false,
+                         getSpecifierRange(startSpecifier, specifierLen));
+  }
+
   // Check each flag does not conflict with any other component.
   if (!FS.hasValidThousandsGroupingPrefix())
     HandleFlag(FS, FS.hasThousandsGrouping(), startSpecifier, specifierLen);
@@ -5664,8 +5832,7 @@
   using namespace analyze_printf;
   // Now type check the data expression that matches the
   // format specifier.
-  const analyze_printf::ArgType &AT = FS.getArgType(S.Context,
-                                                    ObjCContext);
+  const analyze_printf::ArgType &AT = FS.getArgType(S.Context, isObjCContext());
   if (!AT.isValid())
     return true;
 
@@ -5720,7 +5887,7 @@
   // If the argument is an integer of some kind, believe the %C and suggest
   // a cast instead of changing the conversion specifier.
   QualType IntendedTy = ExprTy;
-  if (ObjCContext &&
+  if (isObjCContext() &&
       FS.getConversionSpecifier().getKind() == ConversionSpecifier::CArg) {
     if (ExprTy->isIntegralOrUnscopedEnumerationType() &&
         !ExprTy->isCharType()) {
@@ -5761,8 +5928,8 @@
 
   // We may be able to offer a FixItHint if it is a supported type.
   PrintfSpecifier fixedFS = FS;
-  bool success = fixedFS.fixType(IntendedTy, S.getLangOpts(),
-                                 S.Context, ObjCContext);
+  bool success =
+      fixedFS.fixType(IntendedTy, S.getLangOpts(), S.Context, isObjCContext());
 
   if (success) {
     // Get the fix string from the fixed format specifier
@@ -5918,19 +6085,18 @@
 class CheckScanfHandler : public CheckFormatHandler {
 public:
   CheckScanfHandler(Sema &s, const FormatStringLiteral *fexpr,
-                    const Expr *origFormatExpr, unsigned firstDataArg,
-                    unsigned numDataArgs, const char *beg, bool hasVAListArg,
-                    ArrayRef<const Expr *> Args,
-                    unsigned formatIdx, bool inFunctionCall,
-                    Sema::VariadicCallType CallType,
+                    const Expr *origFormatExpr, Sema::FormatStringType type,
+                    unsigned firstDataArg, unsigned numDataArgs,
+                    const char *beg, bool hasVAListArg,
+                    ArrayRef<const Expr *> Args, unsigned formatIdx,
+                    bool inFunctionCall, Sema::VariadicCallType CallType,
                     llvm::SmallBitVector &CheckedVarArgs,
                     UncoveredArgHandler &UncoveredArg)
-    : CheckFormatHandler(s, fexpr, origFormatExpr, firstDataArg,
-                         numDataArgs, beg, hasVAListArg,
-                         Args, formatIdx, inFunctionCall, CallType,
-                         CheckedVarArgs, UncoveredArg)
-  {}
-  
+      : CheckFormatHandler(s, fexpr, origFormatExpr, type, firstDataArg,
+                           numDataArgs, beg, hasVAListArg, Args, formatIdx,
+                           inFunctionCall, CallType, CheckedVarArgs,
+                           UncoveredArg) {}
+
   bool HandleScanfSpecifier(const analyze_scanf::ScanfSpecifier &FS,
                             const char *startSpecifier,
                             unsigned specifierLen) override;
@@ -6140,24 +6306,23 @@
   }
 
   if (Type == Sema::FST_Printf || Type == Sema::FST_NSString ||
-      Type == Sema::FST_FreeBSDKPrintf || Type == Sema::FST_OSTrace) {
-    CheckPrintfHandler H(S, FExpr, OrigFormatExpr, firstDataArg,
-                         numDataArgs, (Type == Sema::FST_NSString ||
-                                       Type == Sema::FST_OSTrace),
-                         Str, HasVAListArg, Args, format_idx,
-                         inFunctionCall, CallType, CheckedVarArgs,
-                         UncoveredArg);
+      Type == Sema::FST_FreeBSDKPrintf || Type == Sema::FST_OSLog ||
+      Type == Sema::FST_OSTrace) {
+    CheckPrintfHandler H(
+        S, FExpr, OrigFormatExpr, Type, firstDataArg, numDataArgs,
+        (Type == Sema::FST_NSString || Type == Sema::FST_OSTrace), Str,
+        HasVAListArg, Args, format_idx, inFunctionCall, CallType,
+        CheckedVarArgs, UncoveredArg);
 
     if (!analyze_format_string::ParsePrintfString(H, Str, Str + StrLen,
                                                   S.getLangOpts(),
                                                   S.Context.getTargetInfo(),
                                             Type == Sema::FST_FreeBSDKPrintf))
       H.DoneProcessing();
   } else if (Type == Sema::FST_Scanf) {
-    CheckScanfHandler H(S, FExpr, OrigFormatExpr, firstDataArg, numDataArgs,
-                        Str, HasVAListArg, Args, format_idx,
-                        inFunctionCall, CallType, CheckedVarArgs,
-                        UncoveredArg);
+    CheckScanfHandler H(S, FExpr, OrigFormatExpr, Type, firstDataArg,
+                        numDataArgs, Str, HasVAListArg, Args, format_idx,
+                        inFunctionCall, CallType, CheckedVarArgs, UncoveredArg);
 
     if (!analyze_format_string::ParseScanfString(H, Str, Str + StrLen,
                                                  S.getLangOpts(),
Index: clang/lib/CodeGen/CGBuiltin.cpp
===================================================================
--- clang/lib/CodeGen/CGBuiltin.cpp
+++ clang/lib/CodeGen/CGBuiltin.cpp
@@ -11,14 +11,15 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "CodeGenFunction.h"
 #include "CGCXXABI.h"
 #include "CGObjCRuntime.h"
 #include "CGOpenCLRuntime.h"
+#include "CodeGenFunction.h"
 #include "CodeGenModule.h"
 #include "TargetInfo.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Decl.h"
+#include "clang/Analysis/Analyses/OSLog.h"
 #include "clang/Basic/TargetBuiltins.h"
 #include "clang/Basic/TargetInfo.h"
 #include "clang/CodeGen/CGFunctionInfo.h"
@@ -564,6 +565,18 @@
   llvm_unreachable("Incorrect MSVC intrinsic!");
 }
 
+namespace {
+// ARC cleanup for __builtin_os_log_format
+struct CallObjCArcUse final : EHScopeStack::Cleanup {
+  CallObjCArcUse(llvm::Value *object) : object(object) {}
+  llvm::Value *object;
+
+  void Emit(CodeGenFunction &CGF, Flags flags) override {
+    CGF.EmitARCIntrinsicUse(object);
+  }
+};
+}
+
 RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD,
                                         unsigned BuiltinID, const CallExpr *E,
                                         ReturnValueSlot ReturnValue) {
@@ -2597,6 +2610,76 @@
     // Fall through - it's already mapped to the intrinsic by GCCBuiltin.
     break;
   }
+  case Builtin::BI__builtin_os_log_format: {
+    assert(E->getNumArgs() >= 2 &&
+           "__builtin_os_log_format takes at least 2 arguments");
+    analyze_os_log::OSLogBufferLayout Layout;
+    analyze_os_log::computeOSLogBufferLayout(CGM.getContext(), E, Layout);
+    Address BufAddr = EmitPointerWithAlignment(E->getArg(0));
+    // Ignore argument 1, the format string. It is not currently used.
+    CharUnits Offset;
+    Builder.CreateStore(
+        Builder.getInt8(Layout.getSummaryByte()),
+        Builder.CreateConstByteGEP(BufAddr, Offset++, "summary"));
+    Builder.CreateStore(
+        Builder.getInt8(Layout.getNumArgsByte()),
+        Builder.CreateConstByteGEP(BufAddr, Offset++, "numArgs"));
+
+    llvm::SmallVector<llvm::Value *, 4> RetainableOperands;
+    for (const auto &Item : Layout.Items) {
+      Builder.CreateStore(
+          Builder.getInt8(Item.getDescriptorByte()),
+          Builder.CreateConstByteGEP(BufAddr, Offset++, "argDescriptor"));
+      Builder.CreateStore(
+          Builder.getInt8(Item.getSizeByte()),
+          Builder.CreateConstByteGEP(BufAddr, Offset++, "argSize"));
+      Address Addr = Builder.CreateConstByteGEP(BufAddr, Offset);
+      if (const Expr *TheExpr = Item.getExpr()) {
+        Addr = Builder.CreateElementBitCast(
+            Addr, ConvertTypeForMem(TheExpr->getType()));
+        // Check if this is a retainable type.
+        if (TheExpr->getType()->isObjCRetainableType()) {
+          assert(getEvaluationKind(TheExpr->getType()) == TEK_Scalar &&
+                 "Only scalar can be a ObjC retainable type");
+          llvm::Value *SV = EmitScalarExpr(TheExpr, /*Ignore*/ false);
+          RValue RV = RValue::get(SV);
+          LValue LV = MakeAddrLValue(Addr, TheExpr->getType());
+          EmitStoreThroughLValue(RV, LV);
+          // Check if the object is constant, if not, save it in
+          // RetainableOperands.
+          if (!isa<Constant>(SV))
+            RetainableOperands.push_back(SV);
+        } else {
+          EmitAnyExprToMem(TheExpr, Addr, Qualifiers(), /*isInit*/ true);
+        }
+      } else {
+        Addr = Builder.CreateElementBitCast(Addr, Int32Ty);
+        Builder.CreateStore(
+            Builder.getInt32(Item.getConstValue().getQuantity()), Addr);
+      }
+      Offset += Item.getSize();
+    }
+
+    // Push a clang.arc.use cleanup for each object in RetainableOperands. The
+    // cleanup will cause the use to appear after the final log call, keeping
+    // the object valid while it’s held in the log buffer.  Note that if there’s
+    // a release cleanup on the object, it will already be active; since
+    // cleanups are emitted in reverse order, the use will occur before the
+    // object is released.
+    if (!RetainableOperands.empty() && getLangOpts().ObjCAutoRefCount &&
+        CGM.getCodeGenOpts().OptimizationLevel != 0)
+      for (llvm::Value *object : RetainableOperands)
+        pushFullExprCleanup<CallObjCArcUse>(getARCCleanupKind(), object);
+
+    return RValue::get(BufAddr.getPointer());
+  }
+
+  case Builtin::BI__builtin_os_log_format_buffer_size: {
+    analyze_os_log::OSLogBufferLayout Layout;
+    analyze_os_log::computeOSLogBufferLayout(CGM.getContext(), E, Layout);
+    return RValue::get(ConstantInt::get(ConvertType(E->getType()),
+                                        Layout.getSize().getQuantity()));
+  }
   }
 
   // If this is an alias for a lib function (e.g. __builtin_sin), emit
Index: clang/lib/Analysis/PrintfFormatString.cpp
===================================================================
--- clang/lib/Analysis/PrintfFormatString.cpp
+++ clang/lib/Analysis/PrintfFormatString.cpp
@@ -119,6 +119,39 @@
     return true;
   }
 
+  const char *OSLogVisibilityFlagsStart = nullptr,
+             *OSLogVisibilityFlagsEnd = nullptr;
+  if (*I == '{') {
+    OSLogVisibilityFlagsStart = I++;
+    // Find the end of the modifier.
+    while (I != E && *I != '}') {
+      I++;
+    }
+    if (I == E) {
+      if (Warn)
+        H.HandleIncompleteSpecifier(Start, E - Start);
+      return true;
+    }
+    assert(*I == '}');
+    OSLogVisibilityFlagsEnd = I++;
+
+    // Just see if 'private' or 'public' is the first word. os_log itself will
+    // do any further parsing.
+    const char *P = OSLogVisibilityFlagsStart + 1;
+    while (P < OSLogVisibilityFlagsEnd && isspace(*P))
+      P++;
+    const char *WordStart = P;
+    while (P < OSLogVisibilityFlagsEnd && (isalnum(*P) || *P == '_'))
+      P++;
+    const char *WordEnd = P;
+    StringRef Word(WordStart, WordEnd - WordStart);
+    if (Word == "private") {
+      FS.setIsPrivate(WordStart);
+    } else if (Word == "public") {
+      FS.setIsPublic(WordStart);
+    }
+  }
+
   // Look for flags (if any).
   bool hasMore = true;
   for ( ; I != E; ++I) {
@@ -253,6 +286,10 @@
     // POSIX specific.
     case 'C': k = ConversionSpecifier::CArg; break;
     case 'S': k = ConversionSpecifier::SArg; break;
+    // Apple extension for os_log
+    case 'P':
+      k = ConversionSpecifier::PArg;
+      break;
     // Objective-C.
     case '@': k = ConversionSpecifier::ObjCObjArg; break;
     // Glibc specific.
@@ -301,7 +338,7 @@
                                            conversionPosition);
     return true;
   }
-  
+
   PrintfConversionSpecifier CS(conversionPosition, k);
   FS.setConversionSpecifier(CS);
   if (CS.consumesDataArgument() && !FS.usesPositionalArg())
@@ -541,6 +578,7 @@
         return Ctx.IntTy;
       return ArgType(Ctx.WideCharTy, "wchar_t");
     case ConversionSpecifier::pArg:
+    case ConversionSpecifier::PArg:
       return ArgType::CPointerTy;
     case ConversionSpecifier::ObjCObjArg:
       return ArgType::ObjCPointerTy;
@@ -900,7 +938,7 @@
   if (Precision.getHowSpecified() == OptionalAmount::NotSpecified)
     return true;
 
-  // Precision is only valid with the diouxXaAeEfFgGs conversions
+  // Precision is only valid with the diouxXaAeEfFgGsP conversions
   switch (CS.getKind()) {
   case ConversionSpecifier::dArg:
   case ConversionSpecifier::DArg:
@@ -922,6 +960,7 @@
   case ConversionSpecifier::sArg:
   case ConversionSpecifier::FreeBSDrArg:
   case ConversionSpecifier::FreeBSDyArg:
+  case ConversionSpecifier::PArg:
     return true;
 
   default:
Index: clang/lib/Analysis/OSLog.cpp
===================================================================
--- /dev/null
+++ clang/lib/Analysis/OSLog.cpp
@@ -0,0 +1,177 @@
+// TODO: header template
+
+#include "clang/Analysis/Analyses/OSLog.h"
+#include "clang/AST/Attr.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/ExprObjC.h"
+#include "clang/Analysis/Analyses/FormatString.h"
+#include "clang/Basic/Builtins.h"
+#include "llvm/ADT/SmallBitVector.h"
+
+using namespace clang;
+using llvm::APInt;
+
+using clang::analyze_os_log::OSLogBufferItem;
+using clang::analyze_os_log::OSLogBufferLayout;
+
+class OSLogFormatStringHandler
+    : public analyze_format_string::FormatStringHandler {
+private:
+  struct ArgData {
+    const Expr *E = nullptr;
+    Optional<OSLogBufferItem::Kind> Kind;
+    Optional<unsigned> Size;
+    unsigned char Flags = 0;
+  };
+  SmallVector<ArgData, 4> ArgsData;
+  ArrayRef<const Expr *> Args;
+
+  OSLogBufferItem::Kind
+  getKind(analyze_format_string::ConversionSpecifier::Kind K) {
+    switch (K) {
+    case clang::analyze_format_string::ConversionSpecifier::sArg: // "%s"
+      return OSLogBufferItem::StringKind;
+    case clang::analyze_format_string::ConversionSpecifier::SArg: // "%S"
+      return OSLogBufferItem::WideStringKind;
+    case clang::analyze_format_string::ConversionSpecifier::PArg: { // "%P"
+      return OSLogBufferItem::PointerKind;
+    case clang::analyze_format_string::ConversionSpecifier::ObjCObjArg: // "%@"
+      return OSLogBufferItem::ObjCObjKind;
+    case clang::analyze_format_string::ConversionSpecifier::PrintErrno: // "%m"
+      return OSLogBufferItem::ErrnoKind;
+    default:
+      return OSLogBufferItem::ScalarKind;
+    }
+    }
+  }
+
+public:
+  OSLogFormatStringHandler(ArrayRef<const Expr *> Args) : Args(Args) {
+    ArgsData.reserve(Args.size());
+  }
+
+  virtual bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS,
+                                     const char *StartSpecifier,
+                                     unsigned SpecifierLen) {
+    if (!FS.consumesDataArgument() &&
+        FS.getConversionSpecifier().getKind() !=
+            clang::analyze_format_string::ConversionSpecifier::PrintErrno)
+      return false;
+
+    ArgsData.emplace_back();
+    unsigned ArgIndex = FS.getArgIndex();
+    if (ArgIndex < Args.size())
+      ArgsData.back().E = Args[ArgIndex];
+
+    // First get the Kind
+    ArgsData.back().Kind = getKind(FS.getConversionSpecifier().getKind());
+    if (ArgsData.back().Kind != OSLogBufferItem::ErrnoKind &&
+        !ArgsData.back().E) {
+      // missing argument
+      ArgsData.pop_back();
+      return false;
+    }
+
+    switch (FS.getConversionSpecifier().getKind()) {
+    case clang::analyze_format_string::ConversionSpecifier::sArg:   // "%s"
+    case clang::analyze_format_string::ConversionSpecifier::SArg: { // "%S"
+      auto &precision = FS.getPrecision();
+      switch (precision.getHowSpecified()) {
+      case clang::analyze_format_string::OptionalAmount::NotSpecified: // "%s"
+        break;
+      case clang::analyze_format_string::OptionalAmount::Constant: // "%.16s"
+        ArgsData.back().Size = precision.getConstantAmount();
+        break;
+      case clang::analyze_format_string::OptionalAmount::Arg: // "%.*s"
+        ArgsData.back().Kind = OSLogBufferItem::CountKind;
+        break;
+      case clang::analyze_format_string::OptionalAmount::Invalid:
+        return false;
+      }
+      break;
+    }
+    case clang::analyze_format_string::ConversionSpecifier::PArg: { // "%P"
+      auto &precision = FS.getPrecision();
+      switch (precision.getHowSpecified()) {
+      case clang::analyze_format_string::OptionalAmount::NotSpecified: // "%P"
+        return false; // length must be supplied with pointer format specifier
+      case clang::analyze_format_string::OptionalAmount::Constant: // "%.16P"
+        ArgsData.back().Size = precision.getConstantAmount();
+        break;
+      case clang::analyze_format_string::OptionalAmount::Arg: // "%.*P"
+        ArgsData.back().Kind = OSLogBufferItem::CountKind;
+        break;
+      case clang::analyze_format_string::OptionalAmount::Invalid:
+        return false;
+      }
+      break;
+    }
+    default:
+      break;
+    }
+
+    if (FS.isPrivate()) {
+      ArgsData.back().Flags |= OSLogBufferItem::IsPrivate;
+    }
+    if (FS.isPublic()) {
+      ArgsData.back().Flags |= OSLogBufferItem::IsPublic;
+    }
+    return true;
+  }
+
+  void computeLayout(ASTContext &Ctx, OSLogBufferLayout &Layout) const {
+    Layout.Items.clear();
+    for (auto &Data : ArgsData) {
+      if (Data.Size)
+        Layout.Items.emplace_back(Ctx, CharUnits::fromQuantity(*Data.Size),
+                                  Data.Flags);
+      if (Data.Kind) {
+        CharUnits Size;
+        if (*Data.Kind == OSLogBufferItem::ErrnoKind)
+          Size = CharUnits::Zero();
+        else
+          Size = Ctx.getTypeSizeInChars(Data.E->getType());
+        Layout.Items.emplace_back(*Data.Kind, Data.E, Size, Data.Flags);
+      } else {
+        auto Size = Ctx.getTypeSizeInChars(Data.E->getType());
+        Layout.Items.emplace_back(OSLogBufferItem::ScalarKind, Data.E, Size,
+                                  Data.Flags);
+      }
+    }
+  }
+};
+
+bool clang::analyze_os_log::computeOSLogBufferLayout(
+    ASTContext &Ctx, const CallExpr *E, OSLogBufferLayout &Layout) {
+  ArrayRef<const Expr *> Args(E->getArgs(), E->getArgs() + E->getNumArgs());
+
+  const Expr *StringArg;
+  ArrayRef<const Expr *> VarArgs;
+  switch (E->getBuiltinCallee()) {
+  case Builtin::BI__builtin_os_log_format_buffer_size:
+    assert(E->getNumArgs() >= 1 &&
+           "__builtin_os_log_format_buffer_size takes at least 1 argument");
+    StringArg = E->getArg(0);
+    VarArgs = Args.slice(1);
+    break;
+  case Builtin::BI__builtin_os_log_format:
+    assert(E->getNumArgs() >= 2 &&
+           "__builtin_os_log_format takes at least 2 arguments");
+    StringArg = E->getArg(1);
+    VarArgs = Args.slice(2);
+    break;
+  default:
+    llvm_unreachable("non-os_log builtin passed to computeOSLogBufferLayout");
+  }
+
+  const StringLiteral *Lit = cast<StringLiteral>(StringArg->IgnoreParenCasts());
+  assert(Lit && (Lit->isAscii() || Lit->isUTF8()));
+  StringRef Data = Lit->getString();
+  OSLogFormatStringHandler H(VarArgs);
+  ParsePrintfString(H, Data.begin(), Data.end(), Ctx.getLangOpts(),
+                    Ctx.getTargetInfo(), /*isFreeBSDKPrintf*/ false);
+
+  H.computeLayout(Ctx, Layout);
+  return true;
+}
Index: clang/lib/Analysis/FormatString.cpp
===================================================================
--- clang/lib/Analysis/FormatString.cpp
+++ clang/lib/Analysis/FormatString.cpp
@@ -591,6 +591,8 @@
   case cArg: return "c";
   case sArg: return "s";
   case pArg: return "p";
+  case PArg:
+    return "P";
   case nArg: return "n";
   case PercentArg:  return "%";
   case ScanListArg: return "[";
@@ -866,6 +868,7 @@
     case ConversionSpecifier::ObjCObjArg:
     case ConversionSpecifier::ScanListArg:
     case ConversionSpecifier::PercentArg:
+    case ConversionSpecifier::PArg:
       return true;
     case ConversionSpecifier::CArg:
     case ConversionSpecifier::SArg:
Index: clang/lib/Analysis/CMakeLists.txt
===================================================================
--- clang/lib/Analysis/CMakeLists.txt
+++ clang/lib/Analysis/CMakeLists.txt
@@ -16,6 +16,7 @@
   Dominators.cpp
   FormatString.cpp
   LiveVariables.cpp
+  OSLog.cpp
   ObjCNoReturn.cpp
   PostOrderCFGView.cpp
   PrintfFormatString.cpp
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -9681,6 +9681,7 @@
                  VariadicCallType CallType);
 
   bool CheckObjCString(Expr *Arg);
+  ExprResult CheckOSLogFormatStringArg(Expr *Arg);
 
   ExprResult CheckBuiltinFunctionCall(FunctionDecl *FDecl,
                                       unsigned BuiltinID, CallExpr *TheCall);
@@ -9703,6 +9704,7 @@
   bool SemaBuiltinVAStartARM(CallExpr *Call);
   bool SemaBuiltinUnorderedCompare(CallExpr *TheCall);
   bool SemaBuiltinFPClassification(CallExpr *TheCall, unsigned NumArgs);
+  bool SemaBuiltinOSLogFormat(CallExpr *TheCall);
 
 public:
   // Used by C++ template instantiation.
@@ -9740,6 +9742,7 @@
     FST_Kprintf,
     FST_FreeBSDKPrintf,
     FST_OSTrace,
+    FST_OSLog,
     FST_Unknown
   };
   static FormatStringType GetFormatStringType(const FormatAttr *Format);
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -7413,6 +7413,12 @@
 def warn_format_non_standard_conversion_spec: Warning<
   "using length modifier '%0' with conversion specifier '%1' is not supported by ISO C">,
   InGroup<FormatNonStandard>, DefaultIgnore;
+def warn_format_invalid_annotation : Warning<
+  "using '%0' format specifier annotation outside of os_log()/os_trace()">,
+  InGroup<Format>;
+def warn_format_P_no_precision : Warning<
+  "using '%%P' format specifier without precision">,
+  InGroup<Format>;
 def warn_printf_ignored_flag: Warning<
   "flag '%0' is ignored when flag '%1' is present">,
   InGroup<Format>;
@@ -7549,6 +7555,15 @@
   "belong to the input codeset UTF-8">,
   InGroup<DiagGroup<"CFString-literal">>;
 
+// os_log checking
+// TODO: separate diagnostic for os_trace()
+def err_os_log_format_not_string_constant : Error<
+  "os_log() format argument is not a string constant">;
+def err_os_log_argument_too_big : Error<
+  "os_log() argument %d is too big (%d bytes, max %d)">;
+def warn_os_log_format_narg : Error<
+ "os_log() '%%n' format specifier is not allowed">, DefaultError;
+
 // Statements.
 def err_continue_not_in_loop : Error<
   "'continue' statement not in loop statement">;
Index: clang/include/clang/Basic/Builtins.def
===================================================================
--- clang/include/clang/Basic/Builtins.def
+++ clang/include/clang/Basic/Builtins.def
@@ -1384,6 +1384,10 @@
 LANGBUILTIN(to_local, "v*v*", "tn", OCLC20_LANG)
 LANGBUILTIN(to_private, "v*v*", "tn", OCLC20_LANG)
 
+// Builtins for os_log/os_trace
+BUILTIN(__builtin_os_log_format_buffer_size, "zcC*.", "p:0:nut")
+BUILTIN(__builtin_os_log_format, "v*v*cC*.", "p:0:nt")
+
 #undef BUILTIN
 #undef LIBBUILTIN
 #undef LANGBUILTIN
Index: clang/include/clang/Analysis/Analyses/OSLog.h
===================================================================
--- /dev/null
+++ clang/include/clang/Analysis/Analyses/OSLog.h
@@ -0,0 +1,156 @@
+//= OSLog.h - Analysis of calls to os_log builtins --*- C++ -*-===============//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines APIs for determining the layout of the data buffer for
+// os_log() and os_trace().
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_ANALYSES_OSLOG_H
+#define LLVM_CLANG_ANALYSIS_ANALYSES_OSLOG_H
+
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Expr.h"
+
+namespace clang {
+namespace analyze_os_log {
+
+/// An OSLogBufferItem represents a single item in the data written by a call
+/// to os_log() or os_trace().
+class OSLogBufferItem {
+public:
+  enum Kind {
+    // The item is a scalar (int, float, raw pointer, etc.). No further copying
+    // is required. This is the only kind allowed by os_trace().
+    ScalarKind = 0,
+
+    // The item is a count, which describes the length of the following item to
+    // be copied. A count may only be followed by an item of kind StringKind,
+    // WideStringKind, or PointerKind.
+    CountKind,
+
+    // The item is a pointer to a C string. If preceded by a count 'n',
+    // os_log() will copy at most 'n' bytes from the pointer.
+    StringKind,
+
+    // The item is a pointer to a block of raw data. This item must be preceded
+    // by a count 'n'. os_log() will copy exactly 'n' bytes from the pointer.
+    PointerKind,
+
+    // The item is a pointer to an Objective-C object. os_log() may retain the
+    // object for later processing.
+    ObjCObjKind,
+
+    // The item is a pointer to wide-char string.
+    WideStringKind,
+
+    // The item is corresponding to the '%m' format specifier, no value is
+    // populated in the buffer and the runtime is loading the errno value.
+    ErrnoKind
+  };
+
+  enum {
+    // The item is marked "private" in the format string.
+    IsPrivate = 0x1,
+
+    // The item is marked "public" in the format string.
+    IsPublic = 0x2
+  };
+
+private:
+  Kind TheKind = ScalarKind;
+  const Expr *TheExpr = nullptr;
+  CharUnits ConstValue;
+  CharUnits Size; // size of the data, not including the header bytes
+  unsigned Flags = 0;
+
+public:
+  OSLogBufferItem(Kind kind, const Expr *expr, CharUnits size, unsigned flags)
+      : TheKind(kind), TheExpr(expr), Size(size), Flags(flags) {}
+
+  OSLogBufferItem(ASTContext &Ctx, CharUnits value, unsigned flags)
+      : TheKind(CountKind), ConstValue(value),
+        Size(Ctx.getTypeSizeInChars(Ctx.IntTy)), Flags(flags) {}
+
+  unsigned char getDescriptorByte() const {
+    unsigned char result = 0;
+    if (getIsPrivate())
+      result |= IsPrivate;
+    if (getIsPublic())
+      result |= IsPublic;
+    result |= ((unsigned)getKind()) << 4;
+    return result;
+  }
+
+  unsigned char getSizeByte() const { return getSize().getQuantity(); }
+
+  Kind getKind() const { return TheKind; }
+  bool getIsPrivate() const { return (Flags & IsPrivate) != 0; }
+  bool getIsPublic() const { return (Flags & IsPublic) != 0; }
+
+  const Expr *getExpr() const { return TheExpr; }
+  CharUnits getConstValue() const { return ConstValue; }
+  CharUnits getSize() const { return Size; }
+};
+
+class OSLogBufferLayout {
+public:
+  SmallVector<OSLogBufferItem, 4> Items;
+
+  CharUnits getSize() const {
+    CharUnits result;
+    result += CharUnits::fromQuantity(2); // summary byte, num-args byte
+    for (auto &item : Items) {
+      // descriptor byte, size byte
+      result += item.getSize() + CharUnits::fromQuantity(2);
+    }
+    return result;
+  }
+
+  bool getHasPrivateItems() const {
+    return std::any_of(
+        Items.begin(), Items.end(),
+        [](const OSLogBufferItem &item) { return item.getIsPrivate(); });
+  }
+
+  bool getHasPublicItems() const {
+    return std::any_of(
+        Items.begin(), Items.end(),
+        [](const OSLogBufferItem &item) { return item.getIsPublic(); });
+  }
+
+  bool getHasNonScalar() const {
+    return std::any_of(Items.begin(), Items.end(),
+                       [](const OSLogBufferItem &item) {
+                         return item.getKind() != OSLogBufferItem::ScalarKind;
+                       });
+  }
+
+  unsigned char getSummaryByte() const {
+    unsigned char result = 0;
+    if (getHasPrivateItems())
+      result |= 0x01;
+    if (getHasNonScalar())
+      result |= 0x02;
+    return result;
+  }
+
+  unsigned char getNumArgsByte() const { return Items.size(); }
+};
+
+// Given a call 'E' to one of the builtins __builtin_os_log_format() or
+// __builtin_os_log_format_buffer_size(), compute the layout of the buffer that
+// the call will write into and store it in 'layout'. Returns 'false' if there
+// was some error encountered while computing the layout, and 'true' otherwise.
+bool computeOSLogBufferLayout(clang::ASTContext &Ctx, const clang::CallExpr *E,
+                              OSLogBufferLayout &layout);
+
+} // namespace analyze_os_log
+} // namespace clang
+#endif
Index: clang/include/clang/Analysis/Analyses/FormatString.h
===================================================================
--- clang/include/clang/Analysis/Analyses/FormatString.h
+++ clang/include/clang/Analysis/Analyses/FormatString.h
@@ -35,7 +35,7 @@
 public:
   OptionalFlag(const char *Representation)
       : representation(Representation), flag(false) {}
-  bool isSet() { return flag; }
+  bool isSet() const { return flag; }
   void set() { flag = true; }
   void clear() { flag = false; }
   void setPosition(const char *position) {
@@ -122,20 +122,22 @@
 public:
   enum Kind {
     InvalidSpecifier = 0,
-      // C99 conversion specifiers.
+    // C99 conversion specifiers.
     cArg,
     dArg,
     DArg, // Apple extension
     iArg,
-    IntArgBeg = dArg, IntArgEnd = iArg,
+    IntArgBeg = dArg,
+    IntArgEnd = iArg,
 
     oArg,
     OArg, // Apple extension
     uArg,
     UArg, // Apple extension
     xArg,
     XArg,
-    UIntArgBeg = oArg, UIntArgEnd = XArg,
+    UIntArgBeg = oArg,
+    UIntArgEnd = XArg,
 
     fArg,
     FArg,
@@ -145,37 +147,46 @@
     GArg,
     aArg,
     AArg,
-    DoubleArgBeg = fArg, DoubleArgEnd = AArg,
+    DoubleArgBeg = fArg,
+    DoubleArgEnd = AArg,
 
     sArg,
     pArg,
     nArg,
     PercentArg,
     CArg,
     SArg,
 
+    // Apple extension: P specifies to os_log that the data being pointed to is
+    // to be copied by os_log. The precision indicates the number of bytes to
+    // copy.
+    PArg,
+
     // ** Printf-specific **
 
     ZArg, // MS extension
 
     // Objective-C specific specifiers.
-    ObjCObjArg,  // '@'
-    ObjCBeg = ObjCObjArg, ObjCEnd = ObjCObjArg,
+    ObjCObjArg, // '@'
+    ObjCBeg = ObjCObjArg,
+    ObjCEnd = ObjCObjArg,
 
     // FreeBSD kernel specific specifiers.
     FreeBSDbArg,
     FreeBSDDArg,
     FreeBSDrArg,
     FreeBSDyArg,
 
     // GlibC specific specifiers.
-    PrintErrno,   // 'm'
+    PrintErrno, // 'm'
 
-    PrintfConvBeg = ObjCObjArg, PrintfConvEnd = PrintErrno,
+    PrintfConvBeg = ObjCObjArg,
+    PrintfConvEnd = PrintErrno,
 
     // ** Scanf-specific **
     ScanListArg, // '['
-    ScanfConvBeg = ScanListArg, ScanfConvEnd = ScanListArg
+    ScanfConvBeg = ScanListArg,
+    ScanfConvEnd = ScanListArg
   };
 
   ConversionSpecifier(bool isPrintf = true)
@@ -437,13 +448,15 @@
   OptionalFlag HasAlternativeForm; // '#'
   OptionalFlag HasLeadingZeroes; // '0'
   OptionalFlag HasObjCTechnicalTerm; // '[tt]'
+  OptionalFlag IsPrivate;            // '{private}'
+  OptionalFlag IsPublic;             // '{public}'
   OptionalAmount Precision;
 public:
-  PrintfSpecifier() :
-    FormatSpecifier(/* isPrintf = */ true),
-    HasThousandsGrouping("'"), IsLeftJustified("-"), HasPlusPrefix("+"),
-    HasSpacePrefix(" "), HasAlternativeForm("#"), HasLeadingZeroes("0"),
-    HasObjCTechnicalTerm("tt") {}
+  PrintfSpecifier()
+      : FormatSpecifier(/* isPrintf = */ true), HasThousandsGrouping("'"),
+        IsLeftJustified("-"), HasPlusPrefix("+"), HasSpacePrefix(" "),
+        HasAlternativeForm("#"), HasLeadingZeroes("0"),
+        HasObjCTechnicalTerm("tt"), IsPrivate("private"), IsPublic("public") {}
 
   static PrintfSpecifier Parse(const char *beg, const char *end);
 
@@ -472,6 +485,8 @@
   void setHasObjCTechnicalTerm(const char *position) {
     HasObjCTechnicalTerm.setPosition(position);
   }
+  void setIsPrivate(const char *position) { IsPrivate.setPosition(position); }
+  void setIsPublic(const char *position) { IsPublic.setPosition(position); }
   void setUsesPositionalArg() { UsesPositionalArg = true; }
 
     // Methods for querying the format specifier.
@@ -509,6 +524,8 @@
   const OptionalFlag &hasLeadingZeros() const { return HasLeadingZeroes; }
   const OptionalFlag &hasSpacePrefix() const { return HasSpacePrefix; }
   const OptionalFlag &hasObjCTechnicalTerm() const { return HasObjCTechnicalTerm; }
+  const OptionalFlag &isPrivate() const { return IsPrivate; }
+  const OptionalFlag &isPublic() const { return IsPublic; }
   bool usesPositionalArg() const { return UsesPositionalArg; }
 
   /// Changes the specifier and length according to a QualType, retaining any
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to