diff options
author | Jann Horn <jannh@google.com> | 2018-06-25 12:34:10 -0400 |
---|---|---|
committer | Eric W. Biederman <ebiederm@xmission.com> | 2018-08-11 03:05:53 -0400 |
commit | 42a0cc3478584d4d63f68f2f5af021ddbea771fa (patch) | |
tree | 8dc33625ea1c8488e01965f159ef3f597eba42cb /kernel/utsname_sysctl.c | |
parent | 5820f140edef111a9ea2ef414ab2428b8cb805b1 (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.c | 41 |
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 | ||
21 | static void *get_uts(struct ctl_table *table, int write) | 21 | static 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 | ||
36 | static 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 | } |