mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* Bootconfig updates for v7.4
@ 2026-10-22  0:14 Masami Hiramatsu
  2026-10-22  0:43 ` Masami Hiramatsu
  0 siblings, 1 reply; 2+ messages in thread
From: Masami Hiramatsu @ 2026-10-22  0:14 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Masami Hiramatsu, Sang-Heon Jeon, Steven Rostedt,
	Masami Hiramatsu, linux-kernel

Hi Linus,

Bootconfig updates for v7.4:

- bootconfig: Parser robustness and validation improvements
 . Skip printing early params to cmdline from bootconfig: Suppress
   early_param keys from /proc/cmdline and warn the user, since bootconfig
   does not invoke early_param handlers and silently ignoring them is
   misleading.
 . Reject unexpected data after null character: Detect and reject non-null
   data following an intermediate null character in the bootconfig buffer,
   preventing silently ignored configuration after an early termination.

- bootconfig: Code cleanups
 . Merge _xbc_exit() into xbc_exit(): Since memblock_free() now handles
   late freeing and NULL addresses, the separate _xbc_exit() helper and
   its early argument are unnecessary. Inline it into xbc_exit().
 . Remove redundant assignment in xbc_parse_array(): The node->child = 0
   assignment after the loop duplicates what xbc_init_node() already did.
 . Skip internal tree sanity checks in kernel: Move the node index bounds
   loop from xbc_verify_tree() into a userspace-only xbc_sanity_check_tree()
   to reduce boot-time overhead, since out-of-bounds indices cannot be
   produced by malformed input.
 . Move BOOTCONFIG_FOOTER_SIZE to include/linux/bootconfig.h: Share the
   footer size constant between kernel and user-space tools, replacing the
   hardcoded (BOOTCONFIG_MAGIC_LEN + 8) expression in init/main.c.

- tools/bootconfig: Tooling improvements
 . Remove unused PAGE_SIZE macro from the bootconfig tool.
 . Consolidate xbc_init() to error message wrapper: Use
   init_xbc_with_error() for all bootconfig initialization paths in the
   tool for simpler and more consistent error handling.


Please pull the latest bootconfig-v7.4 tree, which can be found at:


  git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace.git
bootconfig-v7.4

Tag SHA1: 8c84ad1d280719b36079cdd280da8214c3a8013d
Head SHA1: 677f83795fdfc25ddc51b665a9111f5ff199f7ca


Masami Hiramatsu (Google) (5):
      bootconfig: Skip printing early params to cmdline from bootconfig
      bootconfig: Reject unexpected data after null character
      tools/bootconfig: Consolidate xbc_init() to error message wrapper
      bootconfig: Skip internal tree sanity checks in kernel
      bootconfig: Move BOOTCONFIG_FOOTER_SIZE to include/linux/bootconfig.h

Sang-Heon Jeon (3):
      bootconfig: merge _xbc_exit() into xbc_exit()
      bootconfig: remove redundant assignment in xbc_parse_array()
      tools/bootconfig: remove unused PAGE_SIZE macro

----
 include/linux/bootconfig.h          |  21 ++++---
 init/main.c                         |  75 ++++++++++++++++++++++---
 lib/bootconfig.c                    |  84 +++++++++++++++++-----------
 tools/bootconfig/main.c             | 108 +++++++++++++++---------------------
 tools/bootconfig/test-bootconfig.sh |  12 ++++
 5 files changed, 192 insertions(+), 108 deletions(-)
---------------------------
diff --git a/include/linux/bootconfig.h b/include/linux/bootconfig.h
index deda507500da..260a6f46042e 100644
--- a/include/linux/bootconfig.h
+++ b/include/linux/bootconfig.h
@@ -27,6 +27,10 @@ bool __init cmdline_has_extra_options(void);
 #define BOOTCONFIG_ALIGN	(1 << BOOTCONFIG_ALIGN_SHIFT)
 #define BOOTCONFIG_ALIGN_MASK	(BOOTCONFIG_ALIGN - 1)
 
+/* Bootconfig footer is [size][csum][BOOTCONFIG_MAGIC]. */
+#define BOOTCONFIG_FOOTER_SIZE	\
+	(sizeof(uint32_t) * 2 + BOOTCONFIG_MAGIC_LEN)
+
 /**
  * xbc_calc_checksum() - Calculate checksum of bootconfig
  * @data: Bootconfig data.
@@ -266,7 +270,15 @@ int __init xbc_node_compose_key_after(struct xbc_node *root,
 			struct xbc_node *node, char *buf, size_t size);
 
 /* Render key/value pairs under @root as a flat cmdline string */
-int __init xbc_snprint_cmdline(char *buf, size_t size, struct xbc_node *root);
+typedef bool (*xbc_cmdline_filter_fn)(struct xbc_node *node, const char *key, void *data);
+
+int __init xbc_snprint_cmdline_filter(char *buf, size_t size, struct xbc_node *root,
+				      xbc_cmdline_filter_fn filter, void *data);
+
+static inline int __init xbc_snprint_cmdline(char *buf, size_t size, struct xbc_node *root)
+{
+	return xbc_snprint_cmdline_filter(buf, size, root, NULL, NULL);
+}
 
 /**
  * xbc_node_compose_key() - Compose full key string of the XBC node
@@ -291,12 +303,7 @@ int __init xbc_init(const char *buf, size_t size, const char **emsg, int *epos);
 int __init xbc_get_info(int *node_size, size_t *data_size);
 
 /* XBC cleanup data structures */
-void __init _xbc_exit(bool early);
-
-static __always_inline void xbc_exit(void)
-{
-	_xbc_exit(false);
-}
+void __init xbc_exit(void);
 
 /* XBC embedded bootconfig data in kernel */
 #ifdef CONFIG_BOOT_CONFIG_EMBED
diff --git a/init/main.c b/init/main.c
index 16749bb7a219..861e07f6c05c 100644
--- a/init/main.c
+++ b/init/main.c
@@ -278,7 +278,7 @@ static void * __init get_boot_config_from_initrd(size_t *_size)
 	int i;
 
 	if (!initrd_end || initrd_end < initrd_start ||
-	    initrd_end - initrd_start < BOOTCONFIG_MAGIC_LEN + 8)
+	    initrd_end - initrd_start < BOOTCONFIG_FOOTER_SIZE)
 		return NULL;
 
 	data = (char *)initrd_end - BOOTCONFIG_MAGIC_LEN;
@@ -336,9 +336,64 @@ static void * __init get_boot_config_from_initrd(size_t *_size)
 
 #ifdef CONFIG_BOOT_CONFIG
 
+/* Check if a parameter name matches an obs_kernel_param entry */
+static bool __init match_obs_param(const char *param, const char *pattern)
+{
+	size_t n = strlen(pattern);
+
+	if (n > 0 && pattern[n - 1] == '=')
+		return parameqn(param, pattern, n - 1) && param[n - 1] == '\0';
+
+	return parameq(param, pattern);
+}
+
+/* Return true if the given param is only defined by early_param(). */
+static bool __init is_early_only_param(const char *param)
+{
+	const struct obs_kernel_param *p;
+	const struct kernel_param *kp;
+	bool has_early = false;
+
+	/* If handled by a regular module_param, it is not early-only */
+	for (kp = __start___param; kp < __stop___param; kp++) {
+		if (parameq(param, kp->name))
+			return false;
+	}
+
+	/* Check __setup / early_param table */
+	for (p = __setup_start; p < __setup_end; p++) {
+		if (match_obs_param(param, p->str)) {
+			if (!p->early)
+				return false;
+			has_early = true;
+		}
+	}
+	return has_early;
+}
+
+struct xbc_filter_data {
+	bool warn;
+};
+
+static bool __init xbc_kernel_param_filter(struct xbc_node *node,
+					   const char *key, void *data)
+{
+	struct xbc_filter_data *fdata = data;
+
+	if (is_early_only_param(key)) {
+		if (fdata && fdata->warn)
+			pr_warn("bootconfig: early_param 'kernel.%s' cannot be applied from bootconfig, skipping\n",
+				key);
+		return false;
+	}
+	return true;
+}
+
 /* Make an extra command line under given key word */
-static char * __init xbc_make_cmdline(const char *key)
+static char * __init xbc_make_cmdline(const char *key, bool is_kernel)
 {
+	struct xbc_filter_data fdata = { .warn = true };
+	xbc_cmdline_filter_fn filter = is_kernel ? xbc_kernel_param_filter : NULL;
 	struct xbc_node *root;
 	char *new_cmdline;
 	int ret, len = 0;
@@ -347,8 +402,12 @@ static char * __init xbc_make_cmdline(const char *key)
 	if (!root)
 		return NULL;
 
-	/* Count required buffer size */
-	len = xbc_snprint_cmdline(NULL, 0, root);
+	/*
+	 * Pass 1: Count required buffer size. Emit warnings for skipped early
+	 * params in this pass so they are logged even if all parameters under
+	 * @key are skipped and len becomes 0.
+	 */
+	len = xbc_snprint_cmdline_filter(NULL, 0, root, filter, &fdata);
 	if (len <= 0)
 		return NULL;
 
@@ -358,7 +417,9 @@ static char * __init xbc_make_cmdline(const char *key)
 		return NULL;
 	}
 
-	ret = xbc_snprint_cmdline(new_cmdline, len + 1, root);
+	/* Pass 2: Render into buffer with warnings suppressed */
+	fdata.warn = false;
+	ret = xbc_snprint_cmdline_filter(new_cmdline, len + 1, root, filter, &fdata);
 	if (ret < 0 || ret > len) {
 		pr_err("Failed to print extra kernel cmdline.\n");
 		memblock_free(new_cmdline, len + 1);
@@ -432,9 +493,9 @@ static void __init setup_boot_config(void)
 		 * before this series.
 		 */
 		if (!from_embedded || !xbc_embedded_cmdline_applied())
-			extra_command_line = xbc_make_cmdline("kernel");
+			extra_command_line = xbc_make_cmdline("kernel", true);
 		/* Also, "init." keys are init arguments */
-		extra_init_args = xbc_make_cmdline("init");
+		extra_init_args = xbc_make_cmdline("init", false);
 	}
 	return;
 }
diff --git a/lib/bootconfig.c b/lib/bootconfig.c
index 89c88e359179..819685d6c68d 100644
--- a/lib/bootconfig.c
+++ b/lib/bootconfig.c
@@ -187,12 +187,9 @@ static inline void * __init xbc_alloc_mem(size_t size)
 	return memblock_alloc(size, SMP_CACHE_BYTES);
 }
 
-static inline void __init xbc_free_mem(void *addr, size_t size, bool early)
+static inline void __init xbc_free_mem(void *addr, size_t size)
 {
-	if (early)
-		memblock_free(addr, size);
-	else if (addr)
-		memblock_free(addr, size);
+	memblock_free(addr, size);
 }
 
 #else /* !__KERNEL__ */
@@ -202,7 +199,7 @@ static inline void *xbc_alloc_mem(size_t size)
 	return calloc(1, size);
 }
 
-static inline void xbc_free_mem(void *addr, size_t size, bool early)
+static inline void xbc_free_mem(void *addr, size_t size)
 {
 	free(addr);
 }
@@ -539,18 +536,21 @@ static char xbc_namebuf[XBC_KEYLEN_MAX] __initdata;
 #define rest(dst, end) ((end) > (dst) ? (end) - (dst) : 0)
 
 /**
- * xbc_snprint_cmdline() - Render bootconfig keys under @root as a cmdline string
+ * xbc_snprint_cmdline_filter() - Render bootconfig keys as cmdline string
  * @buf: Destination buffer (may be NULL when @size is 0 to query the length)
  * @size: Size of @buf in bytes
  * @root: Subtree root whose key=value pairs should be rendered
+ * @filter: Filter callback (returns true to include, false to skip; may be NULL)
+ * @data: Private context passed to @filter
  *
  * Walk all key/value pairs under @root and emit them as a space-separated
  * cmdline string into @buf. Values containing whitespace are quoted with
- * double quotes. Returns the number of bytes that would be written if @buf
- * were large enough (matching snprintf semantics), or a negative errno on
- * failure.
+ * double quotes. Keys for which @filter returns false are omitted.
+ * Returns the number of bytes that would be written if @buf were large enough
+ * (matching snprintf semantics), or a negative errno on failure.
  */
-int __init xbc_snprint_cmdline(char *buf, size_t size, struct xbc_node *root)
+int __init xbc_snprint_cmdline_filter(char *buf, size_t size, struct xbc_node *root,
+				      xbc_cmdline_filter_fn filter, void *data)
 {
 	struct xbc_node *knode, *vnode;
 	const char *val, *q;
@@ -582,6 +582,9 @@ int __init xbc_snprint_cmdline(char *buf, size_t size, struct xbc_node *root)
 		if (ret < 0)
 			return ret;
 
+		if (filter && !filter(knode, xbc_namebuf, data))
+			continue;
+
 		vnode = xbc_node_get_child(knode);
 		if (!vnode) {
 			ret = snprintf(buf ? buf + len : NULL, rest(len, size),
@@ -836,7 +839,6 @@ static int __init xbc_parse_array(char **__v)
 			return -ENOMEM;
 		*__v = next;
 	} while (c == ',');
-	node->child = 0;
 
 	return c;
 }
@@ -1003,9 +1005,30 @@ static int __init xbc_close_brace(char **k, char *n)
 	return __xbc_close_brace(n - 1);
 }
 
+#ifndef __KERNEL__
+/* Sanity check for regression: node indices must be within bounds */
+static int __init xbc_sanity_check_tree(void)
+{
+	int i;
+
+	for (i = 0; i < xbc_node_num; i++) {
+		if (xbc_nodes[i].next >= xbc_node_num) {
+			return xbc_parse_error("No closing brace",
+				xbc_node_get_data(xbc_nodes + i));
+		}
+		if (xbc_nodes[i].child >= xbc_node_num) {
+			return xbc_parse_error("Broken child node",
+				xbc_node_get_data(xbc_nodes + i));
+		}
+	}
+
+	return 0;
+}
+#endif
+
 static int __init xbc_verify_tree(void)
 {
-	int i, depth;
+	int depth;
 	size_t len, wlen;
 	struct xbc_node *n, *m;
 
@@ -1022,17 +1045,6 @@ static int __init xbc_verify_tree(void)
 		return -ENOENT;
 	}
 
-	for (i = 0; i < xbc_node_num; i++) {
-		if (xbc_nodes[i].next >= xbc_node_num) {
-			return xbc_parse_error("No closing brace",
-				xbc_node_get_data(xbc_nodes + i));
-		}
-		if (xbc_nodes[i].child >= xbc_node_num) {
-			return xbc_parse_error("Broken child node",
-				xbc_node_get_data(xbc_nodes + i));
-		}
-	}
-
 	/* Key tree limitation check */
 	n = &xbc_nodes[0];
 	depth = 1;
@@ -1119,24 +1131,30 @@ static int __init xbc_parse_tree(void)
 		}
 	} while (!ret);
 
+	if (!ret) {
+		while (p < xbc_data + xbc_data_size - 1 && *p == '\0')
+			p++;
+		if (p < xbc_data + xbc_data_size - 1)
+			ret = xbc_parse_error("Unexpected data after null character", p);
+	}
+
 	return ret;
 }
 
 /**
- * _xbc_exit() - Clean up all parsed bootconfig
- * @early: Set true if this is called before budy system is initialized.
+ * xbc_exit() - Clean up all parsed bootconfig
  *
  * This clears all data structures of parsed bootconfig on memory.
  * If you need to reuse xbc_init() with new boot config, you can
  * use this.
  */
-void __init _xbc_exit(bool early)
+void __init xbc_exit(void)
 {
-	xbc_free_mem(xbc_data, xbc_data_size, early);
+	xbc_free_mem(xbc_data, xbc_data_size);
 	xbc_data = NULL;
 	xbc_data_size = 0;
 	xbc_node_num = 0;
-	xbc_free_mem(xbc_nodes, sizeof(struct xbc_node) * XBC_NODE_MAX, early);
+	xbc_free_mem(xbc_nodes, sizeof(struct xbc_node) * XBC_NODE_MAX);
 	xbc_nodes = NULL;
 	brace_index = 0;
 }
@@ -1189,20 +1207,24 @@ int __init xbc_init(const char *data, size_t size, const char **emsg, int *epos)
 	if (!xbc_nodes) {
 		if (emsg)
 			*emsg = "Failed to allocate bootconfig nodes";
-		_xbc_exit(true);
+		xbc_exit();
 		return -ENOMEM;
 	}
 
 	ret = xbc_parse_tree();
 	if (!ret)
 		ret = xbc_verify_tree();
+#ifndef __KERNEL__
+	if (!ret)
+		ret = xbc_sanity_check_tree();
+#endif
 
 	if (ret < 0) {
 		if (epos)
 			*epos = xbc_err_pos;
 		if (emsg)
 			*emsg = xbc_err_msg;
-		_xbc_exit(true);
+		xbc_exit();
 	} else {
 		ret = xbc_node_num;
 	}
diff --git a/tools/bootconfig/main.c b/tools/bootconfig/main.c
index 17d971d47f87..56d76bdeaa09 100644
--- a/tools/bootconfig/main.c
+++ b/tools/bootconfig/main.c
@@ -17,9 +17,38 @@
 
 #define pr_err(fmt, ...) fprintf(stderr, fmt, ##__VA_ARGS__)
 
-/* Bootconfig footer is [size][csum][BOOTCONFIG_MAGIC]. */
-#define BOOTCONFIG_FOOTER_SIZE	\
-	(sizeof(uint32_t) * 2 + BOOTCONFIG_MAGIC_LEN)
+static void show_xbc_error(const char *data, const char *msg, int pos)
+{
+	int lin = 1, col, i;
+
+	if (pos < 0) {
+		pr_err("Error: %s.\n", msg);
+		return;
+	}
+
+	/* Note that pos starts from 0 but lin and col should start from 1. */
+	col = pos + 1;
+	for (i = 0; i < pos; i++) {
+		if (data[i] == '\n') {
+			lin++;
+			col = pos - i;
+		}
+	}
+	pr_err("Parse Error: %s at %d:%d\n", msg, lin, col);
+
+}
+
+static int init_xbc_with_error(char *buf, int len)
+{
+	const char *msg;
+	int ret, pos;
+
+	ret = xbc_init(buf, len, &msg, &pos);
+	if (ret < 0)
+		show_xbc_error(buf, msg, pos);
+
+	return ret;
+}
 
 static int xbc_show_value(struct xbc_node *node, bool semicolon)
 {
@@ -134,8 +163,6 @@ static void xbc_show_list(void)
 	}
 }
 
-#define PAGE_SIZE	4096
-
 static int load_xbc_fd(int fd, char **buf, int size)
 {
 	int ret;
@@ -197,7 +224,6 @@ static int load_xbc_from_initrd(int fd, char **buf)
 	int ret;
 	uint32_t size = 0, csum = 0, rcsum;
 	char magic[BOOTCONFIG_MAGIC_LEN];
-	const char *msg;
 
 	ret = fstat(fd, &stat);
 	if (ret < 0)
@@ -249,52 +275,9 @@ static int load_xbc_from_initrd(int fd, char **buf)
 		return -EINVAL;
 	}
 
-	ret = xbc_init(*buf, size, &msg, NULL);
-	/* Wrong data */
-	if (ret < 0) {
-		pr_err("parse error: %s.\n", msg);
-		return ret;
-	}
-
-	return size;
-}
-
-static void show_xbc_error(const char *data, const char *msg, int pos)
-{
-	int lin = 1, col, i;
-
-	if (pos < 0) {
-		pr_err("Error: %s.\n", msg);
-		return;
-	}
-
-	/* Note that pos starts from 0 but lin and col should start from 1. */
-	col = pos + 1;
-	for (i = 0; i < pos; i++) {
-		if (data[i] == '\n') {
-			lin++;
-			col = pos - i;
-		}
-	}
-	pr_err("Parse Error: %s at %d:%d\n", msg, lin, col);
+	ret = init_xbc_with_error(*buf, size);
 
-}
-
-static int init_xbc_with_error(char *buf, int len)
-{
-	char *copy = strdup(buf);
-	const char *msg;
-	int ret, pos;
-
-	if (!copy)
-		return -ENOMEM;
-
-	ret = xbc_init(buf, len, &msg, &pos);
-	if (ret < 0)
-		show_xbc_error(copy, msg, pos);
-	free(copy);
-
-	return ret;
+	return ret < 0 ? ret : size;
 }
 
 static int show_xbc_kernel_cmdline(void)
@@ -423,9 +406,8 @@ static int apply_xbc(const char *path, const char *xbc_path)
 	char *buf, *data;
 	size_t total_size;
 	struct stat stat;
-	const char *msg;
 	uint32_t size, csum;
-	int pos, pad;
+	int pad;
 	int ret, fd;
 
 	ret = load_xbc_file(xbc_path, &buf);
@@ -433,9 +415,18 @@ static int apply_xbc(const char *path, const char *xbc_path)
 		pr_err("Failed to load %s : %d\n", xbc_path, ret);
 		return ret;
 	}
-	size = strlen(buf) + 1;
+	size = ret;
+	if (size == 0 || buf[size - 1] != '\0')
+		size++;
 	csum = xbc_calc_checksum(buf, size);
 
+	/* Verify the data format */
+	ret = init_xbc_with_error(buf, size);
+	if (ret < 0) {
+		free(buf);
+		return ret;
+	}
+
 	/* Backup the bootconfig data */
 	data = calloc(size + BOOTCONFIG_ALIGN + BOOTCONFIG_FOOTER_SIZE, 1);
 	if (!data) {
@@ -444,15 +435,6 @@ static int apply_xbc(const char *path, const char *xbc_path)
 	}
 	memcpy(data, buf, size);
 
-	/* Check the data format */
-	ret = xbc_init(buf, size, &msg, &pos);
-	if (ret < 0) {
-		show_xbc_error(data, msg, pos);
-		free(data);
-		free(buf);
-
-		return ret;
-	}
 	printf("Apply %s to %s\n", xbc_path, path);
 	xbc_get_info(&ret, NULL);
 	printf("\tNumber of nodes: %d\n", ret);
diff --git a/tools/bootconfig/test-bootconfig.sh b/tools/bootconfig/test-bootconfig.sh
index fc69f815ce4a..530ce7e28d63 100755
--- a/tools/bootconfig/test-bootconfig.sh
+++ b/tools/bootconfig/test-bootconfig.sh
@@ -180,6 +180,18 @@ EOF
 $BOOTCONF -a $TEMPCONF $INITRD 2> $OUTFILE
 xpass grep -q "1:1" $OUTFILE
 
+echo "Intermediate null character test"
+printf "key = value\n\0extra = data\n" > $TEMPCONF
+xfail $BOOTCONF -a $TEMPCONF $INITRD
+$BOOTCONF -a $TEMPCONF $INITRD 2> $OUTFILE
+xpass grep -q "Unexpected" $OUTFILE
+
+echo "Trailing null character test"
+printf "key = value\n\0" > $TEMPCONF
+xpass $BOOTCONF -a $TEMPCONF $INITRD
+$BOOTCONF $INITRD > $OUTFILE
+xpass grep -q "value" $OUTFILE
+
 echo "=== expected failure cases ==="
 for i in samples/bad-* ; do
   xfail $BOOTCONF -a $i $INITRD
-- 
Masami Hiramatsu (Google) <mhiramat@kernel.org>


^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-10-22  0:43 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-22  0:14 Bootconfig updates for v7.4 Masami Hiramatsu
2026-10-22  0:43 ` Masami Hiramatsu

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®