Commit 471f1ea1 authored by Paul Chaignon's avatar Paul Chaignon

Fix dereference replacements for pointers to pointers

Currently, the bcc rewriter is unable to track external pointers if
there is more than a single level of indirection (e.g., pointer to
external pointer).  For example, in the following, the rewriter is
unable to detect that ptr2 doesn't need a call to bpf_probe_read,
only *ptr2 do.

int test(struct pt_regs *ctx, struct sock *sk) {
    struct sock *ptr1;
    struct sock **ptr2 = &ptr1;
    *ptr2 = sk;
    return ((struct sock *)(*ptr2))->sk_daddr;
}

This commit fixes this issue by tracking the levels of indirections
in addition to the variable declarations (identifies each variable).
When traversing dereferences, the level of indirections is used to
decide whether the base expression is an external pointer.  The level
of indirections is inherited when a pointer is assigned to a new
variable (assignments and function calls).
parent 492acf7f
...@@ -82,6 +82,8 @@ const char **get_call_conv(void) { ...@@ -82,6 +82,8 @@ const char **get_call_conv(void) {
using std::map; using std::map;
using std::move; using std::move;
using std::set; using std::set;
using std::tuple;
using std::make_tuple;
using std::string; using std::string;
using std::to_string; using std::to_string;
using std::unique_ptr; using std::unique_ptr;
...@@ -90,15 +92,19 @@ using namespace clang; ...@@ -90,15 +92,19 @@ using namespace clang;
class ProbeChecker : public RecursiveASTVisitor<ProbeChecker> { class ProbeChecker : public RecursiveASTVisitor<ProbeChecker> {
public: public:
explicit ProbeChecker(Expr *arg, const set<Decl *> &ptregs, bool track_helpers) explicit ProbeChecker(Expr *arg, const set<tuple<Decl *, int>> &ptregs,
bool track_helpers, bool is_assign)
: needs_probe_(false), is_transitive_(false), ptregs_(ptregs), : needs_probe_(false), is_transitive_(false), ptregs_(ptregs),
track_helpers_(track_helpers) { track_helpers_(track_helpers), nb_derefs_(0), is_assign_(is_assign) {
if (arg) { if (arg) {
TraverseStmt(arg); TraverseStmt(arg);
if (arg->getType()->isPointerType()) if (arg->getType()->isPointerType())
is_transitive_ = needs_probe_; is_transitive_ = needs_probe_;
} }
} }
explicit ProbeChecker(Expr *arg, const set<tuple<Decl *, int>> &ptregs,
bool is_transitive)
: ProbeChecker(arg, ptregs, is_transitive, false) {}
bool VisitCallExpr(CallExpr *E) { bool VisitCallExpr(CallExpr *E) {
needs_probe_ = false; needs_probe_ = false;
if (!track_helpers_) if (!track_helpers_)
...@@ -108,40 +114,78 @@ class ProbeChecker : public RecursiveASTVisitor<ProbeChecker> { ...@@ -108,40 +114,78 @@ class ProbeChecker : public RecursiveASTVisitor<ProbeChecker> {
return false; return false;
} }
bool VisitMemberExpr(MemberExpr *M) { bool VisitMemberExpr(MemberExpr *M) {
if (ptregs_.find(M->getMemberDecl()) != ptregs_.end()) { tuple<Decl *, int> pt = make_tuple(M->getMemberDecl(), nb_derefs_);
if (ptregs_.find(pt) != ptregs_.end()) {
needs_probe_ = true; needs_probe_ = true;
return false; return false;
} }
return true; return true;
} }
bool VisitUnaryOperator(UnaryOperator *E) {
if (E->getOpcode() == UO_Deref)
nb_derefs_++;
else if (E->getOpcode() == UO_AddrOf)
nb_derefs_--;
return true;
}
bool VisitDeclRefExpr(DeclRefExpr *E) { bool VisitDeclRefExpr(DeclRefExpr *E) {
if (ptregs_.find(E->getDecl()) != ptregs_.end()) if (is_assign_) {
needs_probe_ = true; // We're looking for an external pointer, regardless of the number of
// dereferences.
for(auto p : ptregs_) {
if (std::get<0>(p) == E->getDecl()) {
needs_probe_ = true;
nb_derefs_ += std::get<1>(p);
return false;
}
}
} else {
tuple<Decl *, int> pt = make_tuple(E->getDecl(), nb_derefs_);
if (ptregs_.find(pt) != ptregs_.end())
needs_probe_ = true;
}
return true; return true;
} }
bool needs_probe() const { return needs_probe_; } bool needs_probe() const { return needs_probe_; }
bool is_transitive() const { return is_transitive_; } bool is_transitive() const { return is_transitive_; }
int get_nb_derefs() const { return nb_derefs_; }
private: private:
bool needs_probe_; bool needs_probe_;
bool is_transitive_; bool is_transitive_;
const set<Decl *> &ptregs_; const set<tuple<Decl *, int>> &ptregs_;
bool track_helpers_; bool track_helpers_;
// Nb of dereferences we go through before finding the external pointer.
// A negative number counts the number of addrof.
int nb_derefs_;
bool is_assign_;
}; };
// Visit a piece of the AST and mark it as needing probe reads // Visit a piece of the AST and mark it as needing probe reads
class ProbeSetter : public RecursiveASTVisitor<ProbeSetter> { class ProbeSetter : public RecursiveASTVisitor<ProbeSetter> {
public: public:
explicit ProbeSetter(set<Decl *> *ptregs) : ptregs_(ptregs) {} explicit ProbeSetter(set<tuple<Decl *, int>> *ptregs, int nb_addrof)
: ptregs_(ptregs), nb_derefs_(-nb_addrof) {}
bool VisitDeclRefExpr(DeclRefExpr *E) { bool VisitDeclRefExpr(DeclRefExpr *E) {
ptregs_->insert(E->getDecl()); tuple<Decl *, int> pt = make_tuple(E->getDecl(), nb_derefs_);
ptregs_->insert(pt);
return true;
}
explicit ProbeSetter(set<tuple<Decl *, int>> *ptregs)
: ProbeSetter(ptregs, 0) {}
bool VisitUnaryOperator(UnaryOperator *E) {
if (E->getOpcode() == UO_Deref)
nb_derefs_++;
return true; return true;
} }
bool VisitMemberExpr(MemberExpr *M) { bool VisitMemberExpr(MemberExpr *M) {
ptregs_->insert(M->getMemberDecl()); tuple<Decl *, int> pt = make_tuple(M->getMemberDecl(), nb_derefs_);
ptregs_->insert(pt);
return false; return false;
} }
private: private:
set<Decl *> *ptregs_; set<tuple<Decl *, int>> *ptregs_;
// Nb of dereferences we go through before getting to the actual variable.
int nb_derefs_;
}; };
MapVisitor::MapVisitor(set<Decl *> &m) : m_(m) {} MapVisitor::MapVisitor(set<Decl *> &m) : m_(m) {}
...@@ -155,9 +199,10 @@ bool MapVisitor::VisitCallExpr(CallExpr *Call) { ...@@ -155,9 +199,10 @@ bool MapVisitor::VisitCallExpr(CallExpr *Call) {
return true; return true;
if (memb_name == "update" || memb_name == "insert") { if (memb_name == "update" || memb_name == "insert") {
if (ProbeChecker(Call->getArg(1), ptregs_, true).needs_probe()) { ProbeChecker checker = ProbeChecker(Call->getArg(1), ptregs_, true,
true);
if (checker.needs_probe())
m_.insert(Ref->getDecl()); m_.insert(Ref->getDecl());
}
} }
} }
} }
...@@ -165,13 +210,16 @@ bool MapVisitor::VisitCallExpr(CallExpr *Call) { ...@@ -165,13 +210,16 @@ bool MapVisitor::VisitCallExpr(CallExpr *Call) {
return true; return true;
} }
ProbeVisitor::ProbeVisitor(ASTContext &C, Rewriter &rewriter, set<Decl *> &m, bool track_helpers) : ProbeVisitor::ProbeVisitor(ASTContext &C, Rewriter &rewriter,
set<Decl *> &m, bool track_helpers) :
C(C), rewriter_(rewriter), m_(m), track_helpers_(track_helpers) {} C(C), rewriter_(rewriter), m_(m), track_helpers_(track_helpers) {}
bool ProbeVisitor::VisitVarDecl(VarDecl *Decl) { bool ProbeVisitor::VisitVarDecl(VarDecl *D) {
if (Expr *E = Decl->getInit()) { if (Expr *E = D->getInit()) {
if (ProbeChecker(E, ptregs_, track_helpers_).is_transitive() || IsContextMemberExpr(E)) { ProbeChecker checker = ProbeChecker(E, ptregs_, track_helpers_, true);
set_ptreg(Decl); if (checker.is_transitive() || IsContextMemberExpr(E)) {
tuple<Decl *, int> pt = make_tuple(D, checker.get_nb_derefs());
set_ptreg(pt);
} }
} }
return true; return true;
...@@ -181,8 +229,13 @@ bool ProbeVisitor::VisitCallExpr(CallExpr *Call) { ...@@ -181,8 +229,13 @@ bool ProbeVisitor::VisitCallExpr(CallExpr *Call) {
if (F->hasBody()) { if (F->hasBody()) {
unsigned i = 0; unsigned i = 0;
for (auto arg : Call->arguments()) { for (auto arg : Call->arguments()) {
if (ProbeChecker(arg, ptregs_, track_helpers_).needs_probe()) ProbeChecker checker = ProbeChecker(arg, ptregs_, track_helpers_,
ptregs_.insert(F->getParamDecl(i)); true);
if (checker.needs_probe()) {
tuple<Decl *, int> pt = make_tuple(F->getParamDecl(i),
checker.get_nb_derefs());
ptregs_.insert(pt);
}
++i; ++i;
} }
if (fn_visited_.find(F) == fn_visited_.end()) { if (fn_visited_.find(F) == fn_visited_.end()) {
...@@ -197,8 +250,14 @@ bool ProbeVisitor::VisitBinaryOperator(BinaryOperator *E) { ...@@ -197,8 +250,14 @@ bool ProbeVisitor::VisitBinaryOperator(BinaryOperator *E) {
if (!E->isAssignmentOp()) if (!E->isAssignmentOp())
return true; return true;
// copy probe attribute from RHS to LHS if present // copy probe attribute from RHS to LHS if present
if (ProbeChecker(E->getRHS(), ptregs_, track_helpers_).is_transitive()) { ProbeChecker checker = ProbeChecker(E->getRHS(), ptregs_, track_helpers_,
ProbeSetter setter(&ptregs_); true);
if (checker.is_transitive()) {
// The negative of the number of dereferences is the number of addrof. In
// an assignment, if we went through n addrof before getting the external
// pointer, then we'll need n dereferences on the left-hand side variable
// to get to the external pointer.
ProbeSetter setter(&ptregs_, -checker.get_nb_derefs());
setter.TraverseStmt(E->getLHS()); setter.TraverseStmt(E->getLHS());
} else if (E->getRHS()->getStmtClass() == Stmt::CallExprClass) { } else if (E->getRHS()->getStmtClass() == Stmt::CallExprClass) {
CallExpr *Call = dyn_cast<CallExpr>(E->getRHS()); CallExpr *Call = dyn_cast<CallExpr>(E->getRHS());
...@@ -211,8 +270,13 @@ bool ProbeVisitor::VisitBinaryOperator(BinaryOperator *E) { ...@@ -211,8 +270,13 @@ bool ProbeVisitor::VisitBinaryOperator(BinaryOperator *E) {
if (memb_name == "lookup" || memb_name == "lookup_or_init") { if (memb_name == "lookup" || memb_name == "lookup_or_init") {
if (m_.find(Ref->getDecl()) != m_.end()) { if (m_.find(Ref->getDecl()) != m_.end()) {
// Retrieved an external pointer from a map, mark LHS as external pointer. // Retrieved an ext. pointer from a map, mark LHS as ext. pointer.
ProbeSetter setter(&ptregs_); // Pointers from maps always need a single dereference to get the
// actual value. The value may be an external pointer but cannot
// be a pointer to an external pointer as the verifier prohibits
// storing known pointers (to map values, context, the stack, or
// the packet) in maps.
ProbeSetter setter(&ptregs_, 1);
setter.TraverseStmt(E->getLHS()); setter.TraverseStmt(E->getLHS());
} }
} }
...@@ -230,10 +294,10 @@ bool ProbeVisitor::VisitUnaryOperator(UnaryOperator *E) { ...@@ -230,10 +294,10 @@ bool ProbeVisitor::VisitUnaryOperator(UnaryOperator *E) {
return true; return true;
if (memb_visited_.find(E) != memb_visited_.end()) if (memb_visited_.find(E) != memb_visited_.end())
return true; return true;
if (!ProbeChecker(E, ptregs_, track_helpers_).needs_probe()) Expr *sub = E->getSubExpr();
if (!ProbeChecker(sub, ptregs_, track_helpers_).needs_probe())
return true; return true;
memb_visited_.insert(E); memb_visited_.insert(E);
Expr *sub = E->getSubExpr();
string rhs = rewriter_.getRewrittenText(expansionRange(sub->getSourceRange())); string rhs = rewriter_.getRewrittenText(expansionRange(sub->getSourceRange()));
string text; string text;
text = "({ typeof(" + E->getType().getAsString() + ") _val; __builtin_memset(&_val, 0, sizeof(_val));"; text = "({ typeof(" + E->getType().getAsString() + ") _val; __builtin_memset(&_val, 0, sizeof(_val));";
...@@ -922,7 +986,8 @@ void BTypeConsumer::HandleTranslationUnit(ASTContext &Context) { ...@@ -922,7 +986,8 @@ void BTypeConsumer::HandleTranslationUnit(ASTContext &Context) {
type.substr(0, 19) == "struct tracepoint__") type.substr(0, 19) == "struct tracepoint__")
probe_visitor1_.set_ctx(arg); probe_visitor1_.set_ctx(arg);
} else if (!arg->getType()->isFundamentalType()) { } else if (!arg->getType()->isFundamentalType()) {
probe_visitor1_.set_ptreg(arg); tuple<Decl *, int> pt = make_tuple(arg, 0);
probe_visitor1_.set_ptreg(pt);
} }
} }
......
...@@ -47,10 +47,10 @@ class MapVisitor : public clang::RecursiveASTVisitor<MapVisitor> { ...@@ -47,10 +47,10 @@ class MapVisitor : public clang::RecursiveASTVisitor<MapVisitor> {
public: public:
explicit MapVisitor(std::set<clang::Decl *> &m); explicit MapVisitor(std::set<clang::Decl *> &m);
bool VisitCallExpr(clang::CallExpr *Call); bool VisitCallExpr(clang::CallExpr *Call);
void set_ptreg(clang::Decl *D) { ptregs_.insert(D); } void set_ptreg(std::tuple<clang::Decl *, int> &pt) { ptregs_.insert(pt); }
private: private:
std::set<clang::Decl *> &m_; std::set<clang::Decl *> &m_;
std::set<clang::Decl *> ptregs_; std::set<std::tuple<clang::Decl *, int>> ptregs_;
}; };
// Type visitor and rewriter for B programs. // Type visitor and rewriter for B programs.
...@@ -95,9 +95,9 @@ class ProbeVisitor : public clang::RecursiveASTVisitor<ProbeVisitor> { ...@@ -95,9 +95,9 @@ class ProbeVisitor : public clang::RecursiveASTVisitor<ProbeVisitor> {
bool VisitBinaryOperator(clang::BinaryOperator *E); bool VisitBinaryOperator(clang::BinaryOperator *E);
bool VisitUnaryOperator(clang::UnaryOperator *E); bool VisitUnaryOperator(clang::UnaryOperator *E);
bool VisitMemberExpr(clang::MemberExpr *E); bool VisitMemberExpr(clang::MemberExpr *E);
void set_ptreg(clang::Decl *D) { ptregs_.insert(D); } void set_ptreg(std::tuple<clang::Decl *, int> &pt) { ptregs_.insert(pt); }
void set_ctx(clang::Decl *D) { ctx_ = D; } void set_ctx(clang::Decl *D) { ctx_ = D; }
std::set<clang::Decl *> get_ptregs() { return ptregs_; } std::set<std::tuple<clang::Decl *, int>> get_ptregs() { return ptregs_; }
private: private:
bool IsContextMemberExpr(clang::Expr *E); bool IsContextMemberExpr(clang::Expr *E);
clang::SourceRange expansionRange(clang::SourceRange range); clang::SourceRange expansionRange(clang::SourceRange range);
...@@ -108,7 +108,7 @@ class ProbeVisitor : public clang::RecursiveASTVisitor<ProbeVisitor> { ...@@ -108,7 +108,7 @@ class ProbeVisitor : public clang::RecursiveASTVisitor<ProbeVisitor> {
clang::Rewriter &rewriter_; clang::Rewriter &rewriter_;
std::set<clang::Decl *> fn_visited_; std::set<clang::Decl *> fn_visited_;
std::set<clang::Expr *> memb_visited_; std::set<clang::Expr *> memb_visited_;
std::set<clang::Decl *> ptregs_; std::set<std::tuple<clang::Decl *, int>> ptregs_;
std::set<clang::Decl *> &m_; std::set<clang::Decl *> &m_;
clang::Decl *ctx_; clang::Decl *ctx_;
bool track_helpers_; bool track_helpers_;
...@@ -117,7 +117,8 @@ class ProbeVisitor : public clang::RecursiveASTVisitor<ProbeVisitor> { ...@@ -117,7 +117,8 @@ class ProbeVisitor : public clang::RecursiveASTVisitor<ProbeVisitor> {
// A helper class to the frontend action, walks the decls // A helper class to the frontend action, walks the decls
class BTypeConsumer : public clang::ASTConsumer { class BTypeConsumer : public clang::ASTConsumer {
public: public:
explicit BTypeConsumer(clang::ASTContext &C, BFrontendAction &fe, clang::Rewriter &rewriter, std::set<clang::Decl *> &map); explicit BTypeConsumer(clang::ASTContext &C, BFrontendAction &fe,
clang::Rewriter &rewriter, std::set<clang::Decl *> &m);
void HandleTranslationUnit(clang::ASTContext &Context) override; void HandleTranslationUnit(clang::ASTContext &Context) override;
private: private:
BFrontendAction &fe_; BFrontendAction &fe_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment