Blob Blame History Raw
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))
+}