/[linux-patches]/genpatches-2.6/tags/2.6.14-5/1000_1_sysctl-unregistration.patch
Gentoo

Contents of /genpatches-2.6/tags/2.6.14-5/1000_1_sysctl-unregistration.patch

Parent Directory Parent Directory | Revision Log Revision Log


Revision 226 - (show annotations) (download)
Fri Dec 2 12:14:55 2005 UTC (12 years, 5 months ago) by dsd
File size: 9037 byte(s)
2.6.14-5 release
1 From security-bounces@linux.kernel.org Tue Nov 8 07:05:26 2005
2 Date: Tue, 8 Nov 2005 15:03:46 +0000 (GMT)
3 From: Mark J Cox <mjc@redhat.com>
4 To: security@kernel.org
5 Message-ID: <0511081502560.8682@dell1.moose.awe.com>
6 Cc: aviro@redhat.com, vendor-sec@lst.de
7 Subject: CVE-2005-2709 sysctl unregistration oops
8
9 From: Al Viro <viro@zeniv.linux.org.uk>
10
11 You could open the /proc/sys/net/ipv4/conf/<if>/<whatever> file, then
12 wait for interface to go away, try to grab as much memory as possible in
13 hope to hit the (kfreed) ctl_table. Then fill it with pointers to your
14 function. Then do read from file you've opened and if you are lucky,
15 you'll get it called as ->proc_handler() in kernel mode.
16
17 So this is at least an Oops and possibly more. It does depend on an
18 interface going away though, so less of a security risk than it would
19 otherwise be.
20
21 Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
22
23 diff -urN current/arch/s390/appldata/appldata_base.c sysctl/arch/s390/appldata/appldata_base.c
24 --- current/arch/s390/appldata/appldata_base.c 2005-08-28 23:09:40.000000000 -0400
25 +++ sysctl/arch/s390/appldata/appldata_base.c 2005-11-03 08:53:57.000000000 -0500
26 @@ -592,12 +592,15 @@
27 */
28 void appldata_unregister_ops(struct appldata_ops *ops)
29 {
30 + void *table;
31 spin_lock(&appldata_ops_lock);
32 - unregister_sysctl_table(ops->sysctl_header);
33 list_del(&ops->list);
34 - kfree(ops->ctl_table);
35 + /* at that point any incoming access will fail */
36 + table = ops->ctl_table;
37 ops->ctl_table = NULL;
38 spin_unlock(&appldata_ops_lock);
39 + unregister_sysctl_table(ops->sysctl_header);
40 + kfree(table);
41 P_INFO("%s-ops unregistered!\n", ops->name);
42 }
43 /********************** module-ops management <END> **************************/
44 diff -urN current/include/linux/proc_fs.h sysctl/include/linux/proc_fs.h
45 --- current/include/linux/proc_fs.h 2005-08-28 23:09:48.000000000 -0400
46 +++ sysctl/include/linux/proc_fs.h 2005-11-03 08:43:22.000000000 -0500
47 @@ -66,6 +66,7 @@
48 write_proc_t *write_proc;
49 atomic_t count; /* use count */
50 int deleted; /* delete flag */
51 + void *set;
52 };
53
54 struct kcore_list {
55 diff -urN current/include/linux/sysctl.h sysctl/include/linux/sysctl.h
56 --- current/include/linux/sysctl.h 2005-10-28 16:42:49.000000000 -0400
57 +++ sysctl/include/linux/sysctl.h 2005-11-03 08:43:22.000000000 -0500
58 @@ -24,6 +24,7 @@
59 #include <linux/compiler.h>
60
61 struct file;
62 +struct completion;
63
64 #define CTL_MAXNAME 10 /* how many path components do we allow in a
65 call to sysctl? In other words, what is
66 @@ -925,6 +926,8 @@
67 {
68 ctl_table *ctl_table;
69 struct list_head ctl_entry;
70 + int used;
71 + struct completion *unregistering;
72 };
73
74 struct ctl_table_header * register_sysctl_table(ctl_table * table,
75 diff -urN current/kernel/sysctl.c sysctl/kernel/sysctl.c
76 --- current/kernel/sysctl.c 2005-10-28 16:42:49.000000000 -0400
77 +++ sysctl/kernel/sysctl.c 2005-11-03 08:50:17.000000000 -0500
78 @@ -169,7 +169,7 @@
79
80 extern struct proc_dir_entry *proc_sys_root;
81
82 -static void register_proc_table(ctl_table *, struct proc_dir_entry *);
83 +static void register_proc_table(ctl_table *, struct proc_dir_entry *, void *);
84 static void unregister_proc_table(ctl_table *, struct proc_dir_entry *);
85 #endif
86
87 @@ -992,10 +992,51 @@
88
89 extern void init_irq_proc (void);
90
91 +static DEFINE_SPINLOCK(sysctl_lock);
92 +
93 +/* called under sysctl_lock */
94 +static int use_table(struct ctl_table_header *p)
95 +{
96 + if (unlikely(p->unregistering))
97 + return 0;
98 + p->used++;
99 + return 1;
100 +}
101 +
102 +/* called under sysctl_lock */
103 +static void unuse_table(struct ctl_table_header *p)
104 +{
105 + if (!--p->used)
106 + if (unlikely(p->unregistering))
107 + complete(p->unregistering);
108 +}
109 +
110 +/* called under sysctl_lock, will reacquire if has to wait */
111 +static void start_unregistering(struct ctl_table_header *p)
112 +{
113 + /*
114 + * if p->used is 0, nobody will ever touch that entry again;
115 + * we'll eliminate all paths to it before dropping sysctl_lock
116 + */
117 + if (unlikely(p->used)) {
118 + struct completion wait;
119 + init_completion(&wait);
120 + p->unregistering = &wait;
121 + spin_unlock(&sysctl_lock);
122 + wait_for_completion(&wait);
123 + spin_lock(&sysctl_lock);
124 + }
125 + /*
126 + * do not remove from the list until nobody holds it; walking the
127 + * list in do_sysctl() relies on that.
128 + */
129 + list_del_init(&p->ctl_entry);
130 +}
131 +
132 void __init sysctl_init(void)
133 {
134 #ifdef CONFIG_PROC_FS
135 - register_proc_table(root_table, proc_sys_root);
136 + register_proc_table(root_table, proc_sys_root, &root_table_header);
137 init_irq_proc();
138 #endif
139 }
140 @@ -1004,6 +1045,7 @@
141 void __user *newval, size_t newlen)
142 {
143 struct list_head *tmp;
144 + int error = -ENOTDIR;
145
146 if (nlen <= 0 || nlen >= CTL_MAXNAME)
147 return -ENOTDIR;
148 @@ -1012,20 +1054,30 @@
149 if (!oldlenp || get_user(old_len, oldlenp))
150 return -EFAULT;
151 }
152 + spin_lock(&sysctl_lock);
153 tmp = &root_table_header.ctl_entry;
154 do {
155 struct ctl_table_header *head =
156 list_entry(tmp, struct ctl_table_header, ctl_entry);
157 void *context = NULL;
158 - int error = parse_table(name, nlen, oldval, oldlenp,
159 +
160 + if (!use_table(head))
161 + continue;
162 +
163 + spin_unlock(&sysctl_lock);
164 +
165 + error = parse_table(name, nlen, oldval, oldlenp,
166 newval, newlen, head->ctl_table,
167 &context);
168 kfree(context);
169 +
170 + spin_lock(&sysctl_lock);
171 + unuse_table(head);
172 if (error != -ENOTDIR)
173 - return error;
174 - tmp = tmp->next;
175 - } while (tmp != &root_table_header.ctl_entry);
176 - return -ENOTDIR;
177 + break;
178 + } while ((tmp = tmp->next) != &root_table_header.ctl_entry);
179 + spin_unlock(&sysctl_lock);
180 + return error;
181 }
182
183 asmlinkage long sys_sysctl(struct __sysctl_args __user *args)
184 @@ -1236,12 +1288,16 @@
185 return NULL;
186 tmp->ctl_table = table;
187 INIT_LIST_HEAD(&tmp->ctl_entry);
188 + tmp->used = 0;
189 + tmp->unregistering = NULL;
190 + spin_lock(&sysctl_lock);
191 if (insert_at_head)
192 list_add(&tmp->ctl_entry, &root_table_header.ctl_entry);
193 else
194 list_add_tail(&tmp->ctl_entry, &root_table_header.ctl_entry);
195 + spin_unlock(&sysctl_lock);
196 #ifdef CONFIG_PROC_FS
197 - register_proc_table(table, proc_sys_root);
198 + register_proc_table(table, proc_sys_root, tmp);
199 #endif
200 return tmp;
201 }
202 @@ -1255,10 +1311,13 @@
203 */
204 void unregister_sysctl_table(struct ctl_table_header * header)
205 {
206 - list_del(&header->ctl_entry);
207 + might_sleep();
208 + spin_lock(&sysctl_lock);
209 + start_unregistering(header);
210 #ifdef CONFIG_PROC_FS
211 unregister_proc_table(header->ctl_table, proc_sys_root);
212 #endif
213 + spin_unlock(&sysctl_lock);
214 kfree(header);
215 }
216
217 @@ -1269,7 +1328,7 @@
218 #ifdef CONFIG_PROC_FS
219
220 /* Scan the sysctl entries in table and add them all into /proc */
221 -static void register_proc_table(ctl_table * table, struct proc_dir_entry *root)
222 +static void register_proc_table(ctl_table * table, struct proc_dir_entry *root, void *set)
223 {
224 struct proc_dir_entry *de;
225 int len;
226 @@ -1305,13 +1364,14 @@
227 de = create_proc_entry(table->procname, mode, root);
228 if (!de)
229 continue;
230 + de->set = set;
231 de->data = (void *) table;
232 if (table->proc_handler)
233 de->proc_fops = &proc_sys_file_operations;
234 }
235 table->de = de;
236 if (de->mode & S_IFDIR)
237 - register_proc_table(table->child, de);
238 + register_proc_table(table->child, de, set);
239 }
240 }
241
242 @@ -1336,6 +1396,13 @@
243 continue;
244 }
245
246 + /*
247 + * In any case, mark the entry as goner; we'll keep it
248 + * around if it's busy, but we'll know to do nothing with
249 + * its fields. We are under sysctl_lock here.
250 + */
251 + de->data = NULL;
252 +
253 /* Don't unregister proc entries that are still being used.. */
254 if (atomic_read(&de->count))
255 continue;
256 @@ -1349,27 +1416,38 @@
257 size_t count, loff_t *ppos)
258 {
259 int op;
260 - struct proc_dir_entry *de;
261 + struct proc_dir_entry *de = PDE(file->f_dentry->d_inode);
262 struct ctl_table *table;
263 size_t res;
264 - ssize_t error;
265 -
266 - de = PDE(file->f_dentry->d_inode);
267 - if (!de || !de->data)
268 - return -ENOTDIR;
269 - table = (struct ctl_table *) de->data;
270 - if (!table || !table->proc_handler)
271 - return -ENOTDIR;
272 - op = (write ? 002 : 004);
273 - if (ctl_perm(table, op))
274 - return -EPERM;
275 + ssize_t error = -ENOTDIR;
276
277 - res = count;
278 -
279 - error = (*table->proc_handler) (table, write, file, buf, &res, ppos);
280 - if (error)
281 - return error;
282 - return res;
283 + spin_lock(&sysctl_lock);
284 + if (de && de->data && use_table(de->set)) {
285 + /*
286 + * at that point we know that sysctl was not unregistered
287 + * and won't be until we finish
288 + */
289 + spin_unlock(&sysctl_lock);
290 + table = (struct ctl_table *) de->data;
291 + if (!table || !table->proc_handler)
292 + goto out;
293 + error = -EPERM;
294 + op = (write ? 002 : 004);
295 + if (ctl_perm(table, op))
296 + goto out;
297 +
298 + /* careful: calling conventions are nasty here */
299 + res = count;
300 + error = (*table->proc_handler)(table, write, file,
301 + buf, &res, ppos);
302 + if (!error)
303 + error = res;
304 + out:
305 + spin_lock(&sysctl_lock);
306 + unuse_table(de->set);
307 + }
308 + spin_unlock(&sysctl_lock);
309 + return error;
310 }
311
312 static int proc_opensys(struct inode *inode, struct file *file)
313 _______________________________________________
314 Security mailing list
315 Security@linux.kernel.org
316 http://linux.kernel.org/mailman/listinfo/security
317

  ViewVC Help
Powered by ViewVC 1.1.20