Commit f8a8b94d authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull sysctl updates from Joel Granados:

 - Remove "->procname == NULL" check when iterating through sysctl table
   arrays

   Removing sentinels in ctl_table arrays reduces the build time size
   and runtime memory consumed by ~64 bytes per array. With all
   ctl_table sentinels gone, the additional check for ->procname == NULL
   that worked in tandem with the ARRAY_SIZE to calculate the size of
   the ctl_table arrays is no longer needed and has been removed. The
   sysctl register functions now returns an error if a sentinel is used.

 - Preparation patches for sysctl constification

   Constifying ctl_table structs prevents the modification of
   proc_handler function pointers as they would reside in .rodata. The
   ctl_table arguments in sysctl utility functions are const qualified
   in preparation for a future treewide proc_handler argument
   constification commit.

 - Misc fixes

   Increase robustness of set_ownership by providing sane default
   ownership values in case the callee doesn't set them. Bound check
   proc_dou8vec_minmax to avoid loading buggy modules and give sysctl
   testing module a name to avoid compiler complaints.

* tag 'sysctl-6.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/sysctl/sysctl:
  sysctl: Warn on an empty procname element
  sysctl: Remove ctl_table sentinel code comments
  sysctl: Remove "child" sysctl code comments
  sysctl: Remove superfluous empty allocations from sysctl internals
  sysctl: Replace nr_entries with ctl_table_size in new_links
  sysctl: Remove check for sentinel element in ctl_table arrays
  mm profiling: Remove superfluous sentinel element from ctl_table
  locking: Remove superfluous sentinel element from kern_lockdep_table
  sysctl: Add module description to sysctl-testing
  sysctl: constify ctl_table arguments of utility function
  utsname: constify ctl_table arguments of utility function
  sysctl: move the extra1/2 boundary check of u8 to sysctl_check_table_array
  sysctl: always initialize i_uid/i_gid
parents ce5a51bf acc15469
Loading
Loading
Loading
Loading
+38 −32
Original line number Diff line number Diff line
@@ -21,7 +21,7 @@

#define list_for_each_table_entry(entry, header)	\
	entry = header->ctl_table;			\
	for (size_t i = 0 ; i < header->ctl_table_size && entry->procname; ++i, entry++)
	for (size_t i = 0 ; i < header->ctl_table_size; ++i, entry++)

static const struct dentry_operations proc_sys_dentry_operations;
static const struct file_operations proc_sys_file_operations;
@@ -476,12 +476,10 @@ static struct inode *proc_sys_make_inode(struct super_block *sb,
			make_empty_dir_inode(inode);
	}

	if (root->set_ownership)
		root->set_ownership(head, &inode->i_uid, &inode->i_gid);
	else {
	inode->i_uid = GLOBAL_ROOT_UID;
	inode->i_gid = GLOBAL_ROOT_GID;
	}
	if (root->set_ownership)
		root->set_ownership(head, &inode->i_uid, &inode->i_gid);

	return inode;
}
@@ -951,14 +949,14 @@ static struct ctl_dir *new_dir(struct ctl_table_set *set,
	char *new_name;

	new = kzalloc(sizeof(*new) + sizeof(struct ctl_node) +
		      sizeof(struct ctl_table)*2 +  namelen + 1,
		      sizeof(struct ctl_table) +  namelen + 1,
		      GFP_KERNEL);
	if (!new)
		return NULL;

	node = (struct ctl_node *)(new + 1);
	table = (struct ctl_table *)(node + 1);
	new_name = (char *)(table + 2);
	new_name = (char *)(table + 1);
	memcpy(new_name, name, namelen);
	table[0].procname = new_name;
	table[0].mode = S_IFDIR|S_IRUGO|S_IXUGO;
@@ -1093,6 +1091,7 @@ static int sysctl_err(const char *path, struct ctl_table *table, char *fmt, ...)

static int sysctl_check_table_array(const char *path, struct ctl_table *table)
{
	unsigned int extra;
	int err = 0;

	if ((table->proc_handler == proc_douintvec) ||
@@ -1104,6 +1103,19 @@ static int sysctl_check_table_array(const char *path, struct ctl_table *table)
	if (table->proc_handler == proc_dou8vec_minmax) {
		if (table->maxlen != sizeof(u8))
			err |= sysctl_err(path, table, "array not allowed");

		if (table->extra1) {
			extra = *(unsigned int *) table->extra1;
			if (extra > 255U)
				err |= sysctl_err(path, table,
						"range value too large for proc_dou8vec_minmax");
		}
		if (table->extra2) {
			extra = *(unsigned int *) table->extra2;
			if (extra > 255U)
				err |= sysctl_err(path, table,
						"range value too large for proc_dou8vec_minmax");
		}
	}

	if (table->proc_handler == proc_dobool) {
@@ -1119,6 +1131,8 @@ static int sysctl_check_table(const char *path, struct ctl_table_header *header)
	struct ctl_table *entry;
	int err = 0;
	list_for_each_table_entry(entry, header) {
		if (!entry->procname)
			err |= sysctl_err(path, entry, "procname is null");
		if ((entry->proc_handler == proc_dostring) ||
		    (entry->proc_handler == proc_dobool) ||
		    (entry->proc_handler == proc_dointvec) ||
@@ -1154,18 +1168,16 @@ static struct ctl_table_header *new_links(struct ctl_dir *dir, struct ctl_table_
	struct ctl_table_header *links;
	struct ctl_node *node;
	char *link_name;
	int nr_entries, name_bytes;
	int name_bytes;

	name_bytes = 0;
	nr_entries = 0;
	list_for_each_table_entry(entry, head) {
		nr_entries++;
		name_bytes += strlen(entry->procname) + 1;
	}

	links = kzalloc(sizeof(struct ctl_table_header) +
			sizeof(struct ctl_node)*nr_entries +
			sizeof(struct ctl_table)*(nr_entries + 1) +
			sizeof(struct ctl_node)*head->ctl_table_size +
			sizeof(struct ctl_table)*head->ctl_table_size +
			name_bytes,
			GFP_KERNEL);

@@ -1173,8 +1185,8 @@ static struct ctl_table_header *new_links(struct ctl_dir *dir, struct ctl_table_
		return NULL;

	node = (struct ctl_node *)(links + 1);
	link_table = (struct ctl_table *)(node + nr_entries);
	link_name = (char *)&link_table[nr_entries + 1];
	link_table = (struct ctl_table *)(node + head->ctl_table_size);
	link_name = (char *)(link_table + head->ctl_table_size);
	link = link_table;

	list_for_each_table_entry(entry, head) {
@@ -1188,7 +1200,7 @@ static struct ctl_table_header *new_links(struct ctl_dir *dir, struct ctl_table_
	}
	init_header(links, dir->header.root, dir->header.set, node, link_table,
		    head->ctl_table_size);
	links->nreg = nr_entries;
	links->nreg = head->ctl_table_size;

	return links;
}
@@ -1300,28 +1312,23 @@ static struct ctl_dir *sysctl_mkdir_p(struct ctl_dir *dir, const char *path)
 * __register_sysctl_table - register a leaf sysctl table
 * @set: Sysctl tree to register on
 * @path: The path to the directory the sysctl table is in.
 * @table: the top-level table structure without any child. This table
 * 	 should not be free'd after registration. So it should not be
 * 	 used on stack. It can either be a global or dynamically allocated
 * 	 by the caller and free'd later after sysctl unregistration.
 *
 * @table: the top-level table structure. This table should not be free'd
 *         after registration. So it should not be used on stack. It can either
 *         be a global or dynamically allocated by the caller and free'd later
 *         after sysctl unregistration.
 * @table_size : The number of elements in table
 *
 * Register a sysctl table hierarchy. @table should be a filled in ctl_table
 * array. A completely 0 filled entry terminates the table.
 * array.
 *
 * The members of the &struct ctl_table structure are used as follows:
 *
 * procname - the name of the sysctl file under /proc/sys. Set to %NULL to not
 *            enter a sysctl file
 *
 * data     - a pointer to data for use by proc_handler
 *
 * maxlen   - the maximum size in bytes of the data
 *
 * mode     - the file permissions for the /proc/sys file
 *
 * child - must be %NULL.
 *
 * type     - Defines the target type (described in struct definition)
 * proc_handler - the text handler routine (described below)
 *
 * extra1, extra2 - extra pointers usable by the proc handler routines
@@ -1329,8 +1336,7 @@ static struct ctl_dir *sysctl_mkdir_p(struct ctl_dir *dir, const char *path)
 * [0] https://lkml.kernel.org/87zgpte9o4.fsf@email.froward.int.ebiederm.org
 *
 * Leaf nodes in the sysctl tree will be represented by a single file
 * under /proc; non-leaf nodes (where child is not NULL) are not allowed,
 * sysctl_check_table() verifies this.
 * under /proc; non-leaf nodes are not allowed.
 *
 * There must be a proc_handler routine for any terminal nodes.
 * Several default handlers are available to cover common cases -
+1 −1
Original line number Diff line number Diff line
@@ -237,7 +237,7 @@ extern struct ctl_table_header *register_sysctl_mount_point(const char *path);

void do_sysctl_args(void);
bool sysctl_is_alias(char *param);
int do_proc_douintvec(struct ctl_table *table, int write,
int do_proc_douintvec(const struct ctl_table *table, int write,
		      void *buffer, size_t *lenp, loff_t *ppos,
		      int (*conv)(unsigned long *lvalp,
				  unsigned int *valp,
+0 −1
Original line number Diff line number Diff line
@@ -97,7 +97,6 @@ static struct ctl_table kern_lockdep_table[] = {
		.proc_handler   = proc_dointvec,
	},
#endif /* CONFIG_LOCK_STAT */
	{ }
};

static __init int kernel_lockdep_sysctls_init(void)
+50 −0
Original line number Diff line number Diff line
@@ -367,6 +367,54 @@ static void sysctl_test_api_dointvec_write_single_greater_int_max(
	KUNIT_EXPECT_EQ(test, 0, *((int *)table.data));
}

/*
 * Test that registering an invalid extra value is not allowed.
 */
static void sysctl_test_register_sysctl_sz_invalid_extra_value(
		struct kunit *test)
{
	unsigned char data = 0;
	struct ctl_table table_foo[] = {
		{
			.procname	= "foo",
			.data		= &data,
			.maxlen		= sizeof(u8),
			.mode		= 0644,
			.proc_handler	= proc_dou8vec_minmax,
			.extra1		= SYSCTL_FOUR,
			.extra2		= SYSCTL_ONE_THOUSAND,
		},
	};

	struct ctl_table table_bar[] = {
		{
			.procname	= "bar",
			.data		= &data,
			.maxlen		= sizeof(u8),
			.mode		= 0644,
			.proc_handler	= proc_dou8vec_minmax,
			.extra1		= SYSCTL_NEG_ONE,
			.extra2		= SYSCTL_ONE_HUNDRED,
		},
	};

	struct ctl_table table_qux[] = {
		{
			.procname	= "qux",
			.data		= &data,
			.maxlen		= sizeof(u8),
			.mode		= 0644,
			.proc_handler	= proc_dou8vec_minmax,
			.extra1		= SYSCTL_ZERO,
			.extra2		= SYSCTL_TWO_HUNDRED,
		},
	};

	KUNIT_EXPECT_NULL(test, register_sysctl("foo", table_foo));
	KUNIT_EXPECT_NULL(test, register_sysctl("foo", table_bar));
	KUNIT_EXPECT_NOT_NULL(test, register_sysctl("foo", table_qux));
}

static struct kunit_case sysctl_test_cases[] = {
	KUNIT_CASE(sysctl_test_api_dointvec_null_tbl_data),
	KUNIT_CASE(sysctl_test_api_dointvec_table_maxlen_unset),
@@ -378,6 +426,7 @@ static struct kunit_case sysctl_test_cases[] = {
	KUNIT_CASE(sysctl_test_dointvec_write_happy_single_negative),
	KUNIT_CASE(sysctl_test_api_dointvec_write_single_less_int_min),
	KUNIT_CASE(sysctl_test_api_dointvec_write_single_greater_int_max),
	KUNIT_CASE(sysctl_test_register_sysctl_sz_invalid_extra_value),
	{}
};

@@ -388,4 +437,5 @@ static struct kunit_suite sysctl_test_suite = {

kunit_test_suites(&sysctl_test_suite);

MODULE_DESCRIPTION("KUnit test of proc sysctl");
MODULE_LICENSE("GPL v2");
+13 −18
Original line number Diff line number Diff line
@@ -205,7 +205,7 @@ static int _proc_do_string(char *data, int maxlen, int write,
	return 0;
}

static void warn_sysctl_write(struct ctl_table *table)
static void warn_sysctl_write(const struct ctl_table *table)
{
	pr_warn_once("%s wrote to %s when file position was not 0!\n"
		"This will not be supported in the future. To silence this\n"
@@ -223,7 +223,7 @@ static void warn_sysctl_write(struct ctl_table *table)
 * handlers can ignore the return value.
 */
static bool proc_first_pos_non_zero_ignore(loff_t *ppos,
					   struct ctl_table *table)
					   const struct ctl_table *table)
{
	if (!*ppos)
		return false;
@@ -468,7 +468,7 @@ static int do_proc_douintvec_conv(unsigned long *lvalp,

static const char proc_wspace_sep[] = { ' ', '\t', '\n' };

static int __do_proc_dointvec(void *tbl_data, struct ctl_table *table,
static int __do_proc_dointvec(void *tbl_data, const struct ctl_table *table,
		  int write, void *buffer,
		  size_t *lenp, loff_t *ppos,
		  int (*conv)(bool *negp, unsigned long *lvalp, int *valp,
@@ -541,7 +541,7 @@ static int __do_proc_dointvec(void *tbl_data, struct ctl_table *table,
	return err;
}

static int do_proc_dointvec(struct ctl_table *table, int write,
static int do_proc_dointvec(const struct ctl_table *table, int write,
		  void *buffer, size_t *lenp, loff_t *ppos,
		  int (*conv)(bool *negp, unsigned long *lvalp, int *valp,
			      int write, void *data),
@@ -552,7 +552,7 @@ static int do_proc_dointvec(struct ctl_table *table, int write,
}

static int do_proc_douintvec_w(unsigned int *tbl_data,
			       struct ctl_table *table,
			       const struct ctl_table *table,
			       void *buffer,
			       size_t *lenp, loff_t *ppos,
			       int (*conv)(unsigned long *lvalp,
@@ -639,7 +639,7 @@ static int do_proc_douintvec_r(unsigned int *tbl_data, void *buffer,
	return err;
}

static int __do_proc_douintvec(void *tbl_data, struct ctl_table *table,
static int __do_proc_douintvec(void *tbl_data, const struct ctl_table *table,
			       int write, void *buffer,
			       size_t *lenp, loff_t *ppos,
			       int (*conv)(unsigned long *lvalp,
@@ -675,7 +675,7 @@ static int __do_proc_douintvec(void *tbl_data, struct ctl_table *table,
	return do_proc_douintvec_r(i, buffer, lenp, ppos, conv, data);
}

int do_proc_douintvec(struct ctl_table *table, int write,
int do_proc_douintvec(const struct ctl_table *table, int write,
		      void *buffer, size_t *lenp, loff_t *ppos,
		      int (*conv)(unsigned long *lvalp,
				  unsigned int *valp,
@@ -977,16 +977,10 @@ int proc_dou8vec_minmax(struct ctl_table *table, int write,
	if (table->maxlen != sizeof(u8))
		return -EINVAL;

	if (table->extra1) {
	if (table->extra1)
		min = *(unsigned int *) table->extra1;
		if (min > 255U)
			return -EINVAL;
	}
	if (table->extra2) {
	if (table->extra2)
		max = *(unsigned int *) table->extra2;
		if (max > 255U)
			return -EINVAL;
	}

	tmp = *table;

@@ -1023,8 +1017,9 @@ static int sysrq_sysctl_handler(struct ctl_table *table, int write,
}
#endif

static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table,
		int write, void *buffer, size_t *lenp, loff_t *ppos,
static int __do_proc_doulongvec_minmax(void *data,
		const struct ctl_table *table, int write,
		void *buffer, size_t *lenp, loff_t *ppos,
		unsigned long convmul, unsigned long convdiv)
{
	unsigned long *i, *min, *max;
@@ -1096,7 +1091,7 @@ static int __do_proc_doulongvec_minmax(void *data, struct ctl_table *table,
	return err;
}

static int do_proc_doulongvec_minmax(struct ctl_table *table, int write,
static int do_proc_doulongvec_minmax(const struct ctl_table *table, int write,
		void *buffer, size_t *lenp, loff_t *ppos, unsigned long convmul,
		unsigned long convdiv)
{
Loading