diff mbox series

[v3,2/5] tracing: Use __free() in trace_probe for cleanup

Message ID 173625062518.1375434.10516553019799245668.stgit@devnote2 (mailing list archive)
State Superseded
Headers show
Series tracing/probes: Cleanup with guard and __free for kprobe and fprobe | expand

Commit Message

Masami Hiramatsu (Google) Jan. 7, 2025, 11:50 a.m. UTC
From: Masami Hiramatsu (Google) <mhiramat@kernel.org>

Use __free() in trace_probe to cleanup some gotos.

Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
---
 kernel/trace/trace_probe.c |   52 +++++++++++++++-----------------------------
 1 file changed, 18 insertions(+), 34 deletions(-)

Comments

Steven Rostedt Jan. 7, 2025, 3:36 p.m. UTC | #1
On Tue,  7 Jan 2025 20:50:25 +0900
"Masami Hiramatsu (Google)" <mhiramat@kernel.org> wrote:

> @@ -1790,18 +1777,15 @@ int traceprobe_expand_dentry_args(int argc, const char *argv[], char **buf)
>  				       offsetof(struct file, f_path.dentry),
>  				       equal ? equal + 1 : tmp);
>  
> -		kfree(tmp);
> +		kfree(no_free_ptr(tmp));

I don't get this? You are telling the compiler not to free tmp, because you
decided to free it yourself? Why not just remove the kfree() here altogether?

-- Steve


>  		if (ret >= bufsize - used)
> -			goto nomem;
> +			return -ENOMEM;
>  		argv[i] = tmpbuf + used;
>  		used += ret + 1;
>  	}
>  
> -	*buf = tmpbuf;
> +	*buf = no_free_ptr(tmpbuf);
>  	return 0;
> -nomem:
> -	kfree(tmpbuf);
> -	return -ENOMEM;
>  }
Masami Hiramatsu (Google) Jan. 8, 2025, 12:38 a.m. UTC | #2
On Tue, 7 Jan 2025 10:36:43 -0500
Steven Rostedt <rostedt@goodmis.org> wrote:

> On Tue,  7 Jan 2025 20:50:25 +0900
> "Masami Hiramatsu (Google)" <mhiramat@kernel.org> wrote:
> 
> > @@ -1790,18 +1777,15 @@ int traceprobe_expand_dentry_args(int argc, const char *argv[], char **buf)
> >  				       offsetof(struct file, f_path.dentry),
> >  				       equal ? equal + 1 : tmp);
> >  
> > -		kfree(tmp);
> > +		kfree(no_free_ptr(tmp));
> 
> I don't get this? You are telling the compiler not to free tmp, because you
> decided to free it yourself? Why not just remove the kfree() here altogether?

In the for-loop block, the __free() work only when we exit the loop, not
each iteration. In each iteration, kstrdup() is assigned to the 'tmp',
so we need to kfree() each time.

Hmm, maybe this is a sign that I should not use __free() for the 'tmp',
or I should call kfree(tmp) right before kstrdup(), like below.

 	for (i = 0; i < argc; i++) {
		char *tmp __free(kfree) = NULL;
		...
		kfree(tmp);
		tmp = kstrdup(argv[i], GFP_KERNEL);
	}

Does this make sense?

> 
> -- Steve
> 
> 
> >  		if (ret >= bufsize - used)
> > -			goto nomem;
> > +			return -ENOMEM;
> >  		argv[i] = tmpbuf + used;
> >  		used += ret + 1;
> >  	}
> >  
> > -	*buf = tmpbuf;
> > +	*buf = no_free_ptr(tmpbuf);
> >  	return 0;
> > -nomem:
> > -	kfree(tmpbuf);
> > -	return -ENOMEM;
> >  }
Steven Rostedt Jan. 8, 2025, 1:34 a.m. UTC | #3
On Wed, 8 Jan 2025 09:38:43 +0900
Masami Hiramatsu (Google) <mhiramat@kernel.org> wrote:

> > I don't get this? You are telling the compiler not to free tmp, because you
> > decided to free it yourself? Why not just remove the kfree() here altogether?  
> 
> In the for-loop block, the __free() work only when we exit the loop, not
> each iteration. In each iteration, kstrdup() is assigned to the 'tmp',
> so we need to kfree() each time.

Really? It doesn't trigger for each iteration? That's rather unintuitive. :-/
And sounds buggy, as wouldn't that then cause a memory leak?

I would say not to use __free() for tmp at all. Because now it's just
getting confusing.

-- Steve


> 
> Hmm, maybe this is a sign that I should not use __free() for the 'tmp',
> or I should call kfree(tmp) right before kstrdup(), like below.
> 
>  	for (i = 0; i < argc; i++) {
> 		char *tmp __free(kfree) = NULL;
> 		...
> 		kfree(tmp);
> 		tmp = kstrdup(argv[i], GFP_KERNEL);
> 	}
> 
> Does this make sense?
Masami Hiramatsu (Google) Jan. 8, 2025, 2:03 a.m. UTC | #4
On Tue, 7 Jan 2025 20:34:32 -0500
Steven Rostedt <rostedt@goodmis.org> wrote:

> On Wed, 8 Jan 2025 09:38:43 +0900
> Masami Hiramatsu (Google) <mhiramat@kernel.org> wrote:
> 
> > > I don't get this? You are telling the compiler not to free tmp, because you
> > > decided to free it yourself? Why not just remove the kfree() here altogether?  
> > 
> > In the for-loop block, the __free() work only when we exit the loop, not
> > each iteration. In each iteration, kstrdup() is assigned to the 'tmp',
> > so we need to kfree() each time.
> 
> Really? It doesn't trigger for each iteration? That's rather unintuitive. :-/
> And sounds buggy, as wouldn't that then cause a memory leak?

Ahh, sorry, it was my misunderstood. I made a test code and confirmed that
kfree() is called in each iteration. Previously I checked but I confused the result.

----------
#include <stdio.h>

void count_func(int *p)
{
	printf("Scope out: %d\n", *p);
}

int main(void)
{
	for (int i = 0; i < 10; i++) {
		int j __attribute((cleanup(count_func))) = 0;

		j++;
	}
	return 0;
}
----------

$ ./loop_cleanup 
Scope out: 1
Scope out: 1
Scope out: 1
Scope out: 1
Scope out: 1
Scope out: 1
Scope out: 1
Scope out: 1
Scope out: 1
Scope out: 1

Let me fix that.

Thanks,

> 
> I would say not to use __free() for tmp at all. Because now it's just
> getting confusing.
> 
> -- Steve
> 
> 
> > 
> > Hmm, maybe this is a sign that I should not use __free() for the 'tmp',
> > or I should call kfree(tmp) right before kstrdup(), like below.
> > 
> >  	for (i = 0; i < argc; i++) {
> > 		char *tmp __free(kfree) = NULL;
> > 		...
> > 		kfree(tmp);
> > 		tmp = kstrdup(argv[i], GFP_KERNEL);
> > 	}
> > 
> > Does this make sense?
>
diff mbox series

Patch

diff --git a/kernel/trace/trace_probe.c b/kernel/trace/trace_probe.c
index 16a5e368e7b7..bf6a7b81ae95 100644
--- a/kernel/trace/trace_probe.c
+++ b/kernel/trace/trace_probe.c
@@ -1409,7 +1409,7 @@  static int traceprobe_parse_probe_arg_body(const char *argv, ssize_t *size,
 					   struct traceprobe_parse_context *ctx)
 {
 	struct fetch_insn *code, *tmp = NULL;
-	char *type, *arg;
+	char *type, *arg __free(kfree) = NULL;
 	int ret, len;
 
 	len = strlen(argv);
@@ -1426,22 +1426,16 @@  static int traceprobe_parse_probe_arg_body(const char *argv, ssize_t *size,
 		return -ENOMEM;
 
 	parg->comm = kstrdup(arg, GFP_KERNEL);
-	if (!parg->comm) {
-		ret = -ENOMEM;
-		goto out;
-	}
+	if (!parg->comm)
+		return -ENOMEM;
 
 	type = parse_probe_arg_type(arg, parg, ctx);
-	if (IS_ERR(type)) {
-		ret = PTR_ERR(type);
-		goto out;
-	}
+	if (IS_ERR(type))
+		return PTR_ERR(type);
 
 	code = tmp = kcalloc(FETCH_INSN_MAX, sizeof(*code), GFP_KERNEL);
-	if (!code) {
-		ret = -ENOMEM;
-		goto out;
-	}
+	if (!code)
+		return -ENOMEM;
 	code[FETCH_INSN_MAX - 1].op = FETCH_OP_END;
 
 	ctx->last_type = NULL;
@@ -1497,8 +1491,6 @@  static int traceprobe_parse_probe_arg_body(const char *argv, ssize_t *size,
 				kfree(code->data);
 	}
 	kfree(tmp);
-out:
-	kfree(arg);
 
 	return ret;
 }
@@ -1668,7 +1660,7 @@  const char **traceprobe_expand_meta_args(int argc, const char *argv[],
 {
 	const struct btf_param *params = NULL;
 	int i, j, n, used, ret, args_idx = -1;
-	const char **new_argv = NULL;
+	const char **new_argv __free(kfree) = NULL;
 
 	ret = argv_has_var_arg(argc, argv, &args_idx, ctx);
 	if (ret < 0)
@@ -1707,7 +1699,7 @@  const char **traceprobe_expand_meta_args(int argc, const char *argv[],
 				ret = sprint_nth_btf_arg(n, "", buf + used,
 							 bufsize - used, ctx);
 				if (ret < 0)
-					goto error;
+					return ERR_PTR(ret);
 
 				new_argv[j++] = buf + used;
 				used += ret + 1;
@@ -1721,25 +1713,20 @@  const char **traceprobe_expand_meta_args(int argc, const char *argv[],
 			n = simple_strtoul(argv[i] + 4, &type, 10);
 			if (type && !(*type == ':' || *type == '\0')) {
 				trace_probe_log_err(0, BAD_VAR);
-				ret = -ENOENT;
-				goto error;
+				return ERR_PTR(-ENOENT);
 			}
 			/* Note: $argN starts from $arg1 */
 			ret = sprint_nth_btf_arg(n - 1, type, buf + used,
 						 bufsize - used, ctx);
 			if (ret < 0)
-				goto error;
+				return ERR_PTR(ret);
 			new_argv[j++] = buf + used;
 			used += ret + 1;
 		} else
 			new_argv[j++] = argv[i];
 	}
 
-	return new_argv;
-
-error:
-	kfree(new_argv);
-	return ERR_PTR(ret);
+	return_ptr(new_argv);
 }
 
 /* @buf: *buf must be equal to NULL. Caller must to free *buf */
@@ -1747,14 +1734,14 @@  int traceprobe_expand_dentry_args(int argc, const char *argv[], char **buf)
 {
 	int i, used, ret;
 	const int bufsize = MAX_DENTRY_ARGS_LEN;
-	char *tmpbuf = NULL;
+	char *tmpbuf __free(kfree) = NULL;
 
 	if (*buf)
 		return -EINVAL;
 
 	used = 0;
 	for (i = 0; i < argc; i++) {
-		char *tmp;
+		char *tmp __free(kfree) = NULL;
 		char *equal;
 		size_t arg_len;
 
@@ -1769,7 +1756,7 @@  int traceprobe_expand_dentry_args(int argc, const char *argv[], char **buf)
 
 		tmp = kstrdup(argv[i], GFP_KERNEL);
 		if (!tmp)
-			goto nomem;
+			return -ENOMEM;
 
 		equal = strchr(tmp, '=');
 		if (equal)
@@ -1790,18 +1777,15 @@  int traceprobe_expand_dentry_args(int argc, const char *argv[], char **buf)
 				       offsetof(struct file, f_path.dentry),
 				       equal ? equal + 1 : tmp);
 
-		kfree(tmp);
+		kfree(no_free_ptr(tmp));
 		if (ret >= bufsize - used)
-			goto nomem;
+			return -ENOMEM;
 		argv[i] = tmpbuf + used;
 		used += ret + 1;
 	}
 
-	*buf = tmpbuf;
+	*buf = no_free_ptr(tmpbuf);
 	return 0;
-nomem:
-	kfree(tmpbuf);
-	return -ENOMEM;
 }
 
 void traceprobe_finish_parse(struct traceprobe_parse_context *ctx)