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 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818
  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. bool VisitCallExpr(CallExpr *E)
  339. {
  340. if (E->getCalleeDecl() == nullptr) {
  341. print_remark("cannot get callee decl",
  342. E, this->pcontext, this->ci);
  343. return true;
  344. }
  345. auto callee = dyn_cast<NamedDecl>(E->getCalleeDecl());
  346. if (callee == NULL) {
  347. print_remark("cannot get named callee decl",
  348. E, this->pcontext, this->ci);
  349. return true;
  350. }
  351. auto fname = callee->getNameAsString();
  352. auto pos_it = printf_functions.find(fname);
  353. if (pos_it != printf_functions.end()) {
  354. const auto args = E->getArgs();
  355. auto pos = pos_it->second;
  356. auto query = args[pos];
  357. if (!query->isEvaluatable(*pcontext)) {
  358. print_remark("cannot evaluate query",
  359. E, this->pcontext, this->ci);
  360. /* It is not assumed to be an error */
  361. return true;
  362. }
  363. clang::Expr::EvalResult r;
  364. if (!query->EvaluateAsRValue(r, *pcontext)) {
  365. print_warning("cannot evaluate rvalue of query",
  366. E, this->pcontext, this->ci);
  367. return false;
  368. }
  369. auto qval = dyn_cast<StringLiteral>(
  370. r.Val.getLValueBase().get<const Expr *>());
  371. if (!qval) {
  372. print_warning("bad or absent query string",
  373. E, this->pcontext, this->ci);
  374. return false;
  375. }
  376. auto parsers = genParsers(qval->getString(), E);
  377. if (parsers) {
  378. if (parsers->size() != E->getNumArgs() - (pos + 1)) {
  379. std::ostringstream err_buf;
  380. err_buf << "number of arguments for " << fname
  381. << " mismatches query string '" << qval->getString().str()
  382. << "', expected " << parsers->size() << " args"
  383. << ", got " << (E->getNumArgs() - (pos + 1))
  384. << " args";
  385. print_error(err_buf.str().c_str(), E, this->pcontext, this->ci);
  386. return false;
  387. }
  388. else {
  389. for (auto i = pos + 1; i < E->getNumArgs(); i++) {
  390. auto arg = args[i];
  391. if (arg) {
  392. if (!parsers->at(i - (pos + 1))(arg)) {
  393. return false;
  394. }
  395. }
  396. }
  397. }
  398. }
  399. }
  400. return true;
  401. }
  402. };
  403. PrintfCheckVisitor::PrintfCheckVisitor(ASTContext *ctx,
  404. clang::CompilerInstance &ci)
  405. : pimpl{new impl(ctx, ci)}
  406. {
  407. }
  408. PrintfCheckVisitor::~PrintfCheckVisitor()
  409. {
  410. }
  411. bool PrintfCheckVisitor::VisitCallExpr(clang::CallExpr *E)
  412. {
  413. return pimpl->VisitCallExpr(E);
  414. }
  415. /* Type handlers */
  416. static bool
  417. cstring_arg_handler(const Expr *arg, struct PrintfArgChecker *ctx)
  418. {
  419. auto type = arg->getType().split().Ty;
  420. if (!type->isPointerType()) {
  421. auto err_msg = std::string("bad string argument for %s: ") +
  422. arg->getType().getAsString();
  423. print_error(err_msg.c_str(),
  424. arg, ctx->past, ctx->pci);
  425. return false;
  426. }
  427. auto ptr_type = type->getPointeeType().split().Ty;
  428. if (!ptr_type->isCharType()) {
  429. /* We might have char * here */
  430. auto desugared_type = ptr_type->getUnqualifiedDesugaredType();
  431. auto desugared_ptr_type = type->getUnqualifiedDesugaredType();
  432. if (!desugared_type || (!desugared_type->isCharType() &&
  433. !desugared_ptr_type->isVoidPointerType())) {
  434. if (desugared_type) {
  435. desugared_type->dump();
  436. }
  437. auto err_msg = std::string("bad string argument for %s: ") +
  438. arg->getType().getAsString();
  439. print_error(err_msg.c_str(),
  440. arg, ctx->past, ctx->pci);
  441. return false;
  442. }
  443. }
  444. return true;
  445. }
  446. static bool
  447. check_builtin_type(const Expr *arg, struct PrintfArgChecker *ctx,
  448. const std::vector<BuiltinType::Kind> &k, const std::string &fmt)
  449. {
  450. auto type = arg->getType().split().Ty;
  451. auto desugared_type = type->getUnqualifiedDesugaredType();
  452. if (!desugared_type->isBuiltinType()) {
  453. auto err_msg = std::string("not a builtin type for ") + fmt + " arg: " +
  454. arg->getType().getAsString();
  455. print_error(err_msg.c_str(),
  456. arg, ctx->past, ctx->pci);
  457. return false;
  458. }
  459. auto builtin_type = dyn_cast<BuiltinType>(desugared_type);
  460. auto kind = builtin_type->getKind();
  461. auto found = false;
  462. for (auto kk: k) {
  463. if (kind == kk) {
  464. found = true;
  465. break;
  466. }
  467. }
  468. if (!found) {
  469. auto err_msg = std::string("bad argument for ") +
  470. fmt + " arg: " +
  471. arg->getType().getAsString() +
  472. ", resolved as: " +
  473. builtin_type->getNameAsCString(ctx->past->getPrintingPolicy());
  474. print_error(err_msg.c_str(),
  475. arg, ctx->past, ctx->pci);
  476. return false;
  477. }
  478. return true;
  479. }
  480. static bool
  481. int_arg_handler(const Expr *arg, struct PrintfArgChecker *ctx)
  482. {
  483. return check_builtin_type(arg,
  484. ctx,
  485. {BuiltinType::Kind::UInt,
  486. BuiltinType::Kind::Int},
  487. "%d or *");
  488. }
  489. static bool
  490. long_arg_handler(const Expr *arg, struct PrintfArgChecker *ctx)
  491. {
  492. return check_builtin_type(arg,
  493. ctx,
  494. {BuiltinType::Kind::ULong,
  495. BuiltinType::Kind::Long},
  496. "%l");
  497. }
  498. static bool
  499. char_arg_handler(const Expr *arg, struct PrintfArgChecker *ctx)
  500. {
  501. return check_builtin_type(arg,
  502. ctx,
  503. {BuiltinType::Kind::UChar,
  504. BuiltinType::Kind::SChar,
  505. BuiltinType::Kind::Int},// Because of char -> int propagation
  506. "%c");
  507. }
  508. static bool
  509. size_arg_handler(const Expr *arg, struct PrintfArgChecker *ctx)
  510. {
  511. if (sizeof(size_t) == sizeof(long)) {
  512. if (sizeof(long long) == sizeof(long)) {
  513. return check_builtin_type(arg,
  514. ctx,
  515. {BuiltinType::Kind::ULong,
  516. BuiltinType::Kind::Long,
  517. BuiltinType::Kind::LongLong,
  518. BuiltinType::Kind::ULongLong},
  519. "%z");
  520. }
  521. else {
  522. return check_builtin_type(arg,
  523. ctx,
  524. {BuiltinType::Kind::ULong,
  525. BuiltinType::Kind::Long},
  526. "%z");
  527. }
  528. }
  529. else if (sizeof(size_t) == sizeof(int)) {
  530. return check_builtin_type(arg,
  531. ctx,
  532. {BuiltinType::Kind::UInt,
  533. BuiltinType::Kind::Int},
  534. "%z");
  535. }
  536. else {
  537. assert(0);
  538. }
  539. return true;
  540. }
  541. static bool
  542. double_arg_handler(const Expr *arg, struct PrintfArgChecker *ctx)
  543. {
  544. return check_builtin_type(arg,
  545. ctx,
  546. {BuiltinType::Kind::Double},
  547. "%f or %g");
  548. }
  549. static bool
  550. long_double_arg_handler(const Expr *arg, struct PrintfArgChecker *ctx)
  551. {
  552. return check_builtin_type(arg,
  553. ctx,
  554. {BuiltinType::Kind::LongDouble},
  555. "%F or %G");
  556. }
  557. static bool
  558. pid_arg_handler(const Expr *arg, struct PrintfArgChecker *ctx)
  559. {
  560. if (sizeof(pid_t) == sizeof(long)) {
  561. return check_builtin_type(arg,
  562. ctx,
  563. {BuiltinType::Kind::ULong,
  564. BuiltinType::Kind::Long},
  565. "%P");
  566. }
  567. else if (sizeof(pid_t) == sizeof(int)) {
  568. return check_builtin_type(arg,
  569. ctx,
  570. {BuiltinType::Kind::UInt,
  571. BuiltinType::Kind::Int},
  572. "%P");
  573. }
  574. else {
  575. assert(0);
  576. }
  577. }
  578. static bool
  579. time_arg_handler(const Expr *arg, struct PrintfArgChecker *ctx)
  580. {
  581. if (sizeof(time_t) == sizeof(long)) {
  582. return check_builtin_type(arg,
  583. ctx,
  584. {BuiltinType::Kind::ULong,
  585. BuiltinType::Kind::Long},
  586. "%t");
  587. }
  588. else if (sizeof(time_t) == sizeof(int)) {
  589. return check_builtin_type(arg,
  590. ctx,
  591. {BuiltinType::Kind::UInt,
  592. BuiltinType::Kind::Int},
  593. "%t");
  594. }
  595. else {
  596. assert(0);
  597. }
  598. }
  599. static bool
  600. pointer_arg_handler(const Expr *arg, struct PrintfArgChecker *ctx)
  601. {
  602. auto type = arg->getType().split().Ty;
  603. if (!type->isPointerType()) {
  604. auto err_msg = std::string("bad pointer argument for %p: ") +
  605. arg->getType().getAsString();
  606. print_error(err_msg.c_str(),
  607. arg, ctx->past, ctx->pci);
  608. return false;
  609. }
  610. return true;
  611. }
  612. static bool
  613. int64_arg_handler(const Expr *arg, struct PrintfArgChecker *ctx)
  614. {
  615. std::vector<BuiltinType::Kind> check;
  616. if (sizeof(int64_t) == sizeof(long long)) {
  617. check.push_back(BuiltinType::Kind::ULongLong);
  618. check.push_back(BuiltinType::Kind::LongLong);
  619. }
  620. if (sizeof(int64_t) == sizeof(long)) {
  621. check.push_back(BuiltinType::Kind::ULong);
  622. check.push_back(BuiltinType::Kind::Long);
  623. }
  624. return check_builtin_type(arg,
  625. ctx,
  626. check,
  627. "%L");
  628. return true;
  629. }
  630. static bool
  631. int32_arg_handler(const Expr *arg, struct PrintfArgChecker *ctx)
  632. {
  633. std::vector<BuiltinType::Kind> check;
  634. if (sizeof(int32_t) == sizeof(long)) {
  635. check.push_back(BuiltinType::Kind::ULong);
  636. check.push_back(BuiltinType::Kind::Long);
  637. }
  638. if (sizeof(int32_t) == sizeof(int)) {
  639. check.push_back(BuiltinType::Kind::UInt);
  640. check.push_back(BuiltinType::Kind::Int);
  641. }
  642. return check_builtin_type(arg,
  643. ctx,
  644. check,
  645. "%D");
  646. return true;
  647. }
  648. static bool
  649. check_struct_type(const Expr *arg, struct PrintfArgChecker *ctx,
  650. const std::string &sname, const std::string &fmt)
  651. {
  652. auto type = arg->getType().split().Ty;
  653. if (!type->isPointerType()) {
  654. auto err_msg = std::string("non pointer argument for %s: ") +
  655. arg->getType().getAsString();
  656. print_error(err_msg.c_str(),
  657. arg, ctx->past, ctx->pci);
  658. return false;
  659. }
  660. auto ptr_type = type->getPointeeType().split().Ty;
  661. auto desugared_type = ptr_type->getUnqualifiedDesugaredType();
  662. if (!desugared_type->isRecordType()) {
  663. auto err_msg = std::string("not a record type for ") + fmt + " arg: " +
  664. arg->getType().getAsString();
  665. print_error(err_msg.c_str(),
  666. arg, ctx->past, ctx->pci);
  667. return false;
  668. }
  669. auto struct_type = desugared_type->getAsStructureType();
  670. auto struct_decl = struct_type->getDecl();
  671. auto struct_def = struct_decl->getNameAsString();
  672. if (struct_def != sname) {
  673. auto err_msg = std::string("bad argument '") + struct_def + "' for " + fmt + " arg: " +
  674. arg->getType().getAsString();
  675. print_error(err_msg.c_str(),
  676. arg, ctx->past, ctx->pci);
  677. return false;
  678. }
  679. return true;
  680. }
  681. static bool
  682. tok_arg_handler(const Expr *arg, struct PrintfArgChecker *ctx)
  683. {
  684. return check_struct_type(arg,
  685. ctx,
  686. "f_str_tok",
  687. "%T");
  688. }
  689. static bool
  690. fstring_arg_handler(const Expr *arg, struct PrintfArgChecker *ctx)
  691. {
  692. return check_struct_type(arg,
  693. ctx,
  694. "f_str_s",
  695. "%V");
  696. }
  697. static bool
  698. gstring_arg_handler(const Expr *arg, struct PrintfArgChecker *ctx)
  699. {
  700. return check_struct_type(arg,
  701. ctx,
  702. "_GString",
  703. "%v");
  704. }
  705. static bool
  706. gerr_arg_handler(const Expr *arg, struct PrintfArgChecker *ctx)
  707. {
  708. return check_struct_type(arg,
  709. ctx,
  710. "_GError",
  711. "%e");
  712. }
  713. }// namespace rspamd