]> source.dussan.org Git - rspamd.git/commitdiff
Write basics for arguments checks.
authorVsevolod Stakhov <vsevolod@highsecure.ru>
Wed, 11 Nov 2015 13:07:32 +0000 (13:07 +0000)
committerVsevolod Stakhov <vsevolod@highsecure.ru>
Wed, 11 Nov 2015 13:07:32 +0000 (13:07 +0000)
clang-plugin/printf_check.cc

index 4c0e8edd88afb2a5a7bb51f3761493d6dd742392..d0ec55f6104b07b43ed24826fe2bbedbedf9c304 100644 (file)
 #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;
+                                                               }
+                                                       }
+                                               }
                                        }
                                }
                        }