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);
 

Reply via email to