summaryrefslogtreecommitdiffstats
path: root/clang/lib/Sema/SemaChecking.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/Sema/SemaChecking.cpp')
-rw-r--r--clang/lib/Sema/SemaChecking.cpp286
1 files changed, 226 insertions, 60 deletions
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index fd8e19876b9..f7b826ad949 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -1065,6 +1065,13 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
case Builtin::BIget_kernel_preferred_work_group_size_multiple:
if (SemaOpenCLBuiltinKernelWorkGroupSize(*this, TheCall))
return ExprError();
+ break;
+ 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
@@ -3478,6 +3485,31 @@ bool Sema::CheckObjCString(Expr *Arg) {
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.
@@ -3939,6 +3971,86 @@ bool Sema::SemaBuiltinAssumeAligned(CallExpr *TheCall) {
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,
@@ -4569,15 +4681,16 @@ checkFormatStringExpr(Sema &S, const Expr *E, ArrayRef<const Expr *> Args,
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
@@ -4687,6 +4800,7 @@ protected:
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.
@@ -4703,20 +4817,19 @@ protected:
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();
}
@@ -5139,24 +5252,28 @@ void CheckFormatHandler::EmitFormatDiagnostic(
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,
@@ -5510,11 +5627,54 @@ CheckPrintfHandler::HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier
// 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,
@@ -5527,6 +5687,15 @@ CheckPrintfHandler::HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier
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);
@@ -5676,8 +5845,7 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS,
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;
@@ -5732,7 +5900,7 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS,
// 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()) {
@@ -5773,8 +5941,8 @@ CheckPrintfHandler::checkFormatExpr(const analyze_printf::PrintfSpecifier &FS,
// 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
@@ -5930,19 +6098,18 @@ namespace {
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;
@@ -6152,13 +6319,13 @@ static void CheckFormatString(Sema &S, const FormatStringLiteral *FExpr,
}
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(),
@@ -6166,10 +6333,9 @@ static void CheckFormatString(Sema &S, const FormatStringLiteral *FExpr,
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(),
OpenPOWER on IntegriCloud