|
14 | 14 | #include <linux/mm.h> |
15 | 15 | #include <linux/module.h> |
16 | 16 | #include <linux/bpf-cgroup.h> |
| 17 | +#include <linux/mount.h> |
17 | 18 | #include "internal.h" |
18 | 19 |
|
19 | 20 | static const struct dentry_operations proc_sys_dentry_operations; |
@@ -1703,3 +1704,109 @@ int __init proc_sys_init(void) |
1703 | 1704 |
|
1704 | 1705 | return sysctl_init(); |
1705 | 1706 | } |
| 1707 | + |
| 1708 | +/* Set sysctl value passed on kernel command line. */ |
| 1709 | +static int process_sysctl_arg(char *param, char *val, |
| 1710 | + const char *unused, void *arg) |
| 1711 | +{ |
| 1712 | + char *path; |
| 1713 | + struct vfsmount **proc_mnt = arg; |
| 1714 | + struct file_system_type *proc_fs_type; |
| 1715 | + struct file *file; |
| 1716 | + int len; |
| 1717 | + int err; |
| 1718 | + loff_t pos = 0; |
| 1719 | + ssize_t wret; |
| 1720 | + |
| 1721 | + if (strncmp(param, "sysctl", sizeof("sysctl") - 1)) |
| 1722 | + return 0; |
| 1723 | + |
| 1724 | + param += sizeof("sysctl") - 1; |
| 1725 | + |
| 1726 | + if (param[0] != '/' && param[0] != '.') |
| 1727 | + return 0; |
| 1728 | + |
| 1729 | + param++; |
| 1730 | + |
| 1731 | + /* |
| 1732 | + * To set sysctl options, we use a temporary mount of proc, look up the |
| 1733 | + * respective sys/ file and write to it. To avoid mounting it when no |
| 1734 | + * options were given, we mount it only when the first sysctl option is |
| 1735 | + * found. Why not a persistent mount? There are problems with a |
| 1736 | + * persistent mount of proc in that it forces userspace not to use any |
| 1737 | + * proc mount options. |
| 1738 | + */ |
| 1739 | + if (!*proc_mnt) { |
| 1740 | + proc_fs_type = get_fs_type("proc"); |
| 1741 | + if (!proc_fs_type) { |
| 1742 | + pr_err("Failed to find procfs to set sysctl from command line\n"); |
| 1743 | + return 0; |
| 1744 | + } |
| 1745 | + *proc_mnt = kern_mount(proc_fs_type); |
| 1746 | + put_filesystem(proc_fs_type); |
| 1747 | + if (IS_ERR(*proc_mnt)) { |
| 1748 | + pr_err("Failed to mount procfs to set sysctl from command line\n"); |
| 1749 | + return 0; |
| 1750 | + } |
| 1751 | + } |
| 1752 | + |
| 1753 | + path = kasprintf(GFP_KERNEL, "sys/%s", param); |
| 1754 | + if (!path) |
| 1755 | + panic("%s: Failed to allocate path for %s\n", __func__, param); |
| 1756 | + strreplace(path, '.', '/'); |
| 1757 | + |
| 1758 | + file = file_open_root((*proc_mnt)->mnt_root, *proc_mnt, path, O_WRONLY, 0); |
| 1759 | + if (IS_ERR(file)) { |
| 1760 | + err = PTR_ERR(file); |
| 1761 | + if (err == -ENOENT) |
| 1762 | + pr_err("Failed to set sysctl parameter '%s=%s': parameter not found\n", |
| 1763 | + param, val); |
| 1764 | + else if (err == -EACCES) |
| 1765 | + pr_err("Failed to set sysctl parameter '%s=%s': permission denied (read-only?)\n", |
| 1766 | + param, val); |
| 1767 | + else |
| 1768 | + pr_err("Error %pe opening proc file to set sysctl parameter '%s=%s'\n", |
| 1769 | + file, param, val); |
| 1770 | + goto out; |
| 1771 | + } |
| 1772 | + len = strlen(val); |
| 1773 | + wret = kernel_write(file, val, len, &pos); |
| 1774 | + if (wret < 0) { |
| 1775 | + err = wret; |
| 1776 | + if (err == -EINVAL) |
| 1777 | + pr_err("Failed to set sysctl parameter '%s=%s': invalid value\n", |
| 1778 | + param, val); |
| 1779 | + else |
| 1780 | + pr_err("Error %pe writing to proc file to set sysctl parameter '%s=%s'\n", |
| 1781 | + ERR_PTR(err), param, val); |
| 1782 | + } else if (wret != len) { |
| 1783 | + pr_err("Wrote only %zd bytes of %d writing to proc file %s to set sysctl parameter '%s=%s\n", |
| 1784 | + wret, len, path, param, val); |
| 1785 | + } |
| 1786 | + |
| 1787 | + err = filp_close(file, NULL); |
| 1788 | + if (err) |
| 1789 | + pr_err("Error %pe closing proc file to set sysctl parameter '%s=%s\n", |
| 1790 | + ERR_PTR(err), param, val); |
| 1791 | +out: |
| 1792 | + kfree(path); |
| 1793 | + return 0; |
| 1794 | +} |
| 1795 | + |
| 1796 | +void do_sysctl_args(void) |
| 1797 | +{ |
| 1798 | + char *command_line; |
| 1799 | + struct vfsmount *proc_mnt = NULL; |
| 1800 | + |
| 1801 | + command_line = kstrdup(saved_command_line, GFP_KERNEL); |
| 1802 | + if (!command_line) |
| 1803 | + panic("%s: Failed to allocate copy of command line\n", __func__); |
| 1804 | + |
| 1805 | + parse_args("Setting sysctl args", command_line, |
| 1806 | + NULL, 0, -1, -1, &proc_mnt, process_sysctl_arg); |
| 1807 | + |
| 1808 | + if (proc_mnt) |
| 1809 | + kern_unmount(proc_mnt); |
| 1810 | + |
| 1811 | + kfree(command_line); |
| 1812 | +} |
0 commit comments