diff options
Diffstat (limited to 'kernel/utsname_sysctl.c')
| -rw-r--r-- | kernel/utsname_sysctl.c | 84 |
1 files changed, 45 insertions, 39 deletions
diff --git a/kernel/utsname_sysctl.c b/kernel/utsname_sysctl.c index 4f69f9a5e221..bfbaaecb1dd4 100644 --- a/kernel/utsname_sysctl.c +++ b/kernel/utsname_sysctl.c @@ -1,23 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2007 * * Author: Eric Biederman <ebiederm@xmision.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation, version 2 of the - * License. */ #include <linux/export.h> #include <linux/uts.h> #include <linux/utsname.h> +#include <linux/random.h> #include <linux/sysctl.h> #include <linux/wait.h> +#include <linux/rwsem.h> #ifdef CONFIG_PROC_SYSCTL -static void *get_uts(ctl_table *table, int write) +static void *get_uts(const struct ctl_table *table) { char *which = table->data; struct uts_namespace *uts_ns; @@ -25,37 +23,47 @@ static void *get_uts(ctl_table *table, int write) uts_ns = current->nsproxy->uts_ns; which = (which - (char *)&init_uts_ns) + (char *)uts_ns; - if (!write) - down_read(&uts_sem); - else - down_write(&uts_sem); return which; } -static void put_uts(ctl_table *table, int write, void *which) -{ - if (!write) - up_read(&uts_sem); - else - up_write(&uts_sem); -} - /* * Special case of dostring for the UTS structure. This has locks * to observe. Should this be in kernel/sys.c ???? */ -static int proc_do_uts_string(ctl_table *table, int write, - void __user *buffer, size_t *lenp, loff_t *ppos) +static int proc_do_uts_string(const struct ctl_table *table, int write, + void *buffer, size_t *lenp, loff_t *ppos) { struct ctl_table uts_table; int r; - memcpy(&uts_table, table, sizeof(uts_table)); - uts_table.data = get_uts(table, write); - r = proc_dostring(&uts_table,write,buffer,lenp, ppos); - put_uts(table, write, uts_table.data); + char tmp_data[__NEW_UTS_LEN + 1]; - if (write) + memcpy(&uts_table, table, sizeof(uts_table)); + uts_table.data = tmp_data; + + /* + * Buffer the value in tmp_data so that proc_dostring() can be called + * without holding any locks. + * We also need to read the original value in the write==1 case to + * support partial writes. + */ + down_read(&uts_sem); + memcpy(tmp_data, get_uts(table), sizeof(tmp_data)); + up_read(&uts_sem); + r = proc_dostring(&uts_table, write, buffer, lenp, ppos); + + if (write) { + /* + * Write back the new value. + * Note that, since we dropped uts_sem, the result can + * theoretically be incorrect if there are two parallel writes + * at non-zero offsets to the same sysctl. + */ + add_device_randomness(tmp_data, sizeof(tmp_data)); + down_write(&uts_sem); + memcpy(get_uts(table), tmp_data, sizeof(tmp_data)); + up_write(&uts_sem); proc_sys_poll_notify(table->poll); + } return r; } @@ -66,7 +74,15 @@ static int proc_do_uts_string(ctl_table *table, int write, static DEFINE_CTL_TABLE_POLL(hostname_poll); static DEFINE_CTL_TABLE_POLL(domainname_poll); -static struct ctl_table uts_kern_table[] = { +// Note: update 'enum uts_proc' to match any changes to this table +static const struct ctl_table uts_kern_table[] = { + { + .procname = "arch", + .data = init_uts_ns.name.machine, + .maxlen = sizeof(init_uts_ns.name.machine), + .mode = 0444, + .proc_handler = proc_do_uts_string, + }, { .procname = "ostype", .data = init_uts_ns.name.sysname, @@ -104,16 +120,6 @@ static struct ctl_table uts_kern_table[] = { .proc_handler = proc_do_uts_string, .poll = &domainname_poll, }, - {} -}; - -static struct ctl_table uts_root_table[] = { - { - .procname = "kernel", - .mode = 0555, - .child = uts_kern_table, - }, - {} }; #ifdef CONFIG_PROC_SYSCTL @@ -123,7 +129,7 @@ static struct ctl_table uts_root_table[] = { */ void uts_proc_notify(enum uts_proc proc) { - struct ctl_table *table = &uts_kern_table[proc]; + const struct ctl_table *table = &uts_kern_table[proc]; proc_sys_poll_notify(table->poll); } @@ -131,8 +137,8 @@ void uts_proc_notify(enum uts_proc proc) static int __init utsname_sysctl_init(void) { - register_sysctl_table(uts_root_table); + register_sysctl("kernel", uts_kern_table); return 0; } -__initcall(utsname_sysctl_init); +device_initcall(utsname_sysctl_init); |
