summaryrefslogtreecommitdiffstats
path: root/clang-plugin/printf_check.cc
diff options
context:
space:
mode:
authorVsevolod Stakhov <vsevolod@highsecure.ru>2015-11-11 13:07:32 +0000
committerVsevolod Stakhov <vsevolod@highsecure.ru>2015-11-11 13:07:32 +0000
commit2a406c67c4013e16bb97c317628deb8e19f190f5 (patch)
tree63b9962fd127e295d21b81832552bd862cde7cfc /clang-plugin/printf_check.cc
parent440d438fffc17b62ce08fb26d24a1ad0e36fbeb6 (diff)
downloadrspamd-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.cc204
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;
+ }
+ }
+ }
}
}
}