diff options
Diffstat (limited to 'security/yama/yama_lsm.c')
-rw-r--r-- | security/yama/yama_lsm.c | 49 |
1 files changed, 42 insertions, 7 deletions
diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c index 70cd85e3ba30..2663145d1197 100644 --- a/security/yama/yama_lsm.c +++ b/security/yama/yama_lsm.c | |||
@@ -17,6 +17,7 @@ | |||
17 | #include <linux/ptrace.h> | 17 | #include <linux/ptrace.h> |
18 | #include <linux/prctl.h> | 18 | #include <linux/prctl.h> |
19 | #include <linux/ratelimit.h> | 19 | #include <linux/ratelimit.h> |
20 | #include <linux/workqueue.h> | ||
20 | 21 | ||
21 | #define YAMA_SCOPE_DISABLED 0 | 22 | #define YAMA_SCOPE_DISABLED 0 |
22 | #define YAMA_SCOPE_RELATIONAL 1 | 23 | #define YAMA_SCOPE_RELATIONAL 1 |
@@ -29,6 +30,7 @@ static int ptrace_scope = YAMA_SCOPE_RELATIONAL; | |||
29 | struct ptrace_relation { | 30 | struct ptrace_relation { |
30 | struct task_struct *tracer; | 31 | struct task_struct *tracer; |
31 | struct task_struct *tracee; | 32 | struct task_struct *tracee; |
33 | bool invalid; | ||
32 | struct list_head node; | 34 | struct list_head node; |
33 | struct rcu_head rcu; | 35 | struct rcu_head rcu; |
34 | }; | 36 | }; |
@@ -36,6 +38,29 @@ struct ptrace_relation { | |||
36 | static LIST_HEAD(ptracer_relations); | 38 | static LIST_HEAD(ptracer_relations); |
37 | static DEFINE_SPINLOCK(ptracer_relations_lock); | 39 | static DEFINE_SPINLOCK(ptracer_relations_lock); |
38 | 40 | ||
41 | static void yama_relation_cleanup(struct work_struct *work); | ||
42 | static DECLARE_WORK(yama_relation_work, yama_relation_cleanup); | ||
43 | |||
44 | /** | ||
45 | * yama_relation_cleanup - remove invalid entries from the relation list | ||
46 | * | ||
47 | */ | ||
48 | static void yama_relation_cleanup(struct work_struct *work) | ||
49 | { | ||
50 | struct ptrace_relation *relation; | ||
51 | |||
52 | spin_lock(&ptracer_relations_lock); | ||
53 | rcu_read_lock(); | ||
54 | list_for_each_entry_rcu(relation, &ptracer_relations, node) { | ||
55 | if (relation->invalid) { | ||
56 | list_del_rcu(&relation->node); | ||
57 | kfree_rcu(relation, rcu); | ||
58 | } | ||
59 | } | ||
60 | rcu_read_unlock(); | ||
61 | spin_unlock(&ptracer_relations_lock); | ||
62 | } | ||
63 | |||
39 | /** | 64 | /** |
40 | * yama_ptracer_add - add/replace an exception for this tracer/tracee pair | 65 | * yama_ptracer_add - add/replace an exception for this tracer/tracee pair |
41 | * @tracer: the task_struct of the process doing the ptrace | 66 | * @tracer: the task_struct of the process doing the ptrace |
@@ -57,10 +82,13 @@ static int yama_ptracer_add(struct task_struct *tracer, | |||
57 | 82 | ||
58 | added->tracee = tracee; | 83 | added->tracee = tracee; |
59 | added->tracer = tracer; | 84 | added->tracer = tracer; |
85 | added->invalid = false; | ||
60 | 86 | ||
61 | spin_lock_bh(&ptracer_relations_lock); | 87 | spin_lock(&ptracer_relations_lock); |
62 | rcu_read_lock(); | 88 | rcu_read_lock(); |
63 | list_for_each_entry_rcu(relation, &ptracer_relations, node) { | 89 | list_for_each_entry_rcu(relation, &ptracer_relations, node) { |
90 | if (relation->invalid) | ||
91 | continue; | ||
64 | if (relation->tracee == tracee) { | 92 | if (relation->tracee == tracee) { |
65 | list_replace_rcu(&relation->node, &added->node); | 93 | list_replace_rcu(&relation->node, &added->node); |
66 | kfree_rcu(relation, rcu); | 94 | kfree_rcu(relation, rcu); |
@@ -72,7 +100,7 @@ static int yama_ptracer_add(struct task_struct *tracer, | |||
72 | 100 | ||
73 | out: | 101 | out: |
74 | rcu_read_unlock(); | 102 | rcu_read_unlock(); |
75 | spin_unlock_bh(&ptracer_relations_lock); | 103 | spin_unlock(&ptracer_relations_lock); |
76 | return 0; | 104 | return 0; |
77 | } | 105 | } |
78 | 106 | ||
@@ -85,18 +113,22 @@ static void yama_ptracer_del(struct task_struct *tracer, | |||
85 | struct task_struct *tracee) | 113 | struct task_struct *tracee) |
86 | { | 114 | { |
87 | struct ptrace_relation *relation; | 115 | struct ptrace_relation *relation; |
116 | bool marked = false; | ||
88 | 117 | ||
89 | spin_lock_bh(&ptracer_relations_lock); | ||
90 | rcu_read_lock(); | 118 | rcu_read_lock(); |
91 | list_for_each_entry_rcu(relation, &ptracer_relations, node) { | 119 | list_for_each_entry_rcu(relation, &ptracer_relations, node) { |
120 | if (relation->invalid) | ||
121 | continue; | ||
92 | if (relation->tracee == tracee || | 122 | if (relation->tracee == tracee || |
93 | (tracer && relation->tracer == tracer)) { | 123 | (tracer && relation->tracer == tracer)) { |
94 | list_del_rcu(&relation->node); | 124 | relation->invalid = true; |
95 | kfree_rcu(relation, rcu); | 125 | marked = true; |
96 | } | 126 | } |
97 | } | 127 | } |
98 | rcu_read_unlock(); | 128 | rcu_read_unlock(); |
99 | spin_unlock_bh(&ptracer_relations_lock); | 129 | |
130 | if (marked) | ||
131 | schedule_work(&yama_relation_work); | ||
100 | } | 132 | } |
101 | 133 | ||
102 | /** | 134 | /** |
@@ -223,12 +255,15 @@ static int ptracer_exception_found(struct task_struct *tracer, | |||
223 | rcu_read_lock(); | 255 | rcu_read_lock(); |
224 | if (!thread_group_leader(tracee)) | 256 | if (!thread_group_leader(tracee)) |
225 | tracee = rcu_dereference(tracee->group_leader); | 257 | tracee = rcu_dereference(tracee->group_leader); |
226 | list_for_each_entry_rcu(relation, &ptracer_relations, node) | 258 | list_for_each_entry_rcu(relation, &ptracer_relations, node) { |
259 | if (relation->invalid) | ||
260 | continue; | ||
227 | if (relation->tracee == tracee) { | 261 | if (relation->tracee == tracee) { |
228 | parent = relation->tracer; | 262 | parent = relation->tracer; |
229 | found = true; | 263 | found = true; |
230 | break; | 264 | break; |
231 | } | 265 | } |
266 | } | ||
232 | 267 | ||
233 | if (found && (parent == NULL || task_is_descendant(parent, tracer))) | 268 | if (found && (parent == NULL || task_is_descendant(parent, tracer))) |
234 | rc = 1; | 269 | rc = 1; |