diff options
Diffstat (limited to 'clang/lib/Sema/SemaChecking.cpp')
-rw-r--r-- | clang/lib/Sema/SemaChecking.cpp | 286 |
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(), |