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

* Re: Bootconfig updates for v7.4
  2026-10-22  0:14 Bootconfig updates for v7.4 Masami Hiramatsu
@ 2026-10-22  0:43 ` Masami Hiramatsu
  0 siblings, 0 replies; 2+ messages in thread
From: Masami Hiramatsu @ 2026-10-22  0:43 UTC (permalink / raw)
  To: Masami Hiramatsu
  Cc: Linus Torvalds, Sang-Heon Jeon, Steven Rostedt, linux-kernel


Sorry, please ignore this. I missed [GIT PULL] tag. So I resent with it.

On Thu, 22 Oct 2026 09:14:10 +0900
Masami Hiramatsu (Google) <mhiramat@kernel.org> wrote:

> 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>
> 


-- 
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®