diff options
author | Vsevolod Stakhov <vsevolod@highsecure.ru> | 2015-11-11 13:07:32 +0000 |
---|---|---|
committer | Vsevolod Stakhov <vsevolod@highsecure.ru> | 2015-11-11 13:07:32 +0000 |
commit | 2a406c67c4013e16bb97c317628deb8e19f190f5 (patch) | |
tree | 63b9962fd127e295d21b81832552bd862cde7cfc /clang-plugin/printf_check.cc | |
parent | 440d438fffc17b62ce08fb26d24a1ad0e36fbeb6 (diff) | |
download | rspamd-2a406c67c4013e16bb97c317628deb8e19f190f5.tar.gz rspamd-2a406c67c4013e16bb97c317628deb8e19f190f5.zip |
Write basics for arguments checks.
Diffstat (limited to 'clang-plugin/printf_check.cc')
-rw-r--r-- | clang-plugin/printf_check.cc | 204 |
1 files changed, 199 insertions, 5 deletions
diff --git a/clang-plugin/printf_check.cc b/clang-plugin/printf_check.cc index 4c0e8edd8..d0ec55f61 100644 --- a/clang-plugin/printf_check.cc +++ b/clang-plugin/printf_check.cc @@ -28,14 +28,185 @@ #include "clang/AST/ASTConsumer.h" #include "clang/AST/RecursiveASTVisitor.h" #include <unordered_map> +#include <vector> +#include <sstream> +#include <ctype.h> using namespace clang; namespace rspamd { + struct PrintfArgChecker; + + using arg_parser_t = bool (*) (const Expr *, struct PrintfArgChecker *); + + static void + print_error (const std::string &err, const Expr *e, const ASTContext *ast) + { + auto const &sm = ast->getSourceManager (); + auto loc = e->getExprLoc (); + llvm::errs() << err << " at " << loc.printToString (sm) << "\n"; + } + + /* Handles %s */ + static bool + cstring_arg_handler (const Expr *arg, struct PrintfArgChecker *ctx) + { + return true; + } + + static bool + int_arg_handler (const Expr *arg, struct PrintfArgChecker *ctx) + { + return true; + } + + struct PrintfArgChecker { + private: + arg_parser_t parser; + public: + int width; + int precision; + + PrintfArgChecker (arg_parser_t _p) : parser(_p) {} + virtual ~PrintfArgChecker () {} + bool operator () (const Expr *e) + { + return parser (e, this); + } + }; + class PrintfCheckVisitor::impl { std::unordered_map<std::string, int> printf_functions; ASTContext *pcontext; + std::unique_ptr<PrintfArgChecker> parseFlags (const std::string &flags) + { + auto type = flags.back(); + + switch (type) { + case 's': + return llvm::make_unique<PrintfArgChecker>(cstring_arg_handler); + case 'd': + return llvm::make_unique<PrintfArgChecker>(int_arg_handler); + default: + llvm::errs () << "unknown parser flag: " << type << "\n"; + break; + } + + return nullptr; + } + + std::shared_ptr<std::vector<PrintfArgChecker> > + genParsers (const StringRef query) + { + enum { + ignore_chars = 0, + read_percent, + read_width, + read_precision, + read_arg + } state = ignore_chars; + int width, precision; + std::string flags; + + auto res = std::make_shared<std::vector<PrintfArgChecker> >(); + + for (const auto c : query) { + switch (state) { + case ignore_chars: + if (c == '%') { + state = read_percent; + flags.clear (); + width = precision = 0; + } + break; + case read_percent: + if (isdigit (c)) { + state = read_width; + width = c - '0'; + } + else if (c == '.') { + state = read_precision; + precision = c - '0'; + } + else if (c == '*') { + /* %*s - need integer argument */ + res->emplace_back (int_arg_handler); + state = read_arg; + } + else if (c == '%') { + /* Percent character, ignore */ + state = ignore_chars; + } + else { + flags.push_back (c); + state = read_arg; + } + break; + case read_width: + if (isdigit (c)) { + width *= 10; + width += c - '0'; + } + else if (c == '.') { + state = read_precision; + precision = c - '0'; + } + else { + flags.push_back (c); + state = read_arg; + } + break; + case read_precision: + if (isdigit (c)) { + precision *= 10; + precision += c - '0'; + } + else if (c == '*') { + res->emplace_back (int_arg_handler); + state = read_arg; + } + else { + flags.push_back (c); + state = read_arg; + } + break; + case read_arg: + if (!isalpha (c)) { + auto handler = parseFlags (flags); + + if (handler) { + auto handler_copy = *handler; + res->emplace_back (std::move (handler_copy)); + } + else { + llvm::errs () << "invalid modifier\n"; + return nullptr; + } + state = ignore_chars; + } + else { + flags.push_back (c); + } + break; + } + } + + if (state == read_arg) { + auto handler = parseFlags (flags); + + if (handler) { + auto handler_copy = *handler; + res->emplace_back (std::move (handler_copy)); + } + else { + llvm::errs () << "invalid modifier\n"; + return nullptr; + } + } + + return res; + } public: impl (ASTContext *_ctx) : pcontext(_ctx) { @@ -83,13 +254,36 @@ namespace rspamd { llvm::errs () << "query string: " << qval->getString () << "\n"; } + else { + llvm::errs () << "Bad or absent query string\n"; + return false; + } - for (auto i = pos + 1; i < E->getNumArgs (); i++) { - auto arg = args[i]; + auto parsers = genParsers (qval->getString ()); + + if (parsers) { + if (parsers->size () != E->getNumArgs () - (pos + 1)) { + std::ostringstream err_buf; + err_buf << "number of arguments for " << fname + << " missmatches query string '" << + qval->getString().str() + << "', expected " << parsers->size () << " args" + << ", got " << (E->getNumArgs () - (pos + 1)) + << " args"; + print_error (err_buf.str (), E, this->pcontext); + + return false; + } + else { + for (auto i = pos + 1; i < E->getNumArgs (); i++) { + auto arg = args[i]; - if (arg) { - auto type = arg->getType ().split ().Ty; - type->dump (); + if (arg) { + if (!parsers->at(i - (pos + 1))(arg)) { + return false; + } + } + } } } } |