[RFC] Tainting tasks after poking at them

Alexey Dobriyan adobriyan at gmail.com
Sat Apr 10 20:22:54 UTC 2021


I'm not a security guy, but

The idea is to irrevocably mark task as tainted after its registers
and/or memory have been changed by another task.

The list definitely includes
* ptrace PTRACE_POKEUSER, PTRACE_POKETEXT, PTRACE_POKEDATA,
  PTRACE_SETREGS, PTRACE_SETFPREGS.
* process_vm_writev(2)

If an attacker gets an arbitrary code execution in context of task T,
then every task which can be attached to from T is compromised as well
via registers/memory manipulating system calls.

Tainted flag can be examined in kernel coredumps and maybe even help
with post mortem analysis (no idea if it is really true).

Note:
struct mm_struct should be tainted as well (i've noticed right before
sending this email).

---

 arch/x86/kernel/process_64.c |    2 ++
 arch/x86/kernel/ptrace.c     |    7 +++++++
 arch/x86/kernel/tls.c        |    2 ++
 fs/proc/base.c               |   10 ++++++++++
 include/linux/sched.h        |   14 ++++++++++++++
 kernel/ptrace.c              |    3 +++
 mm/process_vm_access.c       |    4 ++++
 7 files changed, 42 insertions(+)

--- a/arch/x86/kernel/process_64.c
+++ b/arch/x86/kernel/process_64.c
@@ -468,6 +468,7 @@ void x86_fsbase_write_task(struct task_struct *task, unsigned long fsbase)
 	WARN_ON_ONCE(task == current);
 
 	task->thread.fsbase = fsbase;
+	task_set_tainted(task);
 }
 
 void x86_gsbase_write_task(struct task_struct *task, unsigned long gsbase)
@@ -475,6 +476,7 @@ void x86_gsbase_write_task(struct task_struct *task, unsigned long gsbase)
 	WARN_ON_ONCE(task == current);
 
 	task->thread.gsbase = gsbase;
+	task_set_tainted(task);
 }
 
 static void
--- a/arch/x86/kernel/ptrace.c
+++ b/arch/x86/kernel/ptrace.c
@@ -214,6 +214,8 @@ static int set_segment_reg(struct task_struct *task,
 		task_user_gs(task) = value;
 	}
 
+	task_set_tainted(task);
+
 	return 0;
 }
 
@@ -315,6 +317,8 @@ static int set_segment_reg(struct task_struct *task,
 		break;
 	}
 
+	task_set_tainted(task);
+
 	return 0;
 }
 
@@ -349,6 +353,8 @@ static int set_flags(struct task_struct *task, unsigned long value)
 
 	regs->flags = (regs->flags & ~FLAG_MASK) | (value & FLAG_MASK);
 
+	task_set_tainted(task);
+
 	return 0;
 }
 
@@ -382,6 +388,7 @@ static int putreg(struct task_struct *child,
 	}
 
 	*pt_regs_access(task_pt_regs(child), offset) = value;
+	task_set_tainted(child);
 	return 0;
 }
 
--- a/arch/x86/kernel/tls.c
+++ b/arch/x86/kernel/tls.c
@@ -106,6 +106,8 @@ static void set_tls_desc(struct task_struct *p, int idx,
 		load_TLS(t, cpu);
 
 	put_cpu();
+
+	task_set_tainted(p);
 }
 
 /*
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -3149,6 +3149,14 @@ static int proc_stack_depth(struct seq_file *m, struct pid_namespace *ns,
 }
 #endif /* CONFIG_STACKLEAK_METRICS */
 
+static int proc_pid_tainted(struct seq_file *m, struct pid_namespace *_,
+			    struct pid *__, struct task_struct *tsk)
+{
+	seq_putc(m, '0' + task_is_tainted(tsk));
+	seq_putc(m, '\n');
+	return 0;
+}
+
 /*
  * Thread groups
  */
@@ -3265,6 +3273,7 @@ static const struct pid_entry tgid_base_stuff[] = {
 #ifdef CONFIG_SECCOMP_CACHE_DEBUG
 	ONE("seccomp_cache", S_IRUSR, proc_pid_seccomp_cache),
 #endif
+	ONE("tainted", S_IRUGO, proc_pid_tainted),
 };
 
 static int proc_tgid_base_readdir(struct file *file, struct dir_context *ctx)
@@ -3598,6 +3607,7 @@ static const struct pid_entry tid_base_stuff[] = {
 #ifdef CONFIG_SECCOMP_CACHE_DEBUG
 	ONE("seccomp_cache", S_IRUSR, proc_pid_seccomp_cache),
 #endif
+	ONE("tainted", S_IRUGO, proc_pid_tainted),
 };
 
 static int proc_tid_base_readdir(struct file *file, struct dir_context *ctx)
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -668,6 +668,7 @@ struct task_struct {
 	/* Per task flags (PF_*), defined further below: */
 	unsigned int			flags;
 	unsigned int			ptrace;
+	bool				tainted;
 
 #ifdef CONFIG_SMP
 	int				on_cpu;
@@ -2026,6 +2027,19 @@ extern long sched_getaffinity(pid_t pid, struct cpumask *mask);
 unsigned long sched_cpu_util(int cpu, unsigned long max);
 #endif /* CONFIG_SMP */
 
+static inline bool task_is_tainted(const struct task_struct *tsk)
+{
+	return READ_ONCE(tsk->tainted);
+}
+
+static inline void task_set_tainted(struct task_struct *tsk)
+{
+	/* Self-flagellation is OK. */
+	if (tsk != current) {
+		WRITE_ONCE(tsk->tainted, true);
+	}
+}
+
 #ifdef CONFIG_RSEQ
 
 /*
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -1297,6 +1297,9 @@ int generic_ptrace_pokedata(struct task_struct *tsk, unsigned long addr,
 
 	copied = ptrace_access_vm(tsk, addr, &data, sizeof(data),
 			FOLL_FORCE | FOLL_WRITE);
+	if (copied > 0) {
+		task_set_tainted(tsk);
+	}
 	return (copied == sizeof(data)) ? 0 : -EIO;
 }
 
--- a/mm/process_vm_access.c
+++ b/mm/process_vm_access.c
@@ -228,6 +228,10 @@ static ssize_t process_vm_rw_core(pid_t pid, struct iov_iter *iter,
 
 	mmput(mm);
 
+	if (vm_write && rc > 0) {
+		task_set_tainted(task);
+	}
+
 put_task_struct:
 	put_task_struct(task);
 



More information about the Linux-security-module-archive mailing list