commit 6b4d1fcbfa2e9706749cd549ea30df2b67716a0f
Author: David Smith <dsmith@redhat.com>
Date: Fri Mar 10 10:15:48 2017 -0600
Fix BZ1425568 by updating task_dentry_path() to handle chroot().
* tapset/linux/dentry.stp (task_dentry_path): Fix task_dentry_path when
called on a target executable that has run chroot().
* testsuite/systemtap.base/task_dentry_path.exp: Add test for chroot'ed
executable.
* testsuite/systemtap.base/task_dentry_path3.stp: New test.
diff --git a/tapset/linux/dentry.stp b/tapset/linux/dentry.stp
index 589260e..0bc4753 100644
--- a/tapset/linux/dentry.stp
+++ b/tapset/linux/dentry.stp
@@ -190,6 +190,10 @@ function real_mount:long(vfsmnt:long)
* @dentry: direntry pointer.
* @vfsmnt: vfsmnt pointer.
*/
+/*
+ * Note that this function is based on __d_path() in RHEL6-era kernels
+ * and prepend_path() in RHEL7+ kernels.
+ */
function task_dentry_path:string(task:long,dentry:long,vfsmnt:long)
{
/*
@@ -207,6 +211,7 @@ function task_dentry_path:string(task:long,dentry:long,vfsmnt:long)
*/
dentry = & @cast(dentry, "dentry")
vfsmnt = & @cast(vfsmnt, "vfsmount")
+
if (@type_member_defined("dentry", d_op->d_dname)
&& dentry->d_op && dentry->d_op->d_dname
&& (!__dentry_IS_ROOT(dentry) || dentry != vfsmnt->mnt_root)) {
@@ -242,26 +247,34 @@ function task_dentry_path:string(task:long,dentry:long,vfsmnt:long)
root_vfsmnt = @cast(task, "task_struct")->fs->root->mnt
}
- while (1) {
- # If we've found the right dentry/vfsmnt, we're done.
- if (dentry == root_dentry && vfsmnt == root_vfsmnt)
- break;
+ if (@type_member_defined("mount", mnt_parent)) {
+ mnt = &@cast(real_mount(vfsmnt), "mount")
+ if (mnt == 0)
+ return "<unknown>"
+ }
+ # If we've found the right dentry/vfsmnt, we're done.
+ while (dentry != root_dentry || vfsmnt == root_vfsmnt) {
if (dentry == vfsmnt->mnt_root || __dentry_IS_ROOT(dentry)) {
- if (! @type_member_defined("vfsmount", mnt_parent)) {
- mnt = & @cast(real_mount(vfsmnt), "mount")
- if (mnt == 0)
- return "<unknown>"
+ /* Escaped? */
+ if (dentry != vfsmnt->mnt_root) {
+ return "<unknown>"
+ }
+ /* RHEL7+ kernels */
+ if (! @type_member_defined("vfsmount", mnt_parent)) {
/* Global root? */
- if (mnt->mnt_parent == vfsmnt)
- return sprintf("%s%s",
- d_name(mnt->mnt_mountpoint),
- name)
-
- dentry = mnt->mnt_mountpoint
- vfsmnt = & mnt->mnt_parent->mnt
+ if (mnt != mnt->mnt_parent) {
+ dentry = mnt->mnt_mountpoint
+ vfsmnt = & mnt->mnt_parent->mnt
+ mnt = mnt->mnt_parent
+ if (mnt == 0)
+ return "<unknown>"
+ continue
+ }
+ break
}
+ /* RHEL6-era kernels */
else {
/* Global root? */
if (vfsmnt->mnt_parent == vfsmnt)
@@ -271,11 +284,11 @@ function task_dentry_path:string(task:long,dentry:long,vfsmnt:long)
dentry = vfsmnt->mnt_mountpoint
vfsmnt = vfsmnt->mnt_parent
+ continue
}
- continue;
}
- name = __dentry_prepend(dentry, name);
- dentry = dentry->d_parent;
+ name = __dentry_prepend(dentry, name)
+ dentry = dentry->d_parent
}
return sprintf("/%s", name);
diff --git a/testsuite/systemtap.base/task_dentry_path.exp b/testsuite/systemtap.base/task_dentry_path.exp
index 00b9e02..3630401 100644
--- a/testsuite/systemtap.base/task_dentry_path.exp
+++ b/testsuite/systemtap.base/task_dentry_path.exp
@@ -5,6 +5,8 @@
set test_base "task_dentry_path"
set test "${test_base}"
+set test_name "$test_base (infinite loop)"
+set am_root [expr 0 == [exec id -u]]
# Only run on make installcheck
if {! [installtest_p]} { untested "$test"; return }
@@ -14,22 +16,45 @@ set ok 0
expect {
-timeout 150
-re {ERROR.*MAXACTION} { incr ok; exp_continue }
- timeout { fail "$test (timeout)" }
+ timeout { fail "$test_name (timeout)" }
eof { }
}
catch {close}; catch {wait}
-if {$ok == 1} { fail "$test ($ok)" } { pass "$test ($ok)" }
+if {$ok == 1} { fail "$test_name ($ok)" } { pass "$test_name" }
# Test the fix for PR19021 - the tapset function task_dentry_path()
# should handle more than just files.
set test "${test_base}2"
+set test_name "$test_base (synthetic files)"
spawn stap $srcdir/$subdir/$test.stp -c "echo hi | cat > /dev/null"
set ok 0
expect {
-timeout 150
-re {^pipe:\[[0-9]+\]\r\n} { incr ok; exp_continue }
- timeout { fail "$test (timeout)" }
+ timeout { fail "$test_name (timeout)" }
eof { }
}
catch {close}; catch {wait}
-if {$ok == 1} { pass "$test" } { fail "$test ($ok)" }
+if {$ok == 1} { pass "$test_name" } { fail "$test_name ($ok)" }
+
+# Test the fix for BZ1425568 - systemtap task_dentry_path crashes
+# under chroot.
+set test "${test_base}3"
+set test_name "$test_base (chroot)"
+
+# We've got to be root to successfully do a 'chroot'.
+if {!$am_root} {
+ untested "$test_name"
+} else {
+ set curdir [pwd]
+ spawn stap $srcdir/$subdir/$test.stp -c "perl -e 'chroot(qw(/tmp))'"
+ set ok 0
+ expect {
+ -timeout 150
+ -re "^${curdir}\r\n" { incr ok; exp_continue }
+ timeout { fail "$test_name (timeout)" }
+ eof { }
+ }
+ catch {close}; catch {wait}
+ if {$ok == 1} { pass "$test_name" } { fail "$test_name ($ok)" }
+}
diff --git a/testsuite/systemtap.base/task_dentry_path3.stp b/testsuite/systemtap.base/task_dentry_path3.stp
new file mode 100644
index 0000000..b04eb6b
--- /dev/null
+++ b/testsuite/systemtap.base/task_dentry_path3.stp
@@ -0,0 +1,6 @@
+probe kprocess.exit
+{
+ t = task_current()
+ println(task_dentry_path(t, @cast(t, "task_struct")->fs->pwd->dentry,
+ @cast(t,"task_struct")->fs->pwd->mnt))
+}