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