Commit 70fa0a1c authored by Brenden Blanco's avatar Brenden Blanco

Translate multiple pointer dereference into bpr_probe_read

This commit adds support for multiple consecutive and nested pointer
dereference of function arguments that should be converted to
bpf_probe_read. The logic works by marking variables as needing a
probe_read if they come from the register argument, and then applying
this property transitively.

Supported syntax:
```
int trace_entry(struct pt_regs *ctx, struct file *file) {
    struct vfsmount *mnt = file->f_path.mnt;
    struct super_block *k = mnt->mnt_sb;
    const char *name = file->f_path.dentry->d_name.name;
```

Not supported: probe reads from map leaves, probe reads after explicit casts.

Fixes: #188
Signed-off-by: default avatarBrenden Blanco <bblanco@plumgrid.com>
parent 4afb3f87
...@@ -90,6 +90,27 @@ bool BMapDeclVisitor::VisitBuiltinType(const BuiltinType *T) { ...@@ -90,6 +90,27 @@ bool BMapDeclVisitor::VisitBuiltinType(const BuiltinType *T) {
return true; return true;
} }
class BProbeChecker : public clang::RecursiveASTVisitor<BProbeChecker> {
public:
bool VisitDeclRefExpr(clang::DeclRefExpr *E) {
if (E->getDecl()->hasAttr<UnavailableAttr>())
return false;
return true;
}
};
// Visit a piece of the AST and mark it as needing probe reads
class BProbeSetter : public clang::RecursiveASTVisitor<BProbeSetter> {
public:
explicit BProbeSetter(ASTContext &C) : C(C) {}
bool VisitDeclRefExpr(clang::DeclRefExpr *E) {
E->getDecl()->addAttr(UnavailableAttr::CreateImplicit(C, "ptregs"));
return true;
}
private:
ASTContext &C;
};
BTypeVisitor::BTypeVisitor(ASTContext &C, Rewriter &rewriter, vector<TableDesc> &tables) BTypeVisitor::BTypeVisitor(ASTContext &C, Rewriter &rewriter, vector<TableDesc> &tables)
: C(C), rewriter_(rewriter), out_(llvm::errs()), tables_(tables) { : C(C), rewriter_(rewriter), out_(llvm::errs()), tables_(tables) {
} }
...@@ -111,6 +132,7 @@ bool BTypeVisitor::VisitFunctionDecl(FunctionDecl *D) { ...@@ -111,6 +132,7 @@ bool BTypeVisitor::VisitFunctionDecl(FunctionDecl *D) {
} }
fn_args_.push_back(arg); fn_args_.push_back(arg);
if (fn_args_.size() > 1) { if (fn_args_.size() > 1) {
arg->addAttr(UnavailableAttr::CreateImplicit(C, "ptregs"));
size_t d = fn_args_.size() - 2; size_t d = fn_args_.size() - 2;
const char *reg = calling_conv_regs[d]; const char *reg = calling_conv_regs[d];
preamble += arg->getName().str() + " = " + fn_args_[0]->getName().str() + "->" + string(reg) + ";"; preamble += arg->getName().str() + " = " + fn_args_[0]->getName().str() + "->" + string(reg) + ";";
...@@ -260,30 +282,34 @@ bool BTypeVisitor::VisitCallExpr(CallExpr *Call) { ...@@ -260,30 +282,34 @@ bool BTypeVisitor::VisitCallExpr(CallExpr *Call) {
return true; return true;
} }
bool BTypeVisitor::TraverseMemberExpr(MemberExpr *E) { bool BTypeVisitor::VisitMemberExpr(MemberExpr *E) {
for (auto child : E->children()) if (visited_.find(E) != visited_.end()) return true;
if (!TraverseStmt(child))
return false; // Checks to see if the expression references something that needs to be run
if (!WalkUpFromMemberExpr(E)) // through bpf_probe_read.
return false; BProbeChecker checker;
if (checker.TraverseStmt(E))
return true; return true;
}
bool BTypeVisitor::VisitMemberExpr(MemberExpr *E) { Expr *base;
if (DeclRefExpr *Ref = dyn_cast<DeclRefExpr>(E->getBase()->IgnoreImplicit())) { SourceLocation rhs_start, op;
auto it = std::find(fn_args_.begin() + 1, fn_args_.end(), Ref->getDecl()); for (MemberExpr *M = E; M; M = dyn_cast<MemberExpr>(M->getBase())) {
if (it != fn_args_.end()) { visited_.insert(M);
FieldDecl *F = dyn_cast<FieldDecl>(E->getMemberDecl()); rhs_start = M->getLocEnd();
string base_type = Ref->getType()->getPointeeType().getAsString(); base = M->getBase();
op = M->getOperatorLoc();
if (M->isArrow())
break;
}
string rhs = rewriter_.getRewrittenText(SourceRange(rhs_start, E->getLocEnd()));
string base_type = base->getType()->getPointeeType().getAsString();
string pre, post; string pre, post;
pre = "({ " + E->getType().getAsString() + " _val; memset(&_val, 0, sizeof(_val));"; pre = "({ " + E->getType().getAsString() + " _val; memset(&_val, 0, sizeof(_val));";
pre += " bpf_probe_read(&_val, sizeof(_val), (u64)"; pre += " bpf_probe_read(&_val, sizeof(_val), (u64)";
post = " + offsetof(" + base_type + ", " + F->getName().str() + ")"; post = " + offsetof(" + base_type + ", " + rhs + ")";
post += "); _val; })"; post += "); _val; })";
rewriter_.InsertText(E->getLocStart(), pre); rewriter_.InsertText(E->getLocStart(), pre);
rewriter_.ReplaceText(SourceRange(E->getOperatorLoc(), E->getLocEnd()), post); rewriter_.ReplaceText(SourceRange(op, E->getLocEnd()), post);
}
}
return true; return true;
} }
...@@ -314,6 +340,12 @@ bool BTypeVisitor::VisitBinaryOperator(BinaryOperator *E) { ...@@ -314,6 +340,12 @@ bool BTypeVisitor::VisitBinaryOperator(BinaryOperator *E) {
} }
} }
} }
// copy probe attribute from RHS to LHS if present
BProbeChecker checker;
if (!checker.TraverseStmt(E->getRHS())) {
BProbeSetter setter(C);
setter.TraverseStmt(E->getLHS());
}
return true; return true;
} }
bool BTypeVisitor::VisitImplicitCastExpr(ImplicitCastExpr *E) { bool BTypeVisitor::VisitImplicitCastExpr(ImplicitCastExpr *E) {
...@@ -421,6 +453,11 @@ bool BTypeVisitor::VisitVarDecl(VarDecl *Decl) { ...@@ -421,6 +453,11 @@ bool BTypeVisitor::VisitVarDecl(VarDecl *Decl) {
} }
} }
} }
if (Expr *E = Decl->getInit()) {
BProbeChecker checker;
if (!checker.TraverseStmt(E))
Decl->addAttr(UnavailableAttr::CreateImplicit(C, "ptregs"));
}
return true; return true;
} }
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <map> #include <map>
#include <memory> #include <memory>
#include <set>
#include <string> #include <string>
#include <vector> #include <vector>
...@@ -62,7 +63,6 @@ class BTypeVisitor : public clang::RecursiveASTVisitor<BTypeVisitor> { ...@@ -62,7 +63,6 @@ class BTypeVisitor : public clang::RecursiveASTVisitor<BTypeVisitor> {
explicit BTypeVisitor(clang::ASTContext &C, clang::Rewriter &rewriter, explicit BTypeVisitor(clang::ASTContext &C, clang::Rewriter &rewriter,
std::vector<TableDesc> &tables); std::vector<TableDesc> &tables);
bool TraverseCallExpr(clang::CallExpr *Call); bool TraverseCallExpr(clang::CallExpr *Call);
bool TraverseMemberExpr(clang::MemberExpr *E);
bool VisitFunctionDecl(clang::FunctionDecl *D); bool VisitFunctionDecl(clang::FunctionDecl *D);
bool VisitCallExpr(clang::CallExpr *Call); bool VisitCallExpr(clang::CallExpr *Call);
bool VisitVarDecl(clang::VarDecl *Decl); bool VisitVarDecl(clang::VarDecl *Decl);
...@@ -76,6 +76,7 @@ class BTypeVisitor : public clang::RecursiveASTVisitor<BTypeVisitor> { ...@@ -76,6 +76,7 @@ class BTypeVisitor : public clang::RecursiveASTVisitor<BTypeVisitor> {
llvm::raw_ostream &out_; /// for debugging llvm::raw_ostream &out_; /// for debugging
std::vector<TableDesc> &tables_; /// store the open FDs std::vector<TableDesc> &tables_; /// store the open FDs
std::vector<clang::ParmVarDecl *> fn_args_; std::vector<clang::ParmVarDecl *> fn_args_;
std::set<clang::Expr *> visited_;
}; };
// A helper class to the frontend action, walks the decls // A helper class to the frontend action, walks the decls
......
...@@ -130,5 +130,40 @@ BPF_HASH(table3, u32, int); ...@@ -130,5 +130,40 @@ BPF_HASH(table3, u32, int);
""" """
b = BPF(text=text, debug=0) b = BPF(text=text, debug=0)
def test_consecutive_probe_read(self):
text = """
#include <linux/fs.h>
#include <linux/mount.h>
BPF_HASH(table1, struct super_block *);
int trace_entry(struct pt_regs *ctx, struct file *file) {
if (!file) return 0;
struct vfsmount *mnt = file->f_path.mnt;
if (mnt) {
struct super_block *k = mnt->mnt_sb;
u64 zero = 0;
table1.update(&k, &zero);
k = mnt->mnt_sb;
table1.update(&k, &zero);
}
return 0;
}
"""
b = BPF(text=text, debug=0)
fn = b.load_func("trace_entry", BPF.KPROBE)
def test_nested_probe_read(self):
text = """
#include <linux/fs.h>
int trace_entry(struct pt_regs *ctx, struct file *file) {
if (!file) return 0;
const char *name = file->f_path.dentry->d_name.name;
bpf_trace_printk("%s\\n", name);
return 0;
}
"""
b = BPF(text=text, debug=0)
fn = b.load_func("trace_entry", BPF.KPROBE)
if __name__ == "__main__": if __name__ == "__main__":
main() main()
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