You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

printf_check.cc 20KB

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