summaryrefslogtreecommitdiffstats
path: root/kernel/utsname_sysctl.c
diff options
context:
space:
mode:
authorJann Horn <jannh@google.com>2018-06-25 12:34:10 -0400
committerEric W. Biederman <ebiederm@xmission.com>2018-08-11 03:05:53 -0400
commit42a0cc3478584d4d63f68f2f5af021ddbea771fa (patch)
tree8dc33625ea1c8488e01965f159ef3f597eba42cb /kernel/utsname_sysctl.c
parent5820f140edef111a9ea2ef414ab2428b8cb805b1 (diff)
sys: don't hold uts_sem while accessing userspace memory
Holding uts_sem as a writer while accessing userspace memory allows a namespace admin to stall all processes that attempt to take uts_sem. Instead, move data through stack buffers and don't access userspace memory while uts_sem is held. Cc: stable@vger.kernel.org Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Jann Horn <jannh@google.com> Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
Diffstat (limited to 'kernel/utsname_sysctl.c')
-rw-r--r--kernel/utsname_sysctl.c41
1 files changed, 25 insertions, 16 deletions
diff --git a/kernel/utsname_sysctl.c b/kernel/utsname_sysctl.c
index 233cd8fc6910..258033d62cb3 100644
--- a/kernel/utsname_sysctl.c
+++ b/kernel/utsname_sysctl.c
@@ -18,7 +18,7 @@
18 18
19#ifdef CONFIG_PROC_SYSCTL 19#ifdef CONFIG_PROC_SYSCTL
20 20
21static void *get_uts(struct ctl_table *table, int write) 21static void *get_uts(struct ctl_table *table)
22{ 22{
23 char *which = table->data; 23 char *which = table->data;
24 struct uts_namespace *uts_ns; 24 struct uts_namespace *uts_ns;
@@ -26,21 +26,9 @@ static void *get_uts(struct ctl_table *table, int write)
26 uts_ns = current->nsproxy->uts_ns; 26 uts_ns = current->nsproxy->uts_ns;
27 which = (which - (char *)&init_uts_ns) + (char *)uts_ns; 27 which = (which - (char *)&init_uts_ns) + (char *)uts_ns;
28 28
29 if (!write)
30 down_read(&uts_sem);
31 else
32 down_write(&uts_sem);
33 return which; 29 return which;
34} 30}
35 31
36static void put_uts(struct ctl_table *table, int write, void *which)
37{
38 if (!write)
39 up_read(&uts_sem);
40 else
41 up_write(&uts_sem);
42}
43
44/* 32/*
45 * Special case of dostring for the UTS structure. This has locks 33 * Special case of dostring for the UTS structure. This has locks
46 * to observe. Should this be in kernel/sys.c ???? 34 * to observe. Should this be in kernel/sys.c ????
@@ -50,13 +38,34 @@ static int proc_do_uts_string(struct ctl_table *table, int write,
50{ 38{
51 struct ctl_table uts_table; 39 struct ctl_table uts_table;
52 int r; 40 int r;
41 char tmp_data[__NEW_UTS_LEN + 1];
42
53 memcpy(&uts_table, table, sizeof(uts_table)); 43 memcpy(&uts_table, table, sizeof(uts_table));
54 uts_table.data = get_uts(table, write); 44 uts_table.data = tmp_data;
45
46 /*
47 * Buffer the value in tmp_data so that proc_dostring() can be called
48 * without holding any locks.
49 * We also need to read the original value in the write==1 case to
50 * support partial writes.
51 */
52 down_read(&uts_sem);
53 memcpy(tmp_data, get_uts(table), sizeof(tmp_data));
54 up_read(&uts_sem);
55 r = proc_dostring(&uts_table, write, buffer, lenp, ppos); 55 r = proc_dostring(&uts_table, write, buffer, lenp, ppos);
56 put_uts(table, write, uts_table.data);
57 56
58 if (write) 57 if (write) {
58 /*
59 * Write back the new value.
60 * Note that, since we dropped uts_sem, the result can
61 * theoretically be incorrect if there are two parallel writes
62 * at non-zero offsets to the same sysctl.
63 */
64 down_write(&uts_sem);
65 memcpy(get_uts(table), tmp_data, sizeof(tmp_data));
66 up_write(&uts_sem);
59 proc_sys_poll_notify(table->poll); 67 proc_sys_poll_notify(table->poll);
68 }
60 69
61 return r; 70 return r;
62} 71}