1 /*- 2 * Copyright 2016 Vsevolod Stakhov 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 #include <sys/types.h> 17 #include "printf_check.h" 18 #include "clang/AST/AST.h" 19 #include "clang/AST/Expr.h" 20 #include "clang/AST/ASTConsumer.h" 21 #include "clang/AST/RecursiveASTVisitor.h" 22 #include <unordered_map> 23 #include <unordered_set> 24 #include <vector> 25 #include <sstream> 26 #include <ctype.h> 27 #include <signal.h> 28 #include <assert.h> 29 #include <cstdint> 30 31 using namespace clang; 32 33 namespace rspamd { 34 struct PrintfArgChecker; 35 36 static bool cstring_arg_handler (const Expr *arg, 37 struct PrintfArgChecker *ctx); 38 39 static bool int_arg_handler (const Expr *arg, 40 struct PrintfArgChecker *ctx); 41 42 static bool long_arg_handler (const Expr *arg, 43 struct PrintfArgChecker *ctx); 44 45 static bool size_arg_handler (const Expr *arg, 46 struct PrintfArgChecker *ctx); 47 48 static bool char_arg_handler (const Expr *arg, 49 struct PrintfArgChecker *ctx); 50 51 static bool double_arg_handler (const Expr *arg, 52 struct PrintfArgChecker *ctx); 53 54 static bool long_double_arg_handler (const Expr *arg, 55 struct PrintfArgChecker *ctx); 56 57 static bool pointer_arg_handler (const Expr *arg, 58 struct PrintfArgChecker *ctx); 59 60 static bool pid_arg_handler (const Expr *arg, 61 struct PrintfArgChecker *ctx); 62 63 static bool time_arg_handler (const Expr *arg, 64 struct PrintfArgChecker *ctx); 65 66 static bool int64_arg_handler (const Expr *arg, 67 struct PrintfArgChecker *ctx); 68 69 static bool int32_arg_handler (const Expr *arg, 70 struct PrintfArgChecker *ctx); 71 72 static bool tok_arg_handler (const Expr *arg, 73 struct PrintfArgChecker *ctx); 74 75 static bool fstring_arg_handler (const Expr *arg, 76 struct PrintfArgChecker *ctx); 77 78 static bool gstring_arg_handler (const Expr *arg, 79 struct PrintfArgChecker *ctx); 80 81 static bool gerr_arg_handler (const Expr *arg, 82 struct PrintfArgChecker *ctx); 83 84 using arg_parser_t = bool (*) (const Expr *, struct PrintfArgChecker *); 85 86 static void print_error(const char * err,const Expr * e,const ASTContext * ast,CompilerInstance * ci)87 print_error (const char *err, const Expr *e, const ASTContext *ast, 88 CompilerInstance *ci) 89 { 90 auto loc = e->getExprLoc (); 91 auto &diag = ci->getDiagnostics (); 92 auto id = diag.getCustomDiagID (DiagnosticsEngine::Error, 93 "format query error: %0"); 94 diag.Report (loc, id) << err; 95 } 96 97 static void print_warning(const char * err,const Expr * e,const ASTContext * ast,CompilerInstance * ci)98 print_warning (const char *err, const Expr *e, const ASTContext *ast, 99 CompilerInstance *ci) 100 { 101 auto loc = e->getExprLoc (); 102 auto &diag = ci->getDiagnostics (); 103 auto id = diag.getCustomDiagID (DiagnosticsEngine::Warning, 104 "format query warning: %0"); 105 diag.Report (loc, id) << err; 106 } 107 108 static void print_remark(const char * err,const Expr * e,const ASTContext * ast,CompilerInstance * ci)109 print_remark (const char *err, const Expr *e, const ASTContext *ast, 110 CompilerInstance *ci) 111 { 112 auto loc = e->getExprLoc (); 113 auto &diag = ci->getDiagnostics (); 114 auto id = diag.getCustomDiagID (DiagnosticsEngine::Remark, 115 "format query warning: %0"); 116 diag.Report (loc, id) << err; 117 } 118 119 struct PrintfArgChecker { 120 private: 121 arg_parser_t parser; 122 public: 123 int width; 124 int precision; 125 bool is_unsigned; 126 ASTContext *past; 127 CompilerInstance *pci; 128 PrintfArgCheckerrspamd::PrintfArgChecker129 PrintfArgChecker (arg_parser_t _p, ASTContext *_ast, CompilerInstance *_ci) : 130 parser (_p), past (_ast), pci(_ci) 131 { 132 width = 0; 133 precision = 0; 134 is_unsigned = false; 135 } 136 ~PrintfArgCheckerrspamd::PrintfArgChecker137 virtual ~PrintfArgChecker () 138 { 139 } 140 operator ()rspamd::PrintfArgChecker141 bool operator() (const Expr *e) 142 { 143 return parser (e, this); 144 } 145 }; 146 147 class PrintfCheckVisitor::impl { 148 std::unordered_map<std::string, unsigned int> printf_functions; 149 std::unordered_set<char> format_specs; 150 ASTContext *pcontext; 151 CompilerInstance *ci; 152 parseFlags(const std::string & flags,const Expr * e)153 std::unique_ptr <PrintfArgChecker> parseFlags (const std::string &flags, 154 const Expr *e) 155 { 156 auto type = flags.back (); 157 158 switch (type) { 159 case 's': 160 return std::make_unique<PrintfArgChecker> (cstring_arg_handler, 161 this->pcontext, this->ci); 162 case 'd': 163 return std::make_unique<PrintfArgChecker> (int_arg_handler, 164 this->pcontext, this->ci); 165 case 'z': 166 return std::make_unique<PrintfArgChecker> (size_arg_handler, 167 this->pcontext, this->ci); 168 case 'l': 169 return std::make_unique<PrintfArgChecker> (long_arg_handler, 170 this->pcontext, this->ci); 171 case 'f': 172 case 'g': 173 return std::make_unique<PrintfArgChecker> (double_arg_handler, 174 this->pcontext, this->ci); 175 case 'F': 176 case 'G': 177 return std::make_unique<PrintfArgChecker> ( 178 long_double_arg_handler, 179 this->pcontext, this->ci); 180 case 'c': 181 return std::make_unique<PrintfArgChecker> (char_arg_handler, 182 this->pcontext, this->ci); 183 case 'p': 184 return std::make_unique<PrintfArgChecker> (pointer_arg_handler, 185 this->pcontext, this->ci); 186 case 'P': 187 return std::make_unique<PrintfArgChecker> (pid_arg_handler, 188 this->pcontext, this->ci); 189 case 't': 190 return std::make_unique<PrintfArgChecker> (time_arg_handler, 191 this->pcontext, this->ci); 192 case 'L': 193 return std::make_unique<PrintfArgChecker> (int64_arg_handler, 194 this->pcontext, this->ci); 195 case 'D': 196 return std::make_unique<PrintfArgChecker> (int32_arg_handler, 197 this->pcontext, this->ci); 198 case 'T': 199 return std::make_unique<PrintfArgChecker> (tok_arg_handler, 200 this->pcontext, this->ci); 201 case 'V': 202 return std::make_unique<PrintfArgChecker> (fstring_arg_handler, 203 this->pcontext, this->ci); 204 case 'v': 205 return std::make_unique<PrintfArgChecker> (gstring_arg_handler, 206 this->pcontext, this->ci); 207 case 'e': 208 return std::make_unique<PrintfArgChecker> (gerr_arg_handler, 209 this->pcontext, this->ci); 210 default: { 211 auto err_msg = std::string ("unknown parser flag: ") + type; 212 print_warning (err_msg.c_str(), 213 e, this->pcontext, this->ci); 214 break; 215 } 216 } 217 218 return nullptr; 219 } 220 221 std::shared_ptr <std::vector<PrintfArgChecker>> genParsers(const StringRef query,const Expr * e)222 genParsers (const StringRef query, const Expr *e) 223 { 224 enum { 225 ignore_chars = 0, 226 read_percent, 227 read_width, 228 read_precision, 229 read_arg 230 } state = ignore_chars; 231 int width, precision; 232 std::string flags; 233 234 auto res = std::make_shared<std::vector<PrintfArgChecker> > (); 235 236 for (auto citer = query.begin(); citer != query.end(); ++citer) { 237 auto c = *citer; 238 239 switch (state) { 240 case ignore_chars: 241 if (c == '%') { 242 state = read_percent; 243 flags.clear (); 244 width = precision = 0; 245 } 246 break; 247 case read_percent: 248 if (isdigit (c)) { 249 state = read_width; 250 width = c - '0'; 251 } 252 else if (c == '.') { 253 state = read_precision; 254 precision = c - '0'; 255 } 256 else if (c == '*') { 257 /* %*s - need integer argument */ 258 res->emplace_back (int_arg_handler, this->pcontext, 259 this->ci); 260 261 if (*std::next (citer) == '.') { 262 ++citer; 263 state = read_precision; 264 } 265 else { 266 state = read_arg; 267 } 268 } 269 else if (c == '%') { 270 /* Percent character, ignore */ 271 state = ignore_chars; 272 } 273 else { 274 // Rewind iter 275 --citer; 276 state = read_arg; 277 } 278 break; 279 case read_width: 280 if (isdigit (c)) { 281 width *= 10; 282 width += c - '0'; 283 } 284 else if (c == '.') { 285 state = read_precision; 286 precision = c - '0'; 287 } 288 else { 289 // Rewind iter 290 --citer; 291 state = read_arg; 292 } 293 break; 294 case read_precision: 295 if (isdigit (c)) { 296 precision *= 10; 297 precision += c - '0'; 298 } 299 else if (c == '*') { 300 res->emplace_back (int_arg_handler, this->pcontext, 301 this->ci); 302 state = read_arg; 303 } 304 else { 305 // Rewind iter 306 --citer; 307 state = read_arg; 308 } 309 break; 310 case read_arg: 311 auto found = format_specs.find (c); 312 if (found != format_specs.end () || !isalpha (c)) { 313 314 if (isalpha (c)) { 315 flags.push_back (c); 316 } 317 318 auto handler = parseFlags (flags, e); 319 320 if (handler) { 321 auto handler_copy = *handler; 322 handler_copy.precision = precision; 323 handler_copy.width = width; 324 res->emplace_back (std::move (handler_copy)); 325 } 326 else { 327 return nullptr; 328 } 329 330 if (c == '%') { 331 state = read_percent; 332 } 333 else { 334 state = ignore_chars; 335 } 336 flags.clear (); 337 width = precision = 0; 338 } 339 else { 340 flags.push_back (c); 341 } 342 break; 343 } 344 } 345 346 if (state == read_arg) { 347 auto handler = parseFlags (flags, e); 348 349 if (handler) { 350 auto handler_copy = *handler; 351 handler_copy.precision = precision; 352 handler_copy.width = width; 353 res->emplace_back (std::move (handler_copy)); 354 } 355 else { 356 return nullptr; 357 } 358 } 359 360 return res; 361 } 362 363 public: impl(ASTContext * _ctx,clang::CompilerInstance & _ci)364 impl (ASTContext *_ctx, clang::CompilerInstance &_ci) 365 : pcontext (_ctx), ci(&_ci) 366 { 367 /* name -> format string position */ 368 printf_functions = { 369 {"rspamd_printf", 0}, 370 {"rspamd_default_log_function", 4}, 371 {"rspamd_snprintf", 2}, 372 {"rspamd_fprintf", 1}, 373 {"rspamd_printf_gstring", 1}, 374 {"rspamd_printf_fstring", 1}, 375 {"rspamd_conditional_debug_fast", 6}, 376 }; 377 378 format_specs = { 379 's', 'd', 'l', 'L', 'v', 'V', 'f', 'F', 'g', 'G', 380 'T', 'z', 'D', 'c', 'p', 'P', 'e' 381 }; 382 }; 383 VisitCallExpr(CallExpr * E)384 bool VisitCallExpr (CallExpr *E) 385 { 386 if (E->getCalleeDecl () == nullptr) { 387 print_remark ("cannot get callee decl", 388 E, this->pcontext, this->ci); 389 return true; 390 } 391 auto callee = dyn_cast<NamedDecl> (E->getCalleeDecl ()); 392 if (callee == NULL) { 393 print_remark ("cannot get named callee decl", 394 E, this->pcontext, this->ci); 395 return true; 396 } 397 398 auto fname = callee->getNameAsString (); 399 400 auto pos_it = printf_functions.find (fname); 401 402 if (pos_it != printf_functions.end ()) { 403 const auto args = E->getArgs (); 404 auto pos = pos_it->second; 405 auto query = args[pos]; 406 407 if (!query->isEvaluatable (*pcontext)) { 408 print_remark ("cannot evaluate query", 409 E, this->pcontext, this->ci); 410 /* It is not assumed to be an error */ 411 return true; 412 } 413 414 clang::Expr::EvalResult r; 415 416 if (!query->EvaluateAsRValue (r, *pcontext)) { 417 print_warning ("cannot evaluate rvalue of query", 418 E, this->pcontext, this->ci); 419 return false; 420 } 421 422 auto qval = dyn_cast<StringLiteral> ( 423 r.Val.getLValueBase ().get<const Expr *> ()); 424 if (!qval) { 425 print_warning ("bad or absent query string", 426 E, this->pcontext, this->ci); 427 return false; 428 } 429 430 auto parsers = genParsers (qval->getString (), E); 431 432 if (parsers) { 433 if (parsers->size () != E->getNumArgs () - (pos + 1)) { 434 std::ostringstream err_buf; 435 err_buf << "number of arguments for " << fname 436 << " mismatches query string '" << 437 qval->getString ().str () 438 << "', expected " << parsers->size () << 439 " args" 440 << ", got " << 441 (E->getNumArgs () - (pos + 1)) 442 << " args"; 443 print_error (err_buf.str().c_str(), E, this->pcontext, this->ci); 444 445 return false; 446 } 447 else { 448 for (auto i = pos + 1; i < E->getNumArgs (); i++) { 449 auto arg = args[i]; 450 451 if (arg) { 452 if (!parsers->at (i - (pos + 1)) (arg)) { 453 return false; 454 } 455 } 456 } 457 } 458 } 459 } 460 461 return true; 462 } 463 }; 464 PrintfCheckVisitor(ASTContext * ctx,clang::CompilerInstance & ci)465 PrintfCheckVisitor::PrintfCheckVisitor (ASTContext *ctx, 466 clang::CompilerInstance &ci) : 467 pimpl{new impl (ctx, ci)} 468 { 469 } 470 ~PrintfCheckVisitor()471 PrintfCheckVisitor::~PrintfCheckVisitor () 472 { 473 } 474 VisitCallExpr(clang::CallExpr * E)475 bool PrintfCheckVisitor::VisitCallExpr (clang::CallExpr *E) 476 { 477 return pimpl->VisitCallExpr (E); 478 } 479 480 /* Type handlers */ 481 static bool cstring_arg_handler(const Expr * arg,struct PrintfArgChecker * ctx)482 cstring_arg_handler (const Expr *arg, struct PrintfArgChecker *ctx) 483 { 484 auto type = arg->getType ().split ().Ty; 485 486 if (!type->isPointerType ()) { 487 auto err_msg = std::string ("bad string argument for %s: ") + 488 arg->getType ().getAsString (); 489 print_error (err_msg.c_str(), 490 arg, ctx->past, ctx->pci); 491 return false; 492 } 493 494 auto ptr_type = type->getPointeeType ().split ().Ty; 495 496 if (!ptr_type->isCharType ()) { 497 /* We might have gchar * here */ 498 auto desugared_type = ptr_type->getUnqualifiedDesugaredType (); 499 auto desugared_ptr_type = type->getUnqualifiedDesugaredType (); 500 501 if (!desugared_type || (!desugared_type->isCharType () && 502 !desugared_ptr_type->isVoidPointerType ())) { 503 if (desugared_type) { 504 desugared_type->dump (); 505 } 506 auto err_msg = std::string ("bad string argument for %s: ") + 507 arg->getType ().getAsString (); 508 print_error (err_msg.c_str(), 509 arg, ctx->past, ctx->pci); 510 return false; 511 } 512 } 513 514 return true; 515 } 516 517 static bool check_builtin_type(const Expr * arg,struct PrintfArgChecker * ctx,const std::vector<BuiltinType::Kind> & k,const std::string & fmt)518 check_builtin_type (const Expr *arg, struct PrintfArgChecker *ctx, 519 const std::vector <BuiltinType::Kind> &k, const std::string &fmt) 520 { 521 auto type = arg->getType ().split ().Ty; 522 523 auto desugared_type = type->getUnqualifiedDesugaredType (); 524 525 if (!desugared_type->isBuiltinType ()) { 526 auto err_msg = std::string ("not a builtin type for ") + fmt + " arg: " + 527 arg->getType ().getAsString (); 528 print_error (err_msg.c_str(), 529 arg, ctx->past, ctx->pci); 530 return false; 531 } 532 533 auto builtin_type = dyn_cast<BuiltinType> (desugared_type); 534 auto kind = builtin_type->getKind (); 535 auto found = false; 536 537 for (auto kk : k) { 538 if (kind == kk) { 539 found = true; 540 break; 541 } 542 } 543 544 if (!found) { 545 auto err_msg = std::string ("bad argument for ") + 546 fmt + " arg: " + 547 arg->getType ().getAsString () + 548 ", resolved as: " + 549 builtin_type->getNameAsCString (ctx->past->getPrintingPolicy ()); 550 print_error (err_msg.c_str(), 551 arg, ctx->past, ctx->pci); 552 return false; 553 } 554 555 return true; 556 } 557 558 static bool int_arg_handler(const Expr * arg,struct PrintfArgChecker * ctx)559 int_arg_handler (const Expr *arg, struct PrintfArgChecker *ctx) 560 { 561 return check_builtin_type (arg, 562 ctx, 563 {BuiltinType::Kind::UInt, 564 BuiltinType::Kind::Int}, 565 "%d or *"); 566 } 567 568 static bool long_arg_handler(const Expr * arg,struct PrintfArgChecker * ctx)569 long_arg_handler (const Expr *arg, struct PrintfArgChecker *ctx) 570 { 571 return check_builtin_type (arg, 572 ctx, 573 {BuiltinType::Kind::ULong, 574 BuiltinType::Kind::Long}, 575 "%l"); 576 } 577 578 static bool char_arg_handler(const Expr * arg,struct PrintfArgChecker * ctx)579 char_arg_handler (const Expr *arg, struct PrintfArgChecker *ctx) 580 { 581 return check_builtin_type (arg, 582 ctx, 583 {BuiltinType::Kind::UChar, 584 BuiltinType::Kind::SChar, 585 BuiltinType::Kind::Int}, // Because of char -> int propagation 586 "%c"); 587 } 588 589 static bool size_arg_handler(const Expr * arg,struct PrintfArgChecker * ctx)590 size_arg_handler (const Expr *arg, struct PrintfArgChecker *ctx) 591 { 592 if (sizeof (size_t) == sizeof (long)) { 593 if (sizeof (long long) == sizeof (long)) { 594 return check_builtin_type (arg, 595 ctx, 596 {BuiltinType::Kind::ULong, 597 BuiltinType::Kind::Long, 598 BuiltinType::Kind::LongLong, 599 BuiltinType::Kind::ULongLong}, 600 "%z"); 601 } 602 else { 603 return check_builtin_type (arg, 604 ctx, 605 {BuiltinType::Kind::ULong, 606 BuiltinType::Kind::Long}, 607 "%z"); 608 } 609 } 610 else if (sizeof (size_t) == sizeof (int)) { 611 return check_builtin_type (arg, 612 ctx, 613 {BuiltinType::Kind::UInt, 614 BuiltinType::Kind::Int}, 615 "%z"); 616 } 617 else { 618 assert (0); 619 } 620 621 return true; 622 } 623 624 static bool double_arg_handler(const Expr * arg,struct PrintfArgChecker * ctx)625 double_arg_handler (const Expr *arg, struct PrintfArgChecker *ctx) 626 { 627 return check_builtin_type (arg, 628 ctx, 629 {BuiltinType::Kind::Double}, 630 "%f or %g"); 631 } 632 633 static bool long_double_arg_handler(const Expr * arg,struct PrintfArgChecker * ctx)634 long_double_arg_handler (const Expr *arg, struct PrintfArgChecker *ctx) 635 { 636 return check_builtin_type (arg, 637 ctx, 638 {BuiltinType::Kind::LongDouble}, 639 "%F or %G"); 640 } 641 642 static bool pid_arg_handler(const Expr * arg,struct PrintfArgChecker * ctx)643 pid_arg_handler (const Expr *arg, struct PrintfArgChecker *ctx) 644 { 645 if (sizeof (pid_t) == sizeof (long)) { 646 return check_builtin_type (arg, 647 ctx, 648 {BuiltinType::Kind::ULong, 649 BuiltinType::Kind::Long}, 650 "%P"); 651 } 652 else if (sizeof (pid_t) == sizeof (int)) { 653 return check_builtin_type (arg, 654 ctx, 655 {BuiltinType::Kind::UInt, 656 BuiltinType::Kind::Int}, 657 "%P"); 658 } 659 else { 660 assert (0); 661 } 662 } 663 664 static bool time_arg_handler(const Expr * arg,struct PrintfArgChecker * ctx)665 time_arg_handler (const Expr *arg, struct PrintfArgChecker *ctx) 666 { 667 if (sizeof (time_t) == sizeof (long)) { 668 return check_builtin_type (arg, 669 ctx, 670 {BuiltinType::Kind::ULong, 671 BuiltinType::Kind::Long}, 672 "%t"); 673 } 674 else if (sizeof (time_t) == sizeof (int)) { 675 return check_builtin_type (arg, 676 ctx, 677 {BuiltinType::Kind::UInt, 678 BuiltinType::Kind::Int}, 679 "%t"); 680 } 681 else { 682 assert (0); 683 } 684 } 685 686 static bool pointer_arg_handler(const Expr * arg,struct PrintfArgChecker * ctx)687 pointer_arg_handler (const Expr *arg, struct PrintfArgChecker *ctx) 688 { 689 auto type = arg->getType ().split ().Ty; 690 691 if (!type->isPointerType ()) { 692 auto err_msg = std::string ("bad pointer argument for %p: ") + 693 arg->getType ().getAsString (); 694 print_error (err_msg.c_str(), 695 arg, ctx->past, ctx->pci); 696 return false; 697 } 698 699 return true; 700 } 701 702 static bool int64_arg_handler(const Expr * arg,struct PrintfArgChecker * ctx)703 int64_arg_handler (const Expr *arg, struct PrintfArgChecker *ctx) 704 { 705 std::vector <BuiltinType::Kind> check; 706 707 if (sizeof (int64_t) == sizeof (long long)) { 708 check.push_back (BuiltinType::Kind::ULongLong); 709 check.push_back (BuiltinType::Kind::LongLong); 710 } 711 if (sizeof (int64_t) == sizeof (long)) { 712 check.push_back (BuiltinType::Kind::ULong); 713 check.push_back (BuiltinType::Kind::Long); 714 } 715 716 return check_builtin_type (arg, 717 ctx, 718 check, 719 "%L"); 720 721 return true; 722 } 723 724 static bool int32_arg_handler(const Expr * arg,struct PrintfArgChecker * ctx)725 int32_arg_handler (const Expr *arg, struct PrintfArgChecker *ctx) 726 { 727 std::vector < BuiltinType::Kind> check; 728 729 if (sizeof (int32_t) == sizeof (long)) { 730 check.push_back (BuiltinType::Kind::ULong); 731 check.push_back (BuiltinType::Kind::Long); 732 } 733 if (sizeof (int32_t) == sizeof (int)) { 734 check.push_back (BuiltinType::Kind::UInt); 735 check.push_back (BuiltinType::Kind::Int); 736 } 737 738 return check_builtin_type (arg, 739 ctx, 740 check, 741 "%D"); 742 743 return true; 744 } 745 746 static bool check_struct_type(const Expr * arg,struct PrintfArgChecker * ctx,const std::string & sname,const std::string & fmt)747 check_struct_type (const Expr *arg, struct PrintfArgChecker *ctx, 748 const std::string &sname, const std::string &fmt) 749 { 750 auto type = arg->getType ().split ().Ty; 751 752 if (!type->isPointerType ()) { 753 auto err_msg = std::string ("non pointer argument for %s: ") + 754 arg->getType ().getAsString (); 755 print_error (err_msg.c_str(), 756 arg, ctx->past, ctx->pci); 757 return false; 758 } 759 760 auto ptr_type = type->getPointeeType ().split ().Ty; 761 auto desugared_type = ptr_type->getUnqualifiedDesugaredType (); 762 763 if (!desugared_type->isRecordType ()) { 764 auto err_msg = std::string ("not a record type for ") + fmt + " arg: " + 765 arg->getType ().getAsString (); 766 print_error (err_msg.c_str(), 767 arg, ctx->past, ctx->pci); 768 return false; 769 } 770 771 auto struct_type = desugared_type->getAsStructureType (); 772 auto struct_decl = struct_type->getDecl (); 773 auto struct_def = struct_decl->getNameAsString (); 774 775 if (struct_def != sname) { 776 auto err_msg = std::string ("bad argument '") + struct_def + "' for " 777 + fmt + " arg: " + 778 arg->getType ().getAsString (); 779 print_error (err_msg.c_str(), 780 arg, ctx->past, ctx->pci); 781 return false; 782 } 783 784 return true; 785 } 786 787 static bool tok_arg_handler(const Expr * arg,struct PrintfArgChecker * ctx)788 tok_arg_handler (const Expr *arg, struct PrintfArgChecker *ctx) 789 { 790 return check_struct_type (arg, 791 ctx, 792 "f_str_tok", 793 "%T"); 794 } 795 796 static bool fstring_arg_handler(const Expr * arg,struct PrintfArgChecker * ctx)797 fstring_arg_handler (const Expr *arg, struct PrintfArgChecker *ctx) 798 { 799 return check_struct_type (arg, 800 ctx, 801 "f_str_s", 802 "%V"); 803 } 804 805 static bool gstring_arg_handler(const Expr * arg,struct PrintfArgChecker * ctx)806 gstring_arg_handler (const Expr *arg, struct PrintfArgChecker *ctx) 807 { 808 return check_struct_type (arg, 809 ctx, 810 "_GString", 811 "%v"); 812 } 813 814 static bool gerr_arg_handler(const Expr * arg,struct PrintfArgChecker * ctx)815 gerr_arg_handler (const Expr *arg, struct PrintfArgChecker *ctx) 816 { 817 return check_struct_type (arg, 818 ctx, 819 "_GError", 820 "%e"); 821 } 822 } 823