* [PATCH v2 1/6] sysctl: avoid spurious permanent empty tables
2024-08-05 9:39 [PATCH v2 0/6] sysctl: prepare sysctl core for const struct ctl_table Thomas Weißschuh
@ 2024-08-05 9:39 ` Thomas Weißschuh
2024-08-24 18:05 ` Thomas Weißschuh
2024-08-05 9:39 ` [PATCH v2 2/6] bpf: Constify ctl_table argument of filter function Thomas Weißschuh
` (5 subsequent siblings)
6 siblings, 1 reply; 10+ messages in thread
From: Thomas Weißschuh @ 2024-08-05 9:39 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song,
John Fastabend, KP Singh, Stanislav Fomichev, Hao Luo, Jiri Olsa,
Luis Chamberlain, Kees Cook, Joel Granados
Cc: bpf, linux-kernel, linux-fsdevel, Thomas Weißschuh, stable
The test if a table is a permanently empty one, inspects the address of
the registered ctl_table argument.
However as sysctl_mount_point is an empty array and does not occupy and
space it can end up sharing an address with another object in memory.
If that other object itself is a "struct ctl_table" then registering
that table will fail as it's incorrectly recognized as permanently empty.
Avoid this issue by adding a dummy element to the array so that is not
empty anymore.
Explicitly register the table with zero elements as otherwise the dummy
element would be recognized as a sentinel element which would lead to a
runtime warning from the sysctl core.
While the issue seems not being encountered at this time, this seems
mostly to be due to luck.
Also a future change, constifying sysctl_mount_point and root_table, can
reliably trigger this issue on clang 18.
Given that empty arrays are non-standard in the first place it seems
prudent to avoid them if possible.
Fixes: 4a7b29f65094 ("sysctl: move sysctl type to ctl_table_header")
Fixes: a35dd3a786f5 ("sysctl: drop now unnecessary out-of-bounds check")
Cc: stable@vger.kernel.org
Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
---
fs/proc/proc_sysctl.c | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c
index 9553e77c9d31..d11ebc055ce0 100644
--- a/fs/proc/proc_sysctl.c
+++ b/fs/proc/proc_sysctl.c
@@ -29,8 +29,13 @@ static const struct inode_operations proc_sys_inode_operations;
static const struct file_operations proc_sys_dir_file_operations;
static const struct inode_operations proc_sys_dir_operations;
-/* Support for permanently empty directories */
-static struct ctl_table sysctl_mount_point[] = { };
+/*
+ * Support for permanently empty directories.
+ * Must be non-empty to avoid sharing an address with other tables.
+ */
+static struct ctl_table sysctl_mount_point[] = {
+ { }
+};
/**
* register_sysctl_mount_point() - registers a sysctl mount point
@@ -42,7 +47,7 @@ static struct ctl_table sysctl_mount_point[] = { };
*/
struct ctl_table_header *register_sysctl_mount_point(const char *path)
{
- return register_sysctl(path, sysctl_mount_point);
+ return register_sysctl_sz(path, sysctl_mount_point, 0);
}
EXPORT_SYMBOL(register_sysctl_mount_point);
--
2.46.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* Re: [PATCH v2 1/6] sysctl: avoid spurious permanent empty tables
2024-08-05 9:39 ` [PATCH v2 1/6] sysctl: avoid spurious permanent empty tables Thomas Weißschuh
@ 2024-08-24 18:05 ` Thomas Weißschuh
2024-09-02 9:19 ` Joel Granados
0 siblings, 1 reply; 10+ messages in thread
From: Thomas Weißschuh @ 2024-08-24 18:05 UTC (permalink / raw)
To: Joel Granados, Luis Chamberlain, Kees Cook
Cc: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song,
John Fastabend, KP Singh, Stanislav Fomichev, Hao Luo, Jiri Olsa,
bpf, linux-kernel, linux-fsdevel, stable
Hi Joel,
On 2024-08-05 11:39:35+0000, Thomas Weißschuh wrote:
> The test if a table is a permanently empty one, inspects the address of
> the registered ctl_table argument.
> However as sysctl_mount_point is an empty array and does not occupy and
> space it can end up sharing an address with another object in memory.
> If that other object itself is a "struct ctl_table" then registering
> that table will fail as it's incorrectly recognized as permanently empty.
>
> Avoid this issue by adding a dummy element to the array so that is not
> empty anymore.
> Explicitly register the table with zero elements as otherwise the dummy
> element would be recognized as a sentinel element which would lead to a
> runtime warning from the sysctl core.
>
> While the issue seems not being encountered at this time, this seems
> mostly to be due to luck.
> Also a future change, constifying sysctl_mount_point and root_table, can
> reliably trigger this issue on clang 18.
>
> Given that empty arrays are non-standard in the first place it seems
> prudent to avoid them if possible.
>
> Fixes: 4a7b29f65094 ("sysctl: move sysctl type to ctl_table_header")
> Fixes: a35dd3a786f5 ("sysctl: drop now unnecessary out-of-bounds check")
> Cc: stable@vger.kernel.org
> Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
Any updates on this?
I fear it can theoretically also happen on v6.11.
> ---
> fs/proc/proc_sysctl.c | 11 ++++++++---
> 1 file changed, 8 insertions(+), 3 deletions(-)
>
> diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c
> index 9553e77c9d31..d11ebc055ce0 100644
> --- a/fs/proc/proc_sysctl.c
> +++ b/fs/proc/proc_sysctl.c
> @@ -29,8 +29,13 @@ static const struct inode_operations proc_sys_inode_operations;
> static const struct file_operations proc_sys_dir_file_operations;
> static const struct inode_operations proc_sys_dir_operations;
>
> -/* Support for permanently empty directories */
> -static struct ctl_table sysctl_mount_point[] = { };
> +/*
> + * Support for permanently empty directories.
> + * Must be non-empty to avoid sharing an address with other tables.
> + */
> +static struct ctl_table sysctl_mount_point[] = {
> + { }
> +};
>
> /**
> * register_sysctl_mount_point() - registers a sysctl mount point
> @@ -42,7 +47,7 @@ static struct ctl_table sysctl_mount_point[] = { };
> */
> struct ctl_table_header *register_sysctl_mount_point(const char *path)
> {
> - return register_sysctl(path, sysctl_mount_point);
> + return register_sysctl_sz(path, sysctl_mount_point, 0);
> }
> EXPORT_SYMBOL(register_sysctl_mount_point);
>
>
> --
> 2.46.0
>
^ permalink raw reply [flat|nested] 10+ messages in thread* Re: [PATCH v2 1/6] sysctl: avoid spurious permanent empty tables
2024-08-24 18:05 ` Thomas Weißschuh
@ 2024-09-02 9:19 ` Joel Granados
0 siblings, 0 replies; 10+ messages in thread
From: Joel Granados @ 2024-09-02 9:19 UTC (permalink / raw)
To: Thomas Weißschuh
Cc: Luis Chamberlain, Kees Cook, Alexei Starovoitov, Daniel Borkmann,
Andrii Nakryiko, Martin KaFai Lau, Eduard Zingerman, Song Liu,
Yonghong Song, John Fastabend, KP Singh, Stanislav Fomichev,
Hao Luo, Jiri Olsa, bpf, linux-kernel, linux-fsdevel, stable
On Sat, Aug 24, 2024 at 08:05:08PM +0200, Thomas Weißschuh wrote:
> Hi Joel,
>
> On 2024-08-05 11:39:35+0000, Thomas Weißschuh wrote:
> > The test if a table is a permanently empty one, inspects the address of
> > the registered ctl_table argument.
> > However as sysctl_mount_point is an empty array and does not occupy and
> > space it can end up sharing an address with another object in memory.
> > If that other object itself is a "struct ctl_table" then registering
> > that table will fail as it's incorrectly recognized as permanently empty.
> >
> > Avoid this issue by adding a dummy element to the array so that is not
> > empty anymore.
> > Explicitly register the table with zero elements as otherwise the dummy
> > element would be recognized as a sentinel element which would lead to a
> > runtime warning from the sysctl core.
> >
> > While the issue seems not being encountered at this time, this seems
> > mostly to be due to luck.
> > Also a future change, constifying sysctl_mount_point and root_table, can
> > reliably trigger this issue on clang 18.
> >
> > Given that empty arrays are non-standard in the first place it seems
> > prudent to avoid them if possible.
> >
> > Fixes: 4a7b29f65094 ("sysctl: move sysctl type to ctl_table_header")
> > Fixes: a35dd3a786f5 ("sysctl: drop now unnecessary out-of-bounds check")
> > Cc: stable@vger.kernel.org
> > Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
>
> Any updates on this?
> I fear it can theoretically also happen on v6.11.
>
This is already in next and will probably make it for v6.11. The "fixed"
tag will make is so it is ported to 6.10.
Best
--
Joel Granados
^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v2 2/6] bpf: Constify ctl_table argument of filter function
2024-08-05 9:39 [PATCH v2 0/6] sysctl: prepare sysctl core for const struct ctl_table Thomas Weißschuh
2024-08-05 9:39 ` [PATCH v2 1/6] sysctl: avoid spurious permanent empty tables Thomas Weißschuh
@ 2024-08-05 9:39 ` Thomas Weißschuh
2024-08-05 9:39 ` [PATCH v2 3/6] sysctl: move internal interfaces to const struct ctl_table Thomas Weißschuh
` (4 subsequent siblings)
6 siblings, 0 replies; 10+ messages in thread
From: Thomas Weißschuh @ 2024-08-05 9:39 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song,
John Fastabend, KP Singh, Stanislav Fomichev, Hao Luo, Jiri Olsa,
Luis Chamberlain, Kees Cook, Joel Granados
Cc: bpf, linux-kernel, linux-fsdevel, Thomas Weißschuh
The sysctl core is moving to allow "struct ctl_table" in read-only memory.
As a preparation for that all functions handling "struct ctl_table" need
to be able to work with "const struct ctl_table".
As __cgroup_bpf_run_filter_sysctl() does not modify its table, it can be
adapted trivially.
Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
---
include/linux/bpf-cgroup.h | 2 +-
kernel/bpf/cgroup.c | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/include/linux/bpf-cgroup.h b/include/linux/bpf-cgroup.h
index fb3c3e7181e6..814a9d1c4a84 100644
--- a/include/linux/bpf-cgroup.h
+++ b/include/linux/bpf-cgroup.h
@@ -138,7 +138,7 @@ int __cgroup_bpf_check_dev_permission(short dev_type, u32 major, u32 minor,
short access, enum cgroup_bpf_attach_type atype);
int __cgroup_bpf_run_filter_sysctl(struct ctl_table_header *head,
- struct ctl_table *table, int write,
+ const struct ctl_table *table, int write,
char **buf, size_t *pcount, loff_t *ppos,
enum cgroup_bpf_attach_type atype);
diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c
index 8ba73042a239..429a43746886 100644
--- a/kernel/bpf/cgroup.c
+++ b/kernel/bpf/cgroup.c
@@ -1691,7 +1691,7 @@ const struct bpf_verifier_ops cg_dev_verifier_ops = {
* returned value != 1 during execution. In all other cases 0 is returned.
*/
int __cgroup_bpf_run_filter_sysctl(struct ctl_table_header *head,
- struct ctl_table *table, int write,
+ const struct ctl_table *table, int write,
char **buf, size_t *pcount, loff_t *ppos,
enum cgroup_bpf_attach_type atype)
{
--
2.46.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH v2 3/6] sysctl: move internal interfaces to const struct ctl_table
2024-08-05 9:39 [PATCH v2 0/6] sysctl: prepare sysctl core for const struct ctl_table Thomas Weißschuh
2024-08-05 9:39 ` [PATCH v2 1/6] sysctl: avoid spurious permanent empty tables Thomas Weißschuh
2024-08-05 9:39 ` [PATCH v2 2/6] bpf: Constify ctl_table argument of filter function Thomas Weißschuh
@ 2024-08-05 9:39 ` Thomas Weißschuh
2024-08-05 9:39 ` [PATCH v2 4/6] sysctl: allow registration of " Thomas Weißschuh
` (3 subsequent siblings)
6 siblings, 0 replies; 10+ messages in thread
From: Thomas Weißschuh @ 2024-08-05 9:39 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song,
John Fastabend, KP Singh, Stanislav Fomichev, Hao Luo, Jiri Olsa,
Luis Chamberlain, Kees Cook, Joel Granados
Cc: bpf, linux-kernel, linux-fsdevel, Thomas Weißschuh
As a preparation to make all the core sysctl code work with const struct
ctl_table switch over the internal function to use the const variant.
Some pointers to "struct ctl_table" need to stay non-const as they are
newly allocated and modified before registration.
Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
---
fs/proc/internal.h | 2 +-
fs/proc/proc_sysctl.c | 81 +++++++++++++++++++++++++-------------------------
include/linux/sysctl.h | 2 +-
3 files changed, 43 insertions(+), 42 deletions(-)
diff --git a/fs/proc/internal.h b/fs/proc/internal.h
index a8a8576d8592..fcab5dd7ddb1 100644
--- a/fs/proc/internal.h
+++ b/fs/proc/internal.h
@@ -102,7 +102,7 @@ struct proc_inode {
union proc_op op;
struct proc_dir_entry *pde;
struct ctl_table_header *sysctl;
- struct ctl_table *sysctl_entry;
+ const struct ctl_table *sysctl_entry;
struct hlist_node sibling_inodes;
const struct proc_ns_operations *ns_ops;
struct inode vfs_inode;
diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c
index d11ebc055ce0..713abccbfcf9 100644
--- a/fs/proc/proc_sysctl.c
+++ b/fs/proc/proc_sysctl.c
@@ -88,7 +88,7 @@ static DEFINE_SPINLOCK(sysctl_lock);
static void drop_sysctl_table(struct ctl_table_header *header);
static int sysctl_follow_link(struct ctl_table_header **phead,
- struct ctl_table **pentry);
+ const struct ctl_table **pentry);
static int insert_links(struct ctl_table_header *head);
static void put_links(struct ctl_table_header *header);
@@ -110,11 +110,11 @@ static int namecmp(const char *name1, int len1, const char *name2, int len2)
}
/* Called under sysctl_lock */
-static struct ctl_table *find_entry(struct ctl_table_header **phead,
+static const struct ctl_table *find_entry(struct ctl_table_header **phead,
struct ctl_dir *dir, const char *name, int namelen)
{
struct ctl_table_header *head;
- struct ctl_table *entry;
+ const struct ctl_table *entry;
struct rb_node *node = dir->root.rb_node;
while (node)
@@ -141,7 +141,7 @@ static struct ctl_table *find_entry(struct ctl_table_header **phead,
return NULL;
}
-static int insert_entry(struct ctl_table_header *head, struct ctl_table *entry)
+static int insert_entry(struct ctl_table_header *head, const struct ctl_table *entry)
{
struct rb_node *node = &head->node[entry - head->ctl_table].node;
struct rb_node **p = &head->parent->root.rb_node;
@@ -151,7 +151,7 @@ static int insert_entry(struct ctl_table_header *head, struct ctl_table *entry)
while (*p) {
struct ctl_table_header *parent_head;
- struct ctl_table *parent_entry;
+ const struct ctl_table *parent_entry;
struct ctl_node *parent_node;
const char *parent_name;
int cmp;
@@ -180,7 +180,7 @@ static int insert_entry(struct ctl_table_header *head, struct ctl_table *entry)
return 0;
}
-static void erase_entry(struct ctl_table_header *head, struct ctl_table *entry)
+static void erase_entry(struct ctl_table_header *head, const struct ctl_table *entry)
{
struct rb_node *node = &head->node[entry - head->ctl_table].node;
@@ -189,7 +189,7 @@ static void erase_entry(struct ctl_table_header *head, struct ctl_table *entry)
static void init_header(struct ctl_table_header *head,
struct ctl_table_root *root, struct ctl_table_set *set,
- struct ctl_node *node, struct ctl_table *table, size_t table_size)
+ struct ctl_node *node, const struct ctl_table *table, size_t table_size)
{
head->ctl_table = table;
head->ctl_table_size = table_size;
@@ -204,7 +204,7 @@ static void init_header(struct ctl_table_header *head,
head->node = node;
INIT_HLIST_HEAD(&head->inodes);
if (node) {
- struct ctl_table *entry;
+ const struct ctl_table *entry;
list_for_each_table_entry(entry, head) {
node->header = head;
@@ -217,7 +217,7 @@ static void init_header(struct ctl_table_header *head,
static void erase_header(struct ctl_table_header *head)
{
- struct ctl_table *entry;
+ const struct ctl_table *entry;
list_for_each_table_entry(entry, head)
erase_entry(head, entry);
@@ -225,7 +225,7 @@ static void erase_header(struct ctl_table_header *head)
static int insert_header(struct ctl_dir *dir, struct ctl_table_header *header)
{
- struct ctl_table *entry;
+ const struct ctl_table *entry;
struct ctl_table_header *dir_h = &dir->header;
int err;
@@ -344,12 +344,12 @@ lookup_header_set(struct ctl_table_root *root)
return set;
}
-static struct ctl_table *lookup_entry(struct ctl_table_header **phead,
- struct ctl_dir *dir,
- const char *name, int namelen)
+static const struct ctl_table *lookup_entry(struct ctl_table_header **phead,
+ struct ctl_dir *dir,
+ const char *name, int namelen)
{
struct ctl_table_header *head;
- struct ctl_table *entry;
+ const struct ctl_table *entry;
spin_lock(&sysctl_lock);
entry = find_entry(&head, dir, name, namelen);
@@ -374,10 +374,10 @@ static struct ctl_node *first_usable_entry(struct rb_node *node)
}
static void first_entry(struct ctl_dir *dir,
- struct ctl_table_header **phead, struct ctl_table **pentry)
+ struct ctl_table_header **phead, const struct ctl_table **pentry)
{
struct ctl_table_header *head = NULL;
- struct ctl_table *entry = NULL;
+ const struct ctl_table *entry = NULL;
struct ctl_node *ctl_node;
spin_lock(&sysctl_lock);
@@ -391,10 +391,10 @@ static void first_entry(struct ctl_dir *dir,
*pentry = entry;
}
-static void next_entry(struct ctl_table_header **phead, struct ctl_table **pentry)
+static void next_entry(struct ctl_table_header **phead, const struct ctl_table **pentry)
{
struct ctl_table_header *head = *phead;
- struct ctl_table *entry = *pentry;
+ const struct ctl_table *entry = *pentry;
struct ctl_node *ctl_node = &head->node[entry - head->ctl_table];
spin_lock(&sysctl_lock);
@@ -427,7 +427,7 @@ static int test_perm(int mode, int op)
return -EACCES;
}
-static int sysctl_perm(struct ctl_table_header *head, struct ctl_table *table, int op)
+static int sysctl_perm(struct ctl_table_header *head, const struct ctl_table *table, int op)
{
struct ctl_table_root *root = head->root;
int mode;
@@ -441,7 +441,7 @@ static int sysctl_perm(struct ctl_table_header *head, struct ctl_table *table, i
}
static struct inode *proc_sys_make_inode(struct super_block *sb,
- struct ctl_table_header *head, struct ctl_table *table)
+ struct ctl_table_header *head, const struct ctl_table *table)
{
struct ctl_table_root *root = head->root;
struct inode *inode;
@@ -512,7 +512,7 @@ static struct dentry *proc_sys_lookup(struct inode *dir, struct dentry *dentry,
struct ctl_table_header *head = grab_header(dir);
struct ctl_table_header *h = NULL;
const struct qstr *name = &dentry->d_name;
- struct ctl_table *p;
+ const struct ctl_table *p;
struct inode *inode;
struct dentry *err = ERR_PTR(-ENOENT);
struct ctl_dir *ctl_dir;
@@ -550,7 +550,7 @@ static ssize_t proc_sys_call_handler(struct kiocb *iocb, struct iov_iter *iter,
{
struct inode *inode = file_inode(iocb->ki_filp);
struct ctl_table_header *head = grab_header(inode);
- struct ctl_table *table = PROC_I(inode)->sysctl_entry;
+ const struct ctl_table *table = PROC_I(inode)->sysctl_entry;
size_t count = iov_iter_count(iter);
char *kbuf;
ssize_t error;
@@ -624,7 +624,7 @@ static ssize_t proc_sys_write(struct kiocb *iocb, struct iov_iter *iter)
static int proc_sys_open(struct inode *inode, struct file *filp)
{
struct ctl_table_header *head = grab_header(inode);
- struct ctl_table *table = PROC_I(inode)->sysctl_entry;
+ const struct ctl_table *table = PROC_I(inode)->sysctl_entry;
/* sysctl was unregistered */
if (IS_ERR(head))
@@ -642,7 +642,7 @@ static __poll_t proc_sys_poll(struct file *filp, poll_table *wait)
{
struct inode *inode = file_inode(filp);
struct ctl_table_header *head = grab_header(inode);
- struct ctl_table *table = PROC_I(inode)->sysctl_entry;
+ const struct ctl_table *table = PROC_I(inode)->sysctl_entry;
__poll_t ret = DEFAULT_POLLMASK;
unsigned long event;
@@ -673,7 +673,7 @@ static __poll_t proc_sys_poll(struct file *filp, poll_table *wait)
static bool proc_sys_fill_cache(struct file *file,
struct dir_context *ctx,
struct ctl_table_header *head,
- struct ctl_table *table)
+ const struct ctl_table *table)
{
struct dentry *child, *dir = file->f_path.dentry;
struct inode *inode;
@@ -717,7 +717,7 @@ static bool proc_sys_fill_cache(struct file *file,
static bool proc_sys_link_fill_cache(struct file *file,
struct dir_context *ctx,
struct ctl_table_header *head,
- struct ctl_table *table)
+ const struct ctl_table *table)
{
bool ret = true;
@@ -735,7 +735,7 @@ static bool proc_sys_link_fill_cache(struct file *file,
return ret;
}
-static int scan(struct ctl_table_header *head, struct ctl_table *table,
+static int scan(struct ctl_table_header *head, const struct ctl_table *table,
unsigned long *pos, struct file *file,
struct dir_context *ctx)
{
@@ -759,7 +759,7 @@ static int proc_sys_readdir(struct file *file, struct dir_context *ctx)
{
struct ctl_table_header *head = grab_header(file_inode(file));
struct ctl_table_header *h = NULL;
- struct ctl_table *entry;
+ const struct ctl_table *entry;
struct ctl_dir *ctl_dir;
unsigned long pos;
@@ -792,7 +792,7 @@ static int proc_sys_permission(struct mnt_idmap *idmap,
* are _NOT_ writeable, capabilities or not.
*/
struct ctl_table_header *head;
- struct ctl_table *table;
+ const struct ctl_table *table;
int error;
/* Executable files are not allowed under /proc/sys/ */
@@ -836,7 +836,7 @@ static int proc_sys_getattr(struct mnt_idmap *idmap,
{
struct inode *inode = d_inode(path->dentry);
struct ctl_table_header *head = grab_header(inode);
- struct ctl_table *table = PROC_I(inode)->sysctl_entry;
+ const struct ctl_table *table = PROC_I(inode)->sysctl_entry;
if (IS_ERR(head))
return PTR_ERR(head);
@@ -935,7 +935,7 @@ static struct ctl_dir *find_subdir(struct ctl_dir *dir,
const char *name, int namelen)
{
struct ctl_table_header *head;
- struct ctl_table *entry;
+ const struct ctl_table *entry;
entry = find_entry(&head, dir, name, namelen);
if (!entry)
@@ -1046,12 +1046,12 @@ static struct ctl_dir *xlate_dir(struct ctl_table_set *set, struct ctl_dir *dir)
}
static int sysctl_follow_link(struct ctl_table_header **phead,
- struct ctl_table **pentry)
+ const struct ctl_table **pentry)
{
struct ctl_table_header *head;
+ const struct ctl_table *entry;
struct ctl_table_root *root;
struct ctl_table_set *set;
- struct ctl_table *entry;
struct ctl_dir *dir;
int ret;
@@ -1078,7 +1078,7 @@ static int sysctl_follow_link(struct ctl_table_header **phead,
return ret;
}
-static int sysctl_err(const char *path, struct ctl_table *table, char *fmt, ...)
+static int sysctl_err(const char *path, const struct ctl_table *table, char *fmt, ...)
{
struct va_format vaf;
va_list args;
@@ -1094,7 +1094,7 @@ static int sysctl_err(const char *path, struct ctl_table *table, char *fmt, ...)
return -EINVAL;
}
-static int sysctl_check_table_array(const char *path, struct ctl_table *table)
+static int sysctl_check_table_array(const char *path, const struct ctl_table *table)
{
unsigned int extra;
int err = 0;
@@ -1133,7 +1133,7 @@ static int sysctl_check_table_array(const char *path, struct ctl_table *table)
static int sysctl_check_table(const char *path, struct ctl_table_header *header)
{
- struct ctl_table *entry;
+ const struct ctl_table *entry;
int err = 0;
list_for_each_table_entry(entry, header) {
if (!entry->procname)
@@ -1169,8 +1169,9 @@ static int sysctl_check_table(const char *path, struct ctl_table_header *header)
static struct ctl_table_header *new_links(struct ctl_dir *dir, struct ctl_table_header *head)
{
- struct ctl_table *link_table, *entry, *link;
+ struct ctl_table *link_table, *link;
struct ctl_table_header *links;
+ const struct ctl_table *entry;
struct ctl_node *node;
char *link_name;
int name_bytes;
@@ -1215,7 +1216,7 @@ static bool get_links(struct ctl_dir *dir,
struct ctl_table_root *link_root)
{
struct ctl_table_header *tmp_head;
- struct ctl_table *entry, *link;
+ const struct ctl_table *entry, *link;
if (header->ctl_table_size == 0 ||
sysctl_is_perm_empty_ctl_header(header))
@@ -1466,7 +1467,7 @@ static void put_links(struct ctl_table_header *header)
struct ctl_table_root *root = header->root;
struct ctl_dir *parent = header->parent;
struct ctl_dir *core_parent;
- struct ctl_table *entry;
+ const struct ctl_table *entry;
if (header->set == root_set)
return;
@@ -1477,7 +1478,7 @@ static void put_links(struct ctl_table_header *header)
list_for_each_table_entry(entry, header) {
struct ctl_table_header *link_head;
- struct ctl_table *link;
+ const struct ctl_table *link;
const char *name = entry->procname;
link = find_entry(&link_head, core_parent, name, strlen(name));
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
index aa4c6d44aaa0..a473deaf5a91 100644
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -162,7 +162,7 @@ struct ctl_node {
struct ctl_table_header {
union {
struct {
- struct ctl_table *ctl_table;
+ const struct ctl_table *ctl_table;
int ctl_table_size;
int used;
int count;
--
2.46.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH v2 4/6] sysctl: allow registration of const struct ctl_table
2024-08-05 9:39 [PATCH v2 0/6] sysctl: prepare sysctl core for const struct ctl_table Thomas Weißschuh
` (2 preceding siblings ...)
2024-08-05 9:39 ` [PATCH v2 3/6] sysctl: move internal interfaces to const struct ctl_table Thomas Weißschuh
@ 2024-08-05 9:39 ` Thomas Weißschuh
2024-08-05 9:39 ` [PATCH v2 5/6] sysctl: make internal ctl_tables const Thomas Weißschuh
` (2 subsequent siblings)
6 siblings, 0 replies; 10+ messages in thread
From: Thomas Weißschuh @ 2024-08-05 9:39 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song,
John Fastabend, KP Singh, Stanislav Fomichev, Hao Luo, Jiri Olsa,
Luis Chamberlain, Kees Cook, Joel Granados
Cc: bpf, linux-kernel, linux-fsdevel, Thomas Weißschuh
Putting structure, especially those containing function pointers,
into read-only memory makes the safer and easier to reason about.
Change the sysctl registration APIs to allow registration of
"const struct ctl_table".
Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
---
fs/proc/proc_sysctl.c | 6 +++---
include/linux/sysctl.h | 10 +++++-----
2 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c
index 713abccbfcf9..968f8dcffd8f 100644
--- a/fs/proc/proc_sysctl.c
+++ b/fs/proc/proc_sysctl.c
@@ -1359,7 +1359,7 @@ static struct ctl_dir *sysctl_mkdir_p(struct ctl_dir *dir, const char *path)
*/
struct ctl_table_header *__register_sysctl_table(
struct ctl_table_set *set,
- const char *path, struct ctl_table *table, size_t table_size)
+ const char *path, const struct ctl_table *table, size_t table_size)
{
struct ctl_table_root *root = set->dir.header.root;
struct ctl_table_header *header;
@@ -1420,7 +1420,7 @@ struct ctl_table_header *__register_sysctl_table(
*
* See __register_sysctl_table for more details.
*/
-struct ctl_table_header *register_sysctl_sz(const char *path, struct ctl_table *table,
+struct ctl_table_header *register_sysctl_sz(const char *path, const struct ctl_table *table,
size_t table_size)
{
return __register_sysctl_table(&sysctl_table_root.default_set,
@@ -1449,7 +1449,7 @@ EXPORT_SYMBOL(register_sysctl_sz);
*
* Context: if your base directory does not exist it will be created for you.
*/
-void __init __register_sysctl_init(const char *path, struct ctl_table *table,
+void __init __register_sysctl_init(const char *path, const struct ctl_table *table,
const char *table_name, size_t table_size)
{
struct ctl_table_header *hdr = register_sysctl_sz(path, table, table_size);
diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h
index a473deaf5a91..202855befa8b 100644
--- a/include/linux/sysctl.h
+++ b/include/linux/sysctl.h
@@ -223,13 +223,13 @@ extern void retire_sysctl_set(struct ctl_table_set *set);
struct ctl_table_header *__register_sysctl_table(
struct ctl_table_set *set,
- const char *path, struct ctl_table *table, size_t table_size);
-struct ctl_table_header *register_sysctl_sz(const char *path, struct ctl_table *table,
+ const char *path, const struct ctl_table *table, size_t table_size);
+struct ctl_table_header *register_sysctl_sz(const char *path, const struct ctl_table *table,
size_t table_size);
void unregister_sysctl_table(struct ctl_table_header * table);
extern int sysctl_init_bases(void);
-extern void __register_sysctl_init(const char *path, struct ctl_table *table,
+extern void __register_sysctl_init(const char *path, const struct ctl_table *table,
const char *table_name, size_t table_size);
#define register_sysctl_init(path, table) \
__register_sysctl_init(path, table, #table, ARRAY_SIZE(table))
@@ -251,7 +251,7 @@ extern int no_unaligned_warning;
#else /* CONFIG_SYSCTL */
-static inline void register_sysctl_init(const char *path, struct ctl_table *table)
+static inline void register_sysctl_init(const char *path, const struct ctl_table *table)
{
}
@@ -261,7 +261,7 @@ static inline struct ctl_table_header *register_sysctl_mount_point(const char *p
}
static inline struct ctl_table_header *register_sysctl_sz(const char *path,
- struct ctl_table *table,
+ const struct ctl_table *table,
size_t table_size)
{
return NULL;
--
2.46.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH v2 5/6] sysctl: make internal ctl_tables const
2024-08-05 9:39 [PATCH v2 0/6] sysctl: prepare sysctl core for const struct ctl_table Thomas Weißschuh
` (3 preceding siblings ...)
2024-08-05 9:39 ` [PATCH v2 4/6] sysctl: allow registration of " Thomas Weißschuh
@ 2024-08-05 9:39 ` Thomas Weißschuh
2024-08-05 9:39 ` [PATCH v2 6/6] const_structs.checkpatch: add ctl_table Thomas Weißschuh
2024-10-09 11:56 ` [PATCH v2 0/6] sysctl: prepare sysctl core for const struct ctl_table Joel Granados
6 siblings, 0 replies; 10+ messages in thread
From: Thomas Weißschuh @ 2024-08-05 9:39 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song,
John Fastabend, KP Singh, Stanislav Fomichev, Hao Luo, Jiri Olsa,
Luis Chamberlain, Kees Cook, Joel Granados
Cc: bpf, linux-kernel, linux-fsdevel, Thomas Weißschuh
Now that the sysctl core can handle registration of
"const struct ctl_table" constify the sysctl internal tables.
Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
---
fs/proc/proc_sysctl.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c
index 968f8dcffd8f..9b9dfc450cb3 100644
--- a/fs/proc/proc_sysctl.c
+++ b/fs/proc/proc_sysctl.c
@@ -33,7 +33,7 @@ static const struct inode_operations proc_sys_dir_operations;
* Support for permanently empty directories.
* Must be non-empty to avoid sharing an address with other tables.
*/
-static struct ctl_table sysctl_mount_point[] = {
+static const struct ctl_table sysctl_mount_point[] = {
{ }
};
@@ -67,7 +67,7 @@ void proc_sys_poll_notify(struct ctl_table_poll *poll)
wake_up_interruptible(&poll->wait);
}
-static struct ctl_table root_table[] = {
+static const struct ctl_table root_table[] = {
{
.procname = "",
.mode = S_IFDIR|S_IRUGO|S_IXUGO,
--
2.46.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH v2 6/6] const_structs.checkpatch: add ctl_table
2024-08-05 9:39 [PATCH v2 0/6] sysctl: prepare sysctl core for const struct ctl_table Thomas Weißschuh
` (4 preceding siblings ...)
2024-08-05 9:39 ` [PATCH v2 5/6] sysctl: make internal ctl_tables const Thomas Weißschuh
@ 2024-08-05 9:39 ` Thomas Weißschuh
2024-10-09 11:56 ` [PATCH v2 0/6] sysctl: prepare sysctl core for const struct ctl_table Joel Granados
6 siblings, 0 replies; 10+ messages in thread
From: Thomas Weißschuh @ 2024-08-05 9:39 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song,
John Fastabend, KP Singh, Stanislav Fomichev, Hao Luo, Jiri Olsa,
Luis Chamberlain, Kees Cook, Joel Granados
Cc: bpf, linux-kernel, linux-fsdevel, Thomas Weißschuh
Now that the sysctl core can handle "const struct ctl_table", make
sure that new usages of the struct already enter the tree as const.
Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
---
scripts/const_structs.checkpatch | 1 +
1 file changed, 1 insertion(+)
diff --git a/scripts/const_structs.checkpatch b/scripts/const_structs.checkpatch
index 014b3bfe3237..e8609a03c3d8 100644
--- a/scripts/const_structs.checkpatch
+++ b/scripts/const_structs.checkpatch
@@ -6,6 +6,7 @@ bus_type
clk_ops
comedi_lrange
component_ops
+ctl_table
dentry_operations
dev_pm_ops
device_type
--
2.46.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* Re: [PATCH v2 0/6] sysctl: prepare sysctl core for const struct ctl_table
2024-08-05 9:39 [PATCH v2 0/6] sysctl: prepare sysctl core for const struct ctl_table Thomas Weißschuh
` (5 preceding siblings ...)
2024-08-05 9:39 ` [PATCH v2 6/6] const_structs.checkpatch: add ctl_table Thomas Weißschuh
@ 2024-10-09 11:56 ` Joel Granados
6 siblings, 0 replies; 10+ messages in thread
From: Joel Granados @ 2024-10-09 11:56 UTC (permalink / raw)
To: Thomas Weißschuh
Cc: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
Martin KaFai Lau, Eduard Zingerman, Song Liu, Yonghong Song,
John Fastabend, KP Singh, Stanislav Fomichev, Hao Luo, Jiri Olsa,
Luis Chamberlain, Kees Cook, Joel Granados, bpf, linux-kernel,
linux-fsdevel, stable
On Mon, Aug 05, 2024 at 11:39:34AM +0200, Thomas Weißschuh wrote:
> Adapt the internal and external APIs of the sysctl core to handle
> read-only instances of "struct ctl_table".
Finally getting around to this. Testing for this has been done on
sysctl-testing base:v6.11-rc6 and now on base:v6.12-rc2. Putting this in
sysctl-next so it will get further testing on its way to v6.13. First
patch (bugfix) will be ignored as it is already upstream.
Best
>
> Patch 1: Bugfix for the sysctl core, the bug can be reliably triggered
> with the series applied
> Patch 2: Trivial preparation commit for the sysctl BPF hook
> Patch 3: Adapts the internal sysctl APIs
> Patch 4: Adapts the external sysctl APIs
> Patch 5: Constifies the sysctl internal tables as proof that it works
> Patch 6: Updates scripts/const_structs.checkpatch for "struct ctl_table"
>
> Motivation
> ==========
>
> Moving structures containing function pointers into unmodifiable .rodata
> prevents attackers or bugs from corrupting and diverting those pointers.
>
> Also the "struct ctl_table" exposed by the sysctl core were never meant
> to be mutated by users.
>
> For this goal changes to both the sysctl core and "const" qualifiers for
> various sysctl APIs are necessary.
>
> Full Process
> ============
>
> * Drop ctl_table modifications from the sysctl core ([0], in mainline)
> * Constify arguments to ctl_table_root::{set_ownership,permissions}
> ([1], in mainline)
> * Migrate users of "ctl_table_header::ctl_table_arg" to "const".
> (in mainline)
> * Afterwards convert "ctl_table_header::ctl_table_arg" itself to const.
> (in mainline)
> * Prepare helpers used to implement proc_handlers throughout the tree to
> use "const struct ctl_table *". ([2], in mainline)
> * Afterwards switch over all proc_handlers callbacks to use
> "const struct ctl_table *" in one commit. (in mainline)
> * Switch over the internals of the sysctl core to "const struct ctl_table *" (this series)
> * Switch include/linux/sysctl.h to "const struct ctl_table *" (this series)
> * Transition instances of "struct ctl_table" through the tree to const (to be done)
>
> This series is meant to be applied through the sysctl tree.
>
> Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
> ---
> Changes in v2:
> - Avoid spurious permanent empty tables (patch 1)
> - Link to v1: https://lore.kernel.org/r/20240729-sysctl-const-api-v1-0-ca628c7a942c@weissschuh.net
>
> ---
> Thomas Weißschuh (6):
> sysctl: avoid spurious permanent empty tables
> bpf: Constify ctl_table argument of filter function
> sysctl: move internal interfaces to const struct ctl_table
> sysctl: allow registration of const struct ctl_table
> sysctl: make internal ctl_tables const
> const_structs.checkpatch: add ctl_table
>
> fs/proc/internal.h | 2 +-
> fs/proc/proc_sysctl.c | 100 +++++++++++++++++++++------------------
> include/linux/bpf-cgroup.h | 2 +-
> include/linux/sysctl.h | 12 ++---
> kernel/bpf/cgroup.c | 2 +-
> scripts/const_structs.checkpatch | 1 +
> 6 files changed, 63 insertions(+), 56 deletions(-)
> ---
> base-commit: 8400291e289ee6b2bf9779ff1c83a291501f017b
> change-id: 20240729-sysctl-const-api-73954f3d62c1
>
> Best regards,
> --
> Thomas Weißschuh <linux@weissschuh.net>
>
--
Joel Granados
^ permalink raw reply [flat|nested] 10+ messages in thread