public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH] tracing/hist: bound expression string construction
@ 2026-04-07  6:09 Pengpeng Hou
  2026-04-08 21:27 ` Steven Rostedt
  2026-04-09  2:56 ` [PATCH v2] tracing/hist: bound expression strings with seq_buf Pengpeng Hou
  0 siblings, 2 replies; 4+ messages in thread
From: Pengpeng Hou @ 2026-04-07  6:09 UTC (permalink / raw)
  To: Steven Rostedt, Masami Hiramatsu
  Cc: Mathieu Desnoyers, linux-trace-kernel, linux-kernel, pengpeng

expr_str() allocates a fixed MAX_FILTER_STR_VAL buffer and then builds
expression names with a series of raw strcat() appends. Nested operands,
constants and field flags can push the rendered string past that fixed
limit before the name is attached to the hist field.

Convert the construction helpers to explicit bounded appends and
propagate failures back to the expression parser when the rendered name
would exceed MAX_FILTER_STR_VAL.

Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
---
 kernel/trace/trace_events_hist.c | 101 +++++++++++++++++++++++--------
 1 file changed, 76 insertions(+), 25 deletions(-)

diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
index 73ea180cad55..caaa262360d2 100644
--- a/kernel/trace/trace_events_hist.c
+++ b/kernel/trace/trace_events_hist.c
@@ -1738,85 +1738,121 @@ static const char *get_hist_field_flags(struct hist_field *hist_field)
 	return flags_str;
 }
 
-static void expr_field_str(struct hist_field *field, char *expr)
+static bool expr_append(char *expr, size_t *len, const char *str)
 {
-	if (field->flags & HIST_FIELD_FL_VAR_REF)
-		strcat(expr, "$");
-	else if (field->flags & HIST_FIELD_FL_CONST) {
+	size_t str_len = strlen(str);
+
+	if (*len + str_len >= MAX_FILTER_STR_VAL)
+		return false;
+
+	memcpy(expr + *len, str, str_len + 1);
+	*len += str_len;
+	return true;
+}
+
+static bool expr_field_str(struct hist_field *field, char *expr, size_t *len)
+{
+	if (field->flags & HIST_FIELD_FL_VAR_REF) {
+		if (!expr_append(expr, len, "$"))
+			return false;
+	} else if (field->flags & HIST_FIELD_FL_CONST) {
 		char str[HIST_CONST_DIGITS_MAX];
+		int ret;
+
+		ret = snprintf(str, sizeof(str), "%llu", field->constant);
+		if (ret >= sizeof(str))
+			return false;
 
-		snprintf(str, HIST_CONST_DIGITS_MAX, "%llu", field->constant);
-		strcat(expr, str);
+		if (!expr_append(expr, len, str))
+			return false;
 	}
 
-	strcat(expr, hist_field_name(field, 0));
+	if (!expr_append(expr, len, hist_field_name(field, 0)))
+		return false;
 
 	if (field->flags && !(field->flags & HIST_FIELD_FL_VAR_REF)) {
 		const char *flags_str = get_hist_field_flags(field);
 
 		if (flags_str) {
-			strcat(expr, ".");
-			strcat(expr, flags_str);
+			if (!expr_append(expr, len, ".") ||
+			    !expr_append(expr, len, flags_str))
+				return false;
 		}
 	}
+
+	return true;
 }
 
 static char *expr_str(struct hist_field *field, unsigned int level)
 {
 	char *expr;
+	size_t len = 0;
 
 	if (level > 1)
-		return NULL;
+		return ERR_PTR(-EINVAL);
 
 	expr = kzalloc(MAX_FILTER_STR_VAL, GFP_KERNEL);
 	if (!expr)
-		return NULL;
+		return ERR_PTR(-ENOMEM);
 
 	if (!field->operands[0]) {
-		expr_field_str(field, expr);
+		if (!expr_field_str(field, expr, &len))
+			goto free;
 		return expr;
 	}
 
 	if (field->operator == FIELD_OP_UNARY_MINUS) {
 		char *subexpr;
 
-		strcat(expr, "-(");
+		if (!expr_append(expr, &len, "-("))
+			goto free;
 		subexpr = expr_str(field->operands[0], ++level);
 		if (!subexpr) {
-			kfree(expr);
-			return NULL;
+			goto free;
+		}
+		if (!expr_append(expr, &len, subexpr) ||
+		    !expr_append(expr, &len, ")")) {
+			kfree(subexpr);
+			goto free;
 		}
-		strcat(expr, subexpr);
-		strcat(expr, ")");
 
 		kfree(subexpr);
 
 		return expr;
 	}
 
-	expr_field_str(field->operands[0], expr);
+	if (!expr_field_str(field->operands[0], expr, &len))
+		goto free;
 
 	switch (field->operator) {
 	case FIELD_OP_MINUS:
-		strcat(expr, "-");
+		if (!expr_append(expr, &len, "-"))
+			goto free;
 		break;
 	case FIELD_OP_PLUS:
-		strcat(expr, "+");
+		if (!expr_append(expr, &len, "+"))
+			goto free;
 		break;
 	case FIELD_OP_DIV:
-		strcat(expr, "/");
+		if (!expr_append(expr, &len, "/"))
+			goto free;
 		break;
 	case FIELD_OP_MULT:
-		strcat(expr, "*");
+		if (!expr_append(expr, &len, "*"))
+			goto free;
 		break;
 	default:
-		kfree(expr);
-		return NULL;
+		goto free;
 	}
 
-	expr_field_str(field->operands[1], expr);
+	if (!expr_field_str(field->operands[1], expr, &len))
+		goto free;
 
 	return expr;
+
+free:
+	kfree(expr);
+	return ERR_PTR(-E2BIG);
 }
 
 /*
@@ -2630,6 +2666,11 @@ static struct hist_field *parse_unary(struct hist_trigger_data *hist_data,
 	expr->is_signed = operand1->is_signed;
 	expr->operator = FIELD_OP_UNARY_MINUS;
 	expr->name = expr_str(expr, 0);
+	if (IS_ERR(expr->name)) {
+		ret = PTR_ERR(expr->name);
+		expr->name = NULL;
+		goto free;
+	}
 	expr->type = kstrdup_const(operand1->type, GFP_KERNEL);
 	if (!expr->type) {
 		ret = -ENOMEM;
@@ -2842,6 +2883,11 @@ static struct hist_field *parse_expr(struct hist_trigger_data *hist_data,
 		destroy_hist_field(operand1, 0);
 
 		expr->name = expr_str(expr, 0);
+		if (IS_ERR(expr->name)) {
+			ret = PTR_ERR(expr->name);
+			expr->name = NULL;
+			goto free_expr;
+		}
 	} else {
 		/* The operand sizes should be the same, so just pick one */
 		expr->size = operand1->size;
@@ -2855,6 +2901,11 @@ static struct hist_field *parse_expr(struct hist_trigger_data *hist_data,
 		}
 
 		expr->name = expr_str(expr, 0);
+		if (IS_ERR(expr->name)) {
+			ret = PTR_ERR(expr->name);
+			expr->name = NULL;
+			goto free_expr;
+		}
 	}
 
 	return expr;
-- 
2.50.1 (Apple Git-155)



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

* Re: [PATCH] tracing/hist: bound expression string construction
  2026-04-07  6:09 [PATCH] tracing/hist: bound expression string construction Pengpeng Hou
@ 2026-04-08 21:27 ` Steven Rostedt
  2026-04-09  2:56 ` [PATCH v2] tracing/hist: bound expression strings with seq_buf Pengpeng Hou
  1 sibling, 0 replies; 4+ messages in thread
From: Steven Rostedt @ 2026-04-08 21:27 UTC (permalink / raw)
  To: Pengpeng Hou
  Cc: Masami Hiramatsu, Mathieu Desnoyers, linux-trace-kernel,
	linux-kernel

On Tue, 7 Apr 2026 14:09:10 +0800
Pengpeng Hou <pengpeng@iscas.ac.cn> wrote:

> expr_str() allocates a fixed MAX_FILTER_STR_VAL buffer and then builds
> expression names with a series of raw strcat() appends. Nested operands,
> constants and field flags can push the rendered string past that fixed
> limit before the name is attached to the hist field.
> 
> Convert the construction helpers to explicit bounded appends and
> propagate failures back to the expression parser when the rendered name
> would exceed MAX_FILTER_STR_VAL.
> 
> Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
> ---
>  kernel/trace/trace_events_hist.c | 101 +++++++++++++++++++++++--------
>  1 file changed, 76 insertions(+), 25 deletions(-)
> 
> diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
> index 73ea180cad55..caaa262360d2 100644
> --- a/kernel/trace/trace_events_hist.c
> +++ b/kernel/trace/trace_events_hist.c
> @@ -1738,85 +1738,121 @@ static const char *get_hist_field_flags(struct hist_field *hist_field)
>  	return flags_str;
>  }
>  
> -static void expr_field_str(struct hist_field *field, char *expr)
> +static bool expr_append(char *expr, size_t *len, const char *str)
>  {
> -	if (field->flags & HIST_FIELD_FL_VAR_REF)
> -		strcat(expr, "$");
> -	else if (field->flags & HIST_FIELD_FL_CONST) {
> +	size_t str_len = strlen(str);
> +
> +	if (*len + str_len >= MAX_FILTER_STR_VAL)
> +		return false;
> +
> +	memcpy(expr + *len, str, str_len + 1);
> +	*len += str_len;
> +	return true;
> +}
> +

This looks like a better job for seq_buf.


> +static bool expr_field_str(struct hist_field *field, char *expr, size_t *len)
> +{

	struct seq_buf s;

	seq_buf_init(&s, expr, MAX_FILTER_STR_VAL);

> +	if (field->flags & HIST_FIELD_FL_VAR_REF) {
> +		if (!expr_append(expr, len, "$"))
> +			return false;

		seq_buf_putc(&s, '$');

> +	} else if (field->flags & HIST_FIELD_FL_CONST) {
>  		char str[HIST_CONST_DIGITS_MAX];
> +		int ret;
> +
> +		ret = snprintf(str, sizeof(str), "%llu", field->constant);
> +		if (ret >= sizeof(str))
> +			return false;
>  
> -		snprintf(str, HIST_CONST_DIGITS_MAX, "%llu", field->constant);
> -		strcat(expr, str);

		seq_buf_printf(&s, "%llu", field->constant);

> +		if (!expr_append(expr, len, str))
> +			return false;
>  	}
>  
> -	strcat(expr, hist_field_name(field, 0));

	seq_buf_puts(&s, hist_field_name(field, 0));

> +	if (!expr_append(expr, len, hist_field_name(field, 0)))
> +		return false;
>  
>  	if (field->flags && !(field->flags & HIST_FIELD_FL_VAR_REF)) {
>  		const char *flags_str = get_hist_field_flags(field);
>  
>  		if (flags_str) {
> -			strcat(expr, ".");
> -			strcat(expr, flags_str);

			seq_buf_printf(&s, ".%s", flags_str);

> +			if (!expr_append(expr, len, ".") ||
> +			    !expr_append(expr, len, flags_str))
> +				return false;
>  		}
>  	}

	/* Add terminating character */
	seq_buf_str(&s);

	return seq_buf_overflow(&s) ? false : true;

> +
> +	return true;
>  }
>  
>  static char *expr_str(struct hist_field *field, unsigned int level)
>  {
>  	char *expr;
> +	size_t len = 0;
>  

This could all be converted too.

-- Steve

>  	if (level > 1)
> -		return NULL;
> +		return ERR_PTR(-EINVAL);
>  
>  	expr = kzalloc(MAX_FILTER_STR_VAL, GFP_KERNEL);
>  	if (!expr)
> -		return NULL;
> +		return ERR_PTR(-ENOMEM);
>  
>  	if (!field->operands[0]) {
> -		expr_field_str(field, expr);
> +		if (!expr_field_str(field, expr, &len))
> +			goto free;
>  		return expr;
>  	}
>  
>  	if (field->operator == FIELD_OP_UNARY_MINUS) {
>  		char *subexpr;
>  
> -		strcat(expr, "-(");
> +		if (!expr_append(expr, &len, "-("))
> +			goto free;
>  		subexpr = expr_str(field->operands[0], ++level);
>  		if (!subexpr) {
> -			kfree(expr);
> -			return NULL;
> +			goto free;
> +		}
> +		if (!expr_append(expr, &len, subexpr) ||
> +		    !expr_append(expr, &len, ")")) {
> +			kfree(subexpr);
> +			goto free;
>  		}
> -		strcat(expr, subexpr);
> -		strcat(expr, ")");
>  
>  		kfree(subexpr);
>  
>  		return expr;
>  	}
>  
> -	expr_field_str(field->operands[0], expr);
> +	if (!expr_field_str(field->operands[0], expr, &len))
> +		goto free;
>  
>  	switch (field->operator) {
>  	case FIELD_OP_MINUS:
> -		strcat(expr, "-");
> +		if (!expr_append(expr, &len, "-"))
> +			goto free;
>  		break;
>  	case FIELD_OP_PLUS:
> -		strcat(expr, "+");
> +		if (!expr_append(expr, &len, "+"))
> +			goto free;
>  		break;
>  	case FIELD_OP_DIV:
> -		strcat(expr, "/");
> +		if (!expr_append(expr, &len, "/"))
> +			goto free;
>  		break;
>  	case FIELD_OP_MULT:
> -		strcat(expr, "*");
> +		if (!expr_append(expr, &len, "*"))
> +			goto free;
>  		break;
>  	default:
> -		kfree(expr);
> -		return NULL;
> +		goto free;
>  	}
>  
> -	expr_field_str(field->operands[1], expr);
> +	if (!expr_field_str(field->operands[1], expr, &len))
> +		goto free;
>  
>  	return expr;
> +
> +free:
> +	kfree(expr);
> +	return ERR_PTR(-E2BIG);
>  }
>  
>  /*
> @@ -2630,6 +2666,11 @@ static struct hist_field *parse_unary(struct hist_trigger_data *hist_data,
>  	expr->is_signed = operand1->is_signed;
>  	expr->operator = FIELD_OP_UNARY_MINUS;
>  	expr->name = expr_str(expr, 0);
> +	if (IS_ERR(expr->name)) {
> +		ret = PTR_ERR(expr->name);
> +		expr->name = NULL;
> +		goto free;
> +	}
>  	expr->type = kstrdup_const(operand1->type, GFP_KERNEL);
>  	if (!expr->type) {
>  		ret = -ENOMEM;
> @@ -2842,6 +2883,11 @@ static struct hist_field *parse_expr(struct hist_trigger_data *hist_data,
>  		destroy_hist_field(operand1, 0);
>  
>  		expr->name = expr_str(expr, 0);
> +		if (IS_ERR(expr->name)) {
> +			ret = PTR_ERR(expr->name);
> +			expr->name = NULL;
> +			goto free_expr;
> +		}
>  	} else {
>  		/* The operand sizes should be the same, so just pick one */
>  		expr->size = operand1->size;
> @@ -2855,6 +2901,11 @@ static struct hist_field *parse_expr(struct hist_trigger_data *hist_data,
>  		}
>  
>  		expr->name = expr_str(expr, 0);
> +		if (IS_ERR(expr->name)) {
> +			ret = PTR_ERR(expr->name);
> +			expr->name = NULL;
> +			goto free_expr;
> +		}
>  	}
>  
>  	return expr;


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

* [PATCH v2] tracing/hist: bound expression strings with seq_buf
  2026-04-07  6:09 [PATCH] tracing/hist: bound expression string construction Pengpeng Hou
  2026-04-08 21:27 ` Steven Rostedt
@ 2026-04-09  2:56 ` Pengpeng Hou
  2026-04-14  9:10   ` Steven Rostedt
  1 sibling, 1 reply; 4+ messages in thread
From: Pengpeng Hou @ 2026-04-09  2:56 UTC (permalink / raw)
  To: Steven Rostedt, Masami Hiramatsu
  Cc: Mathieu Desnoyers, linux-trace-kernel, linux-kernel, pengpeng

expr_str() allocates a fixed MAX_FILTER_STR_VAL buffer and then builds
expression names with a series of raw strcat() appends. Nested operands,
constants and field flags can push the rendered string past that fixed
limit before the name is attached to the hist field.

Build the expression strings with seq_buf and propagate failures back to
the expression parser when the rendered name would exceed
MAX_FILTER_STR_VAL.

Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
---
Changes since v1:
- replace the previous bounded append helper and manual length tracking
  with seq_buf as suggested by Steven Rostedt
- keep the -E2BIG propagation back into parse_unary() and parse_expr()

 kernel/trace/trace_events_hist.c | 93 ++++++++++++++++++++++----------
 1 file changed, 64 insertions(+), 29 deletions(-)

diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
index 73ea180cad55..09aaedb92993 100644
--- a/kernel/trace/trace_events_hist.c
+++ b/kernel/trace/trace_events_hist.c
@@ -8,6 +8,7 @@
 #include <linux/module.h>
 #include <linux/kallsyms.h>
 #include <linux/security.h>
+#include <linux/seq_buf.h>
 #include <linux/mutex.h>
 #include <linux/slab.h>
 #include <linux/stacktrace.h>
@@ -1738,85 +1739,104 @@ static const char *get_hist_field_flags(struct hist_field *hist_field)
 	return flags_str;
 }
 
-static void expr_field_str(struct hist_field *field, char *expr)
+static bool expr_field_str(struct hist_field *field, struct seq_buf *s)
 {
+	const char *field_name;
+
 	if (field->flags & HIST_FIELD_FL_VAR_REF)
-		strcat(expr, "$");
-	else if (field->flags & HIST_FIELD_FL_CONST) {
-		char str[HIST_CONST_DIGITS_MAX];
+		seq_buf_putc(s, '$');
+	else if (field->flags & HIST_FIELD_FL_CONST)
+		seq_buf_printf(s, "%llu", field->constant);
 
-		snprintf(str, HIST_CONST_DIGITS_MAX, "%llu", field->constant);
-		strcat(expr, str);
-	}
+	field_name = hist_field_name(field, 0);
+	if (!field_name)
+		return false;
 
-	strcat(expr, hist_field_name(field, 0));
+	seq_buf_puts(s, field_name);
 
 	if (field->flags && !(field->flags & HIST_FIELD_FL_VAR_REF)) {
 		const char *flags_str = get_hist_field_flags(field);
 
-		if (flags_str) {
-			strcat(expr, ".");
-			strcat(expr, flags_str);
-		}
+		if (flags_str)
+			seq_buf_printf(s, ".%s", flags_str);
 	}
+
+	return !seq_buf_has_overflowed(s);
 }
 
 static char *expr_str(struct hist_field *field, unsigned int level)
 {
 	char *expr;
+	struct seq_buf s;
+	int ret = -E2BIG;
 
 	if (level > 1)
-		return NULL;
+		return ERR_PTR(-EINVAL);
 
 	expr = kzalloc(MAX_FILTER_STR_VAL, GFP_KERNEL);
 	if (!expr)
-		return NULL;
+		return ERR_PTR(-ENOMEM);
+
+	seq_buf_init(&s, expr, MAX_FILTER_STR_VAL);
 
 	if (!field->operands[0]) {
-		expr_field_str(field, expr);
+		if (!expr_field_str(field, &s))
+			goto free;
+		seq_buf_str(&s);
 		return expr;
 	}
 
 	if (field->operator == FIELD_OP_UNARY_MINUS) {
 		char *subexpr;
 
-		strcat(expr, "-(");
+		seq_buf_puts(&s, "-(");
 		subexpr = expr_str(field->operands[0], ++level);
-		if (!subexpr) {
-			kfree(expr);
-			return NULL;
+		if (IS_ERR(subexpr)) {
+			ret = PTR_ERR(subexpr);
+			goto free;
 		}
-		strcat(expr, subexpr);
-		strcat(expr, ")");
+		seq_buf_puts(&s, subexpr);
+		seq_buf_putc(&s, ')');
 
 		kfree(subexpr);
+		if (seq_buf_has_overflowed(&s))
+			goto free;
 
+		seq_buf_str(&s);
 		return expr;
 	}
 
-	expr_field_str(field->operands[0], expr);
+	if (!expr_field_str(field->operands[0], &s))
+		goto free;
 
 	switch (field->operator) {
 	case FIELD_OP_MINUS:
-		strcat(expr, "-");
+		seq_buf_putc(&s, '-');
 		break;
 	case FIELD_OP_PLUS:
-		strcat(expr, "+");
+		seq_buf_putc(&s, '+');
 		break;
 	case FIELD_OP_DIV:
-		strcat(expr, "/");
+		seq_buf_putc(&s, '/');
 		break;
 	case FIELD_OP_MULT:
-		strcat(expr, "*");
+		seq_buf_putc(&s, '*');
 		break;
 	default:
-		kfree(expr);
-		return NULL;
+		ret = -EINVAL;
+		goto free;
 	}
 
-	expr_field_str(field->operands[1], expr);
+	if (seq_buf_has_overflowed(&s) ||
+	    !expr_field_str(field->operands[1], &s))
+		goto free;
 
+	seq_buf_str(&s);
 	return expr;
+
+free:
+	kfree(expr);
+	return ERR_PTR(ret);
 }
 
 /*
@@ -2630,6 +2650,11 @@ static struct hist_field *parse_unary(struct hist_trigger_data *hist_data,
 	expr->is_signed = operand1->is_signed;
 	expr->operator = FIELD_OP_UNARY_MINUS;
 	expr->name = expr_str(expr, 0);
+	if (IS_ERR(expr->name)) {
+		ret = PTR_ERR(expr->name);
+		expr->name = NULL;
+		goto free;
+	}
 	expr->type = kstrdup_const(operand1->type, GFP_KERNEL);
 	if (!expr->type) {
 		ret = -ENOMEM;
@@ -2842,6 +2867,11 @@ static struct hist_field *parse_expr(struct hist_trigger_data *hist_data,
 		destroy_hist_field(operand1, 0);
 
 		expr->name = expr_str(expr, 0);
+		if (IS_ERR(expr->name)) {
+			ret = PTR_ERR(expr->name);
+			expr->name = NULL;
+			goto free_expr;
+		}
 	} else {
 		/* The operand sizes should be the same, so just pick one */
 		expr->size = operand1->size;
@@ -2855,6 +2885,11 @@ static struct hist_field *parse_expr(struct hist_trigger_data *hist_data,
 		}
 
 		expr->name = expr_str(expr, 0);
+		if (IS_ERR(expr->name)) {
+			ret = PTR_ERR(expr->name);
+			expr->name = NULL;
+			goto free_expr;
+		}
 	}
 
 	return expr;
-- 
2.50.1 (Apple Git-155)


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

* Re: [PATCH v2] tracing/hist: bound expression strings with seq_buf
  2026-04-09  2:56 ` [PATCH v2] tracing/hist: bound expression strings with seq_buf Pengpeng Hou
@ 2026-04-14  9:10   ` Steven Rostedt
  0 siblings, 0 replies; 4+ messages in thread
From: Steven Rostedt @ 2026-04-14  9:10 UTC (permalink / raw)
  To: Pengpeng Hou
  Cc: Masami Hiramatsu, Mathieu Desnoyers, linux-trace-kernel,
	linux-kernel

On Thu, 9 Apr 2026 10:56:28 +0800
Pengpeng Hou <pengpeng@iscas.ac.cn> wrote:

Hi Pengpeng,

Again, subject should be:

  tracing: Bound histogram expression strings with seq_buf

> expr_str() allocates a fixed MAX_FILTER_STR_VAL buffer and then builds
> expression names with a series of raw strcat() appends. Nested operands,
> constants and field flags can push the rendered string past that fixed
> limit before the name is attached to the hist field.
> 
> Build the expression strings with seq_buf and propagate failures back to
> the expression parser when the rendered name would exceed
> MAX_FILTER_STR_VAL.
> 
> Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
> ---
> Changes since v1:
> - replace the previous bounded append helper and manual length tracking
>   with seq_buf as suggested by Steven Rostedt
> - keep the -E2BIG propagation back into parse_unary() and parse_expr()
> 
>  kernel/trace/trace_events_hist.c | 93 ++++++++++++++++++++++----------
>  1 file changed, 64 insertions(+), 29 deletions(-)
> 
> diff --git a/kernel/trace/trace_events_hist.c b/kernel/trace/trace_events_hist.c
> index 73ea180cad55..09aaedb92993 100644
> --- a/kernel/trace/trace_events_hist.c
> +++ b/kernel/trace/trace_events_hist.c
> @@ -8,6 +8,7 @@
>  #include <linux/module.h>
>  #include <linux/kallsyms.h>
>  #include <linux/security.h>
> +#include <linux/seq_buf.h>
>  #include <linux/mutex.h>
>  #include <linux/slab.h>
>  #include <linux/stacktrace.h>
> @@ -1738,85 +1739,104 @@ static const char *get_hist_field_flags(struct hist_field *hist_field)
>  	return flags_str;
>  }
>  
> -static void expr_field_str(struct hist_field *field, char *expr)
> +static bool expr_field_str(struct hist_field *field, struct seq_buf *s)
>  {
> +	const char *field_name;
> +
>  	if (field->flags & HIST_FIELD_FL_VAR_REF)
> -		strcat(expr, "$");
> -	else if (field->flags & HIST_FIELD_FL_CONST) {
> -		char str[HIST_CONST_DIGITS_MAX];
> +		seq_buf_putc(s, '$');
> +	else if (field->flags & HIST_FIELD_FL_CONST)
> +		seq_buf_printf(s, "%llu", field->constant);
>  
> -		snprintf(str, HIST_CONST_DIGITS_MAX, "%llu", field->constant);
> -		strcat(expr, str);
> -	}
> +	field_name = hist_field_name(field, 0);
> +	if (!field_name)
> +		return false;
>  
> -	strcat(expr, hist_field_name(field, 0));
> +	seq_buf_puts(s, field_name);
>  
>  	if (field->flags && !(field->flags & HIST_FIELD_FL_VAR_REF)) {
>  		const char *flags_str = get_hist_field_flags(field);
>  
> -		if (flags_str) {
> -			strcat(expr, ".");
> -			strcat(expr, flags_str);
> -		}
> +		if (flags_str)
> +			seq_buf_printf(s, ".%s", flags_str);
>  	}
> +
> +	return !seq_buf_has_overflowed(s);
>  }
>  
>  static char *expr_str(struct hist_field *field, unsigned int level)
>  {
>  	char *expr;
> +	struct seq_buf s;
> +	int ret = -E2BIG;
>  
>  	if (level > 1)
> -		return NULL;
> +		return ERR_PTR(-EINVAL);
>  
>  	expr = kzalloc(MAX_FILTER_STR_VAL, GFP_KERNEL);
>  	if (!expr)
> -		return NULL;
> +		return ERR_PTR(-ENOMEM);

A patch should do one thing at a time. This patch appears to be doing
two things: fixing the bound strings, returning errors instead of NULL.

Please split this up into two patches. One that changes the return
values from NULL to ERR_PTR() and the other to use seq_buf.

Thanks,

-- Steve


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

end of thread, other threads:[~2026-04-14  9:10 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-07  6:09 [PATCH] tracing/hist: bound expression string construction Pengpeng Hou
2026-04-08 21:27 ` Steven Rostedt
2026-04-09  2:56 ` [PATCH v2] tracing/hist: bound expression strings with seq_buf Pengpeng Hou
2026-04-14  9:10   ` Steven Rostedt

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