Skip to contentSkip to conversation

fix reading ESP during coredump

PATCH Superseded Nam Cao 4 participants 10 messages Nov 6, 2024 – Dec 22, 2024

Subsystems (4)

v1 of 3 Superseded Latest is v3 →

lore
Share & tools
lore

Nam Cao

author
#0 lore raw

cover fix reading ESP during coredump

Hi,

In /proc/PID/stat, there is the kstkesp field which is the stack pointer of a thread. While the thread is active, this field reads zero. But during a coredump, it should have a valid value.

However, at the moment, kstkesp is zero even during coredump.

The first commit fixes this problem, and the second commit adds a selftest to detect if this problem appears again in the future.

Nam Cao (2):
fs/proc: do_task_stat: Fix ESP not readable during coredump
selftests: coredump: Add stackdump test

 fs/proc/array.c                               |  36 ++--
 tools/testing/selftests/coredump/Makefile     |   7 +
 tools/testing/selftests/coredump/README.rst   |  50 ++++++
 tools/testing/selftests/coredump/stackdump    |  14 ++
 .../selftests/coredump/stackdump_test.c       | 154 ++++++++++++++++++
 5 files changed, 243 insertions(+), 18 deletions(-)
 create mode 100644 tools/testing/selftests/coredump/Makefile
 create mode 100644 tools/testing/selftests/coredump/README.rst
 create mode 100755 tools/testing/selftests/coredump/stackdump
 create mode 100644 tools/testing/selftests/coredump/stackdump_test.c
Signature · 1 line
2.39.5
author
#1 lore raw

PATCH 1/2 fs/proc: do_task_stat: Fix ESP not readable during coredump

Commit 0a1eb2d474ed ("fs/proc: Stop reporting eip and esp in /proc/PID/stat") disabled stack pointer reading, because it is generally dangerous to do so.

Commit fd7d56270b52 ("fs/proc: Report eip/esp in /prod/PID/stat for coredumping") made an exception for coredumping thread, because for this case it is safe.

The exception was later extended to all threads in a coredumping process by commit cb8f381f1613 ("fs/proc/array.c: allow reporting eip/esp for all coredumping threads").

The above two commits determine if a task is core dumping by checking the PF_EXITING and PF_DUMPCORE flags.

However, commit 92307383082d ("coredump: Don't perform any cleanups before dumping core") moved coredump to happen earlier and before PF_EXITING is set. Thus, the check of the PF_EXITING flag no longer works.

Instead, use task->signal->core_state to determine if coredump is happening. This pointer is set at the beginning of coredump and is cleared once coredump is done. Thus, while this pointer is not NULL, it is safe to read ESP.

Fixes 92307383082d ("coredump: Don't perform any cleanups before dumping core")
Cc Eric W. Biederman <[email protected]>
---
 fs/proc/array.c | 36 ++++++++++++++++++------------------
 1 file changed, 18 insertions(+), 18 deletions(-)
fs/proc/array.c +18 -18
index 34a47fb0c57f..2f1dbfcf143d 100644
@@ -489,25 +489,8 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns,
 	vsize = eip = esp = 0;
 	permitted = ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS | PTRACE_MODE_NOAUDIT);
 	mm = get_task_mm(task);
-	if (mm) {
+	if (mm)
 		vsize = task_vsize(mm);
-		/*
-		 * esp and eip are intentionally zeroed out.  There is no
-		 * non-racy way to read them without freezing the task.
-		 * Programs that need reliable values can use ptrace(2).
-		 *
-		 * The only exception is if the task is core dumping because
-		 * a program is not able to use ptrace(2) in that case. It is
-		 * safe because the task has stopped executing permanently.
-		 */
-		if (permitted && (task->flags & (PF_EXITING|PF_DUMPCORE))) {
-			if (try_get_task_stack(task)) {
-				eip = KSTK_EIP(task);
-				esp = KSTK_ESP(task);
-				put_task_stack(task);
-			}
-		}
-	}
 
 	sigemptyset(&sigign);
 	sigemptyset(&sigcatch);
@@ -534,6 +517,23 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns,
 		ppid = task_tgid_nr_ns(task->real_parent, ns);
 		pgid = task_pgrp_nr_ns(task, ns);
 
+		/*
+		 * esp and eip are intentionally zeroed out.  There is no
+		 * non-racy way to read them without freezing the task.
+		 * Programs that need reliable values can use ptrace(2).
+		 *
+		 * The only exception is if the task is core dumping because
+		 * a program is not able to use ptrace(2) in that case. It is
+		 * safe because the task has stopped executing permanently.
+		 */
+		if (permitted && task->signal->core_state) {
+			if (try_get_task_stack(task)) {
+				eip = KSTK_EIP(task);
+				esp = KSTK_ESP(task);
+				put_task_stack(task);
+			}
+		}
+
 		unlock_task_sighand(task, &flags);
 	}
 
Signature · 1 line
2.39.5
Reviewed-by on v1 1/2
#2 lore raw

Re: [PATCH 1/2] fs/proc: do_task_stat: Fix ESP not readable during coredump

Quoted 19 lines · Nam Cao

Commit 0a1eb2d474ed ("fs/proc: Stop reporting eip and esp in /proc/PID/stat") disabled stack pointer reading, because it is generally dangerous to do so.

Commit fd7d56270b52 ("fs/proc: Report eip/esp in /prod/PID/stat for coredumping") made an exception for coredumping thread, because for this case it is safe.

The exception was later extended to all threads in a coredumping process by commit cb8f381f1613 ("fs/proc/array.c: allow reporting eip/esp for all coredumping threads").

The above two commits determine if a task is core dumping by checking the PF_EXITING and PF_DUMPCORE flags.

However, commit 92307383082d ("coredump: Don't perform any cleanups before dumping core") moved coredump to happen earlier and before PF_EXITING is set. Thus, the check of the PF_EXITING flag no longer works.

Quoted 7 lines

Instead, use task->signal->core_state to determine if coredump is happening. This pointer is set at the beginning of coredump and is cleared once coredump is done. Thus, while this pointer is not NULL, it is safe to read ESP.

Fixes 92307383082d ("coredump: Don't perform any cleanups before dumping core")
#3 lore raw

Re: [PATCH 1/2] fs/proc: do_task_stat: Fix ESP not readable during coredump

In reply to Nam Cao
Quoted 19 lines · Nam Cao

Commit 0a1eb2d474ed ("fs/proc: Stop reporting eip and esp in /proc/PID/stat") disabled stack pointer reading, because it is generally dangerous to do so.

Commit fd7d56270b52 ("fs/proc: Report eip/esp in /prod/PID/stat for coredumping") made an exception for coredumping thread, because for this case it is safe.

The exception was later extended to all threads in a coredumping process by commit cb8f381f1613 ("fs/proc/array.c: allow reporting eip/esp for all coredumping threads").

The above two commits determine if a task is core dumping by checking the PF_EXITING and PF_DUMPCORE flags.

However, commit 92307383082d ("coredump: Don't perform any cleanups before dumping core") moved coredump to happen earlier and before PF_EXITING is set. Thus, the check of the PF_EXITING flag no longer works.

Quoted 6 lines

Instead, use task->signal->core_state to determine if coredump is happening. This pointer is set at the beginning of coredump and is cleared once coredump is done. Thus, while this pointer is not NULL, it is safe to read ESP.

Fixes 92307383082d ("coredump: Don't perform any cleanups before dumping core")

Can we please make progress with that? It's a user space visible change which causes a regression in core dumper tools.

Thanks,

tglx

#4 lore raw

Re: [PATCH 1/2] fs/proc: do_task_stat: Fix ESP not readable during coredump

In reply to Nam Cao

fs/proc/array.c static int do_task_stat(struct seq_file *m, struct pid_namespace *ns, · line 523

Quoted 9 lines · Nam Cao
fs/proc/array.c +4 -0
@@ -534,6 +517,23 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns,
 		ppid = task_tgid_nr_ns(task->real_parent, ns);
 		pgid = task_pgrp_nr_ns(task, ns);
 
+		/*
+		 * esp and eip are intentionally zeroed out.  There is no
+		 * non-racy way to read them without freezing the task.
+		 * Programs that need reliable values can use ptrace(2).

OK,

but then:

fs/proc/array.c static int do_task_stat(struct seq_file *m, struct pid_namespace *ns, · line 533

Quoted 9 lines
fs/proc/array.c +9 -0
+		 * The only exception is if the task is core dumping because
+		 * a program is not able to use ptrace(2) in that case. It is
+		 * safe because the task has stopped executing permanently.
+		 */
+		if (permitted && task->signal->core_state) {
+			if (try_get_task_stack(task)) {
+				eip = KSTK_EIP(task);
+				esp = KSTK_ESP(task);
+				put_task_stack(task);

How can the task->signal->core_state check help ?

Suppose we have a task T1 with T1-pid == 100 and you read /proc/100/stat. It is possible that the T1's sub-thread T2 starts the coredumping and sets signal->core_state != NULL.

But read(/proc/100/stat) can run before T1 gets SIGKILL from T2 and enters the kernel mode?

Oleg.

#5 lore raw

Re: [PATCH 1/2] fs/proc: do_task_stat: Fix ESP not readable during coredump

Quoted 28 lines · Oleg Nesterov

Review static int do_task_stat(struct seq_file *m, struct pid_namespace *ns, · line 523

Quoted 9 lines · Nam Cao
(quoted diff) +4 -0
@@ -534,6 +517,23 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns,
 		ppid = task_tgid_nr_ns(task->real_parent, ns);
 		pgid = task_pgrp_nr_ns(task, ns);
 
+		/*
+		 * esp and eip are intentionally zeroed out.  There is no
+		 * non-racy way to read them without freezing the task.
+		 * Programs that need reliable values can use ptrace(2).

OK,

but then:

Review static int do_task_stat(struct seq_file *m, struct pid_namespace *ns, · line 532

Quoted 9 lines
(quoted diff) +9 -0
+		 * The only exception is if the task is core dumping because
+		 * a program is not able to use ptrace(2) in that case. It is
+		 * safe because the task has stopped executing permanently.
+		 */
+		if (permitted && task->signal->core_state) {
+			if (try_get_task_stack(task)) {
+				eip = KSTK_EIP(task);
+				esp = KSTK_ESP(task);
+				put_task_stack(task);

How can the task->signal->core_state check help ?

Quoted 6 lines

Suppose we have a task T1 with T1-pid == 100 and you read /proc/100/stat. It is possible that the T1's sub-thread T2 starts the coredumping and sets signal->core_state != NULL.

But read(/proc/100/stat) can run before T1 gets SIGKILL from T2 and enters the kernel mode?

Can't the trivial patch below fix the problem?

Oleg.

fs/proc/array.c renamed from proc/array.c +1 -1
@@ -500,7 +500,7 @@
 		 * a program is not able to use ptrace(2) in that case. It is
 		 * safe because the task has stopped executing permanently.
 		 */
-		if (permitted && (task->flags & (PF_EXITING|PF_DUMPCORE))) {
+		if (permitted && (task->flags & (PF_EXITING|PF_DUMPCORE|PF_POSTCOREDUMP))) {
 			if (try_get_task_stack(task)) {
 				eip = KSTK_EIP(task);
 				esp = KSTK_ESP(task);
author
#6 lore raw

Re: [PATCH 1/2] fs/proc: do_task_stat: Fix ESP not readable during coredump

Hi Oleg,

Quoted 29 lines · Oleg Nesterov
Quoted 28 lines · Oleg Nesterov

Review static int do_task_stat(struct seq_file *m, struct pid_namespace *ns, · line 523

Quoted 9 lines · Nam Cao
(quoted diff) +4 -0
@@ -534,6 +517,23 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns,
 		ppid = task_tgid_nr_ns(task->real_parent, ns);
 		pgid = task_pgrp_nr_ns(task, ns);
 
+		/*
+		 * esp and eip are intentionally zeroed out.  There is no
+		 * non-racy way to read them without freezing the task.
+		 * Programs that need reliable values can use ptrace(2).

OK,

but then:

Review static int do_task_stat(struct seq_file *m, struct pid_namespace *ns, · line 532

Quoted 9 lines
(quoted diff) +9 -0
+		 * The only exception is if the task is core dumping because
+		 * a program is not able to use ptrace(2) in that case. It is
+		 * safe because the task has stopped executing permanently.
+		 */
+		if (permitted && task->signal->core_state) {
+			if (try_get_task_stack(task)) {
+				eip = KSTK_EIP(task);
+				esp = KSTK_ESP(task);
+				put_task_stack(task);

How can the task->signal->core_state check help ?

Quoted 6 lines
Quoted 6 lines

Suppose we have a task T1 with T1-pid == 100 and you read /proc/100/stat. It is possible that the T1's sub-thread T2 starts the coredumping and sets signal->core_state != NULL.

But read(/proc/100/stat) can run before T1 gets SIGKILL from T2 and enters the kernel mode?

Right, I missed that race, thanks for pointing it out.

Quoted 1 line

Can't the trivial patch below fix the problem?

It can. In fact this is the original fix we had. I thought that checking a single "core_state" is simpler than checking 3 flags, oh well..

Can you send a proper patch, or should I do it?

Best regards,
Nam

Quoted 16 lines

Oleg.

fs/proc/array.c renamed from proc/array.c +1 -1
@@ -500,7 +500,7 @@
 		 * a program is not able to use ptrace(2) in that case. It is
 		 * safe because the task has stopped executing permanently.
 		 */
-		if (permitted && (task->flags & (PF_EXITING|PF_DUMPCORE))) {
+		if (permitted && (task->flags & (PF_EXITING|PF_DUMPCORE|PF_POSTCOREDUMP))) {
 			if (try_get_task_stack(task)) {
 				eip = KSTK_EIP(task);
 				esp = KSTK_ESP(task);
#7 lore raw

Re: [PATCH 1/2] fs/proc: do_task_stat: Fix ESP not readable during coredump

Hi Nam,

Quoted 7 lines · Nam Cao
Quoted 2 lines

Can't the trivial patch below fix the problem?

It can. In fact this is the original fix we had. I thought that checking a single "core_state" is simpler than checking 3 flags, oh well..

Can you send a proper patch, or should I do it?

Can you send V2 please? It was you who found/investigated the problem, and the patch is trivial.

Feel free to include my acked-by.

Thanks,

Oleg.

author
#8 lore raw

PATCH 2/2 selftests: coredump: Add stackdump test

In reply to Nam Cao

Add a test which checks that the kstkesp field in /proc/pid/stat can be read for all threads of a coredumping process.

For full details including the motivation for this test and how it works, see the README file added by this commit.

---
 tools/testing/selftests/coredump/Makefile     |   7 +
 tools/testing/selftests/coredump/README.rst   |  50 ++++++
 tools/testing/selftests/coredump/stackdump    |  14 ++
 .../selftests/coredump/stackdump_test.c       | 154 ++++++++++++++++++
 4 files changed, 225 insertions(+)
 create mode 100644 tools/testing/selftests/coredump/Makefile
 create mode 100644 tools/testing/selftests/coredump/README.rst
 create mode 100755 tools/testing/selftests/coredump/stackdump
 create mode 100644 tools/testing/selftests/coredump/stackdump_test.c
tools/testing/selftests/coredump/Makefile new file +7 -0
new file mode 100644
index 000000000000..ed210037b29d
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-only
+CFLAGS = $(KHDR_INCLUDES)
+
+TEST_GEN_PROGS := stackdump_test
+TEST_FILES := stackdump
+
+include ../lib.mk
tools/testing/selftests/coredump/README.rst new file +50 -0

50 lines not on this page · raw message

tools/testing/selftests/coredump/stackdump new file +14 -0
new file mode 100755
index 000000000000..96714ce42d12
@@ -0,0 +1,14 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0
+
+CRASH_PROGRAM_ID=$1
+STACKDUMP_FILE=$2
+
+TMP=$(mktemp)
+
+for t in /proc/$CRASH_PROGRAM_ID/task/*; do
+	tid=$(basename $t)
+	cat /proc/$tid/stat | awk '{print $29}' >> $TMP
+done
+
+mv $TMP $STACKDUMP_FILE
tools/testing/selftests/coredump/stackdump_test.c new file +154 -0

154 lines not on this page · raw message

Signature · 1 line
2.39.5
Reviewed-by on v1 2/2
#9 lore raw

Re: [PATCH 2/2] selftests: coredump: Add stackdump test

Quoted 7 lines · Nam Cao

Add a test which checks that the kstkesp field in /proc/pid/stat can be read for all threads of a coredumping process.

For full details including the motivation for this test and how it works, see the README file added by this commit.