RHEL Engineering is moving the tracking of its product development work on RHEL 6 through RHEL 9 to Red Hat Jira (issues.redhat.com). If you're a Red Hat customer, please continue to file support cases via the Red Hat customer portal. If you're not, please head to the "RHEL project" in Red Hat Jira and file new tickets here. Individual Bugzilla bugs in the statuses "NEW", "ASSIGNED", and "POST" are being migrated throughout September 2023. Bugs of Red Hat partners with an assigned Engineering Partner Manager (EPM) are migrated in late September as per pre-agreed dates. Bugs against components "kernel", "kernel-rt", and "kpatch" are only migrated if still in "NEW" or "ASSIGNED". If you cannot log in to RH Jira, please consult article #7032570. That failing, please send an e-mail to the RH Jira admins at rh-issues@redhat.com to troubleshoot your issue as a user management inquiry. The email creates a ServiceNow ticket with Red Hat. Individual Bugzilla bugs that are migrated will be moved to status "CLOSED", resolution "MIGRATED", and set with "MigratedToJIRA" in "Keywords". The link to the successor Jira issue will be found under "Links", have a little "two-footprint" icon next to it, and direct you to the "RHEL project" in Red Hat Jira (issue links are of type "https://issues.redhat.com/browse/RHEL-XXXX", where "X" is a digit). This same link will be available in a blue banner at the top of the page informing you that that bug has been migrated.
Bug 1430828 - Ensure mm cannot be unmapped in task_exe_file()
Summary: Ensure mm cannot be unmapped in task_exe_file()
Keywords:
Status: CLOSED ERRATA
Alias: None
Product: Red Hat Enterprise Linux 7
Classification: Red Hat
Component: systemtap
Version: 7.4
Hardware: Unspecified
OS: Linux
low
low
Target Milestone: rc
: ---
Assignee: David Smith
QA Contact: Martin Cermak
URL:
Whiteboard:
Depends On: 1430861
Blocks:
TreeView+ depends on / blocked
 
Reported: 2017-03-09 16:31 UTC by Aaron Tomlin
Modified: 2020-12-14 08:19 UTC (History)
8 users (show)

Fixed In Version: systemtap-3.1-3.el7
Doc Type: If docs needed, set a value
Doc Text:
Clone Of:
Environment:
Last Closed: 2017-08-01 09:34:40 UTC
Target Upstream Version:
Embargoed:


Attachments (Terms of Use)


Links
System ID Private Priority Status Summary Last Updated
Red Hat Knowledge Base (Solution) 2979111 0 None None None 2017-03-25 15:50:22 UTC
Red Hat Product Errata RHBA-2017:2301 0 normal SHIPPED_LIVE systemtap bug fix and enhancement update 2017-08-01 12:41:08 UTC

Description Aaron Tomlin 2017-03-09 16:31:20 UTC
Problem description:
--------------------

The use of get_task_struct(p) in tapset/linux/task.stp:task_exe_file() is
primarily to ensure p->mm cannot be unexpectedly unmapped.
However this assumption is not true since get_task_struct() does not
increment mm_user. So task_exe_file() can race with mmput() which can
lead to an Oops. Also task_exe_file() should have /* guru */ added.

Version-Release number of selected component (if applicable):

systemtap-client-3.0-7.el7 (*)

[*] Note: Upstream version is also affected i.e.
          since [to date] commit de25b7b ("For custom built upstream
          kernels on Debian, fix version detection")

Additional info:

In task_exe_file(), we never invoke fput() due to the 'goto out' statement
(see the definition of the STAP_RETURN macro) therefore the file object is
leaked. This is to be resolved by another bug report.

Comment 4 David Smith 2017-03-10 20:12:26 UTC
(In reply to Aaron Tomlin from comment #0)
> Problem description:
> --------------------
> 
> The use of get_task_struct(p) in tapset/linux/task.stp:task_exe_file() is
> primarily to ensure p->mm cannot be unexpectedly unmapped.
> However this assumption is not true since get_task_struct() does not
> increment mm_user. So task_exe_file() can race with mmput() which can
> lead to an Oops. Also task_exe_file() should have /* guru */ added.

I think we're going to need to back up here. Can you show me a reproducer of this oops you mentioned?

Why do you think task_exe_file() should have guru added?

> Version-Release number of selected component (if applicable):
> 
> systemtap-client-3.0-7.el7 (*)
> 
> [*] Note: Upstream version is also affected i.e.
>           since [to date] commit de25b7b ("For custom built upstream
>           kernels on Debian, fix version detection")
> 
> Additional info:
> 
> In task_exe_file(), we never invoke fput() due to the 'goto out' statement
> (see the definition of the STAP_RETURN macro) therefore the file object is
> leaked. This is to be resolved by another bug report.

That does appear to be a problem. I don't mind fixing that one here or in a different bug report - whatever makes the most sense.

Comment 6 Aaron Tomlin 2017-03-13 11:18:31 UTC
(In reply to David Smith from comment #4)
[ ... ]
> I think we're going to need to back up here. Can you show me a reproducer of
> this oops you mentioned?

No actual reproducer was used. This was found by code review.
I can see about reproducing this for real if required.

> Why do you think task_exe_file() should have guru added?

In my opinion, extra care should be taken when this function is used since
as is, it is not safe.

> > Version-Release number of selected component (if applicable):
> > 
> > systemtap-client-3.0-7.el7 (*)
> > 
> > [*] Note: Upstream version is also affected i.e.
> >           since [to date] commit de25b7b ("For custom built upstream
> >           kernels on Debian, fix version detection")
> > 
> > Additional info:
> > 
> > In task_exe_file(), we never invoke fput() due to the 'goto out' statement
> > (see the definition of the STAP_RETURN macro) therefore the file object is
> > leaked. This is to be resolved by another bug report.
> 
> That does appear to be a problem. I don't mind fixing that one here or in a
> different bug report - whatever makes the most sense.

This is being tracked via bug 1430861.

Comment 8 David Smith 2017-03-20 19:39:10 UTC
(In reply to Aaron Tomlin from comment #6)
> (In reply to David Smith from comment #4)
> [ ... ]
> > I think we're going to need to back up here. Can you show me a reproducer of
> > this oops you mentioned?
> 
> No actual reproducer was used. This was found by code review.
> I can see about reproducing this for real if required.

Code inspection is fine. We actually test this function in the testsuite, but only against 'current'.

> > Why do you think task_exe_file() should have guru added?
> 
> In my opinion, extra care should be taken when this function is used since
> as is, it is not safe.

So if it was (somehow) safe, it wouldn't need guru? If so, the problem isn't the lack of 'guru', it is the lack of safety.

> > > Version-Release number of selected component (if applicable):
> > > 
> > > systemtap-client-3.0-7.el7 (*)
> > > 
> > > [*] Note: Upstream version is also affected i.e.
> > >           since [to date] commit de25b7b ("For custom built upstream
> > >           kernels on Debian, fix version detection")
> > > 
> > > Additional info:
> > > 
> > > In task_exe_file(), we never invoke fput() due to the 'goto out' statement
> > > (see the definition of the STAP_RETURN macro) therefore the file object is
> > > leaked. This is to be resolved by another bug report.
> > 
> > That does appear to be a problem. I don't mind fixing that one here or in a
> > different bug report - whatever makes the most sense.
> 
> This is being tracked via bug 1430861.

One possible solution here would be to rewrite/rename this function to something like: 'current_exe_file'. It would only return the file struct of current's exe. If we're stopped inside current, current's mm isn't going anywhere.

Comment 9 Aaron Tomlin 2017-03-22 18:28:38 UTC
(In reply to David Smith from comment #8)
> Code inspection is fine. We actually test this function in the testsuite,
> but only against 'current'.

OK.

> One possible solution here would be to rewrite/rename this function to
> something like: 'current_exe_file'. It would only return the file struct of
> current's exe. If we're stopped inside current, current's mm isn't going
> anywhere.

How about the following:

diff --git a/tapset/linux/task.stp b/tapset/linux/task.stp
index 12e2868..589992c 100644
--- a/tapset/linux/task.stp
+++ b/tapset/linux/task.stp
@@ -781,39 +781,23 @@ function task_cwd_path:long(task:long)
 %}
 
 /**
- *   sfunction task_cwd_path - get the file struct pointer for a task's executable file
- *
- *   @task: task_struct pointer.
+ *   sfunction current_exe_file - Returns the file struct pointer of the current
+ *                                process' executable file
  */
 function task_exe_file:long(task:long)
 %{ /* pure */
-	struct task_struct *task
-		= (struct task_struct *)(unsigned long)STAP_ARG_task;
-	struct mm_struct *mm = NULL;
-	struct file *exe_file = NULL;
-
-	// Before using the task_struct pointer, make sure it is valid
-	// to read.
-	(void)kderef_buffer(NULL, task, sizeof(struct task_struct));
+	struct mm_struct *mm;
+	struct file *exe_file;
 
-	// OK, we now know it is valid to read. But, is it really a
-	// task struct?
-	if (!_stp_task_struct_valid(task)) {
-		STAP_ERROR ("invalid task struct pointer");
-	}
+	mm = exe_file = NULL;
 
-	// We'd like to call get_task_mm()/mmput() here, but they can
-	// sleep. So, let's hope incrementing the task's usage (by
-	// calling get_task_struct) is enough to keep the mm around.
-	get_task_struct(task);
-	mm = task->mm;
+	mm = task_current()->mm;
 	if (mm)
 		exe_file = stap_find_exe_file(mm);
-	put_task_struct(task);
 
-	if (exe_file) {
-		STAP_RETURN((unsigned long)exe_file);
+	if (exe_file)
 		fput(exe_file);
-	}
+
+	STAP_RETURN((unsigned long)exe_file);
 	CATCH_DEREF_FAULT();
 %}

Comment 10 Aaron Tomlin 2017-03-22 20:45:01 UTC
(In reply to Aaron Tomlin from comment #9)
> How about the following:

Correction to the patch in comment #9 in particular to the function name
and parameter:

diff --git a/tapset/linux/task.stp b/tapset/linux/task.stp
index 12e2868..4974df5 100644
--- a/tapset/linux/task.stp
+++ b/tapset/linux/task.stp
@@ -781,39 +781,23 @@ function task_cwd_path:long(task:long)
 %}
 
 /**
- *   sfunction task_cwd_path - get the file struct pointer for a task's executable file
- *
- *   @task: task_struct pointer.
+ *   sfunction current_exe_file - Returns the file struct pointer of the current
+ *                                process' executable file
  */
-function task_exe_file:long(task:long)
+function current_exe_file:long()
 %{ /* pure */
-	struct task_struct *task
-		= (struct task_struct *)(unsigned long)STAP_ARG_task;
-	struct mm_struct *mm = NULL;
-	struct file *exe_file = NULL;
-
-	// Before using the task_struct pointer, make sure it is valid
-	// to read.
-	(void)kderef_buffer(NULL, task, sizeof(struct task_struct));
+	struct mm_struct *mm;
+	struct file *exe_file;
 
-	// OK, we now know it is valid to read. But, is it really a
-	// task struct?
-	if (!_stp_task_struct_valid(task)) {
-		STAP_ERROR ("invalid task struct pointer");
-	}
+	mm = exe_file = NULL;
 
-	// We'd like to call get_task_mm()/mmput() here, but they can
-	// sleep. So, let's hope incrementing the task's usage (by
-	// calling get_task_struct) is enough to keep the mm around.
-	get_task_struct(task);
-	mm = task->mm;
+	mm = task_current()->mm;
 	if (mm)
 		exe_file = stap_find_exe_file(mm);
-	put_task_struct(task);
 
-	if (exe_file) {
-		STAP_RETURN((unsigned long)exe_file);
+	if (exe_file)
 		fput(exe_file);
-	}
+
+	STAP_RETURN((unsigned long)exe_file);
 	CATCH_DEREF_FAULT();
 %}

Comment 11 David Smith 2017-03-22 21:06:37 UTC
I took the 'current_exe_file' route in the following upstream commit (before I saw your patch):

<https://www.sourceware.org/git/gitweb.cgi?p=systemtap.git;a=commit;h=4ed3fbc366d168806f94a615f3339b4d2fbd5a75>

This should fix the problem.

Comment 12 Aaron Tomlin 2017-03-22 21:39:58 UTC
(In reply to Aaron Tomlin from comment #10)

Finally compile tested version (XXX can we add /* unprivileged */ to
current_exe_file()? ...):

diff --git a/tapset/linux/task.stp b/tapset/linux/task.stp
index 12e2868..3395c16 100644
--- a/tapset/linux/task.stp
+++ b/tapset/linux/task.stp
@@ -781,39 +781,21 @@ function task_cwd_path:long(task:long)
 %}
 
 /**
- *   sfunction task_cwd_path - get the file struct pointer for a task's executable file
- *
- *   @task: task_struct pointer.
+ *   sfunction current_exe_file - Returns the file struct pointer of the current
+ *                                process' executable file
  */
-function task_exe_file:long(task:long)
+function current_exe_file:long()
 %{ /* pure */
-	struct task_struct *task
-		= (struct task_struct *)(unsigned long)STAP_ARG_task;
 	struct mm_struct *mm = NULL;
 	struct file *exe_file = NULL;
 
-	// Before using the task_struct pointer, make sure it is valid
-	// to read.
-	(void)kderef_buffer(NULL, task, sizeof(struct task_struct));
-
-	// OK, we now know it is valid to read. But, is it really a
-	// task struct?
-	if (!_stp_task_struct_valid(task)) {
-		STAP_ERROR ("invalid task struct pointer");
-	}
-
-	// We'd like to call get_task_mm()/mmput() here, but they can
-	// sleep. So, let's hope incrementing the task's usage (by
-	// calling get_task_struct) is enough to keep the mm around.
-	get_task_struct(task);
-	mm = task->mm;
+	mm = current->mm;
 	if (mm)
 		exe_file = stap_find_exe_file(mm);
-	put_task_struct(task);
 
-	if (exe_file) {
-		STAP_RETURN((unsigned long)exe_file);
+	if (exe_file)
 		fput(exe_file);
-	}
+
+	STAP_RETURN((unsigned long)exe_file);
 	CATCH_DEREF_FAULT();
 %}

Comment 13 Aaron Tomlin 2017-03-22 21:49:16 UTC
(In reply to David Smith from comment #11)
> I took the 'current_exe_file' route in the following upstream commit (before
> I saw your patch):

OK.

> <https://www.sourceware.org/git/gitweb.cgi?p=systemtap.git;a=commit;
> h=4ed3fbc366d168806f94a615f3339b4d2fbd5a75>
> 
> This should fix the problem.

Yes, your patch does resolve the issue.

Comment 16 errata-xmlrpc 2017-08-01 09:34:40 UTC
Since the problem described in this bug report should be
resolved in a recent advisory, it has been closed with a
resolution of ERRATA.

For information on the advisory, and where to find the updated
files, follow the link below.

If the solution does not work for you, open a new bug report.

https://access.redhat.com/errata/RHBA-2017:2301


Note You need to log in before you can comment on or make changes to this bug.