diff mbox series

[v2] parse: handle __cleanup__ attribute

Message ID 8d596a06-9f25-4d9f-8282-deb2d03a6b0a@moroto.mountain (mailing list archive)
State Mainlined, archived
Headers show
Series [v2] parse: handle __cleanup__ attribute | expand

Commit Message

Dan Carpenter Dec. 8, 2023, 9:49 a.m. UTC
The kernel has recently started using the __cleanup__ attribute.  Save
a pointer to cleanup function.

Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org>
---
v2:  The first version of this patch had a bug handling a list of
     declarations.  I had to add a .cleanup = NULL at the start of
     the loops iterations in declaration_list() and
     external_declaration().

 parse.c  | 26 +++++++++++++++++++++++++-
 symbol.h |  2 ++
 2 files changed, 27 insertions(+), 1 deletion(-)

Comments

Luc Van Oostenryck Dec. 11, 2023, 1:16 p.m. UTC | #1
On Fri, Dec 08, 2023 at 12:49:34PM +0300, Dan Carpenter wrote:
> The kernel has recently started using the __cleanup__ attribute.  Save
> a pointer to cleanup function.
> 
> Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org>
> ---
> v2:  The first version of this patch had a bug handling a list of
>      declarations.  I had to add a .cleanup = NULL at the start of
>      the loops iterations in declaration_list() and
>      external_declaration().

OK. See some notes here under, but first at all please forgive my very long delay.
 
> diff --git a/parse.c b/parse.c
> index 3d6fef7cb011..e5b5e6acc062 100644
> --- a/parse.c
> +++ b/parse.c
> @@ -537,6 +542,7 @@ static struct init_keyword {
>  	/* Attributes */
>  	D("packed",		&packed_op),
>  	D("aligned",		&aligned_op),
> +	D("__cleanup__",	&cleanup_op),

This should simply be D("cleanup" (to accept both the plain form and the __X__ form).

> @@ -1964,6 +1984,7 @@ struct token *typename(struct token *token, struct symbol **p, int *forced)
>  	token = declarator(token, &ctx);
>  	apply_modifiers(token->pos, &ctx);
>  	sym->ctype = ctx.ctype;
> +	sym->cleanup = ctx.cleanup;

I don't think this should be needed because the cleanup attribute should be
'attached' to individual symbols, not their types (but I have no idea what GCC do).

> @@ -2924,6 +2945,7 @@ struct token *external_declaration(struct token *token, struct symbol_list **lis
>  
>  	decl->ctype = ctx.ctype;
>  	decl->ctype.modifiers |= mod;
> +	decl->cleanup = ctx.cleanup;

Similarly, the attribute should only be applied to automatic variables,
so this should not be needed/should be detected as an error.

> diff --git a/symbol.h b/symbol.h
> index 5270fcd73a10..88130c15d4bd 100644
> --- a/symbol.h
> +++ b/symbol.h
> @@ -204,6 +205,7 @@ struct symbol {
>  			struct statement *inline_stmt;
>  			struct symbol_list *inline_symbol_list;
>  			struct expression *initializer;
> +			struct expression *cleanup;

This annoys me a little bit as it adds one more member to struct symbol
which is already the biggest memory user.
But currently this is fine as other currently ignored attributes are also
concerned. A solution is needed for all of them but there is no urgency.

It would be nice to have some testcases though.

Anyway, I can take this patch as-is modulo the few changes here above or
you can send a v3 if you prefer. But it may take a few days before I'm able
to push it to k.org.

Best regards,
-- Luc
Dan Carpenter Dec. 12, 2023, 9:39 a.m. UTC | #2
On Mon, Dec 11, 2023 at 02:16:49PM +0100, Luc Van Oostenryck wrote:
> On Fri, Dec 08, 2023 at 12:49:34PM +0300, Dan Carpenter wrote:
> > The kernel has recently started using the __cleanup__ attribute.  Save
> > a pointer to cleanup function.
> > 
> > Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org>
> > ---
> > v2:  The first version of this patch had a bug handling a list of
> >      declarations.  I had to add a .cleanup = NULL at the start of
> >      the loops iterations in declaration_list() and
> >      external_declaration().
> 
> OK. See some notes here under, but first at all please forgive my very long delay.
>  

I would demand my money back, but I checked with my accountant and it
turns out I'm not paying you anything.  :P

> > diff --git a/parse.c b/parse.c
> > index 3d6fef7cb011..e5b5e6acc062 100644
> > --- a/parse.c
> > +++ b/parse.c
> > @@ -537,6 +542,7 @@ static struct init_keyword {
> >  	/* Attributes */
> >  	D("packed",		&packed_op),
> >  	D("aligned",		&aligned_op),
> > +	D("__cleanup__",	&cleanup_op),

> 
> This should simply be D("cleanup" (to accept both the plain form and the __X__ form).
> 
> > @@ -1964,6 +1984,7 @@ struct token *typename(struct token *token, struct symbol **p, int *forced)
> >  	token = declarator(token, &ctx);
> >  	apply_modifiers(token->pos, &ctx);
> >  	sym->ctype = ctx.ctype;
> > +	sym->cleanup = ctx.cleanup;
> 
> I don't think this should be needed because the cleanup attribute should be
> 'attached' to individual symbols, not their types (but I have no idea what GCC do).
> 
> > @@ -2924,6 +2945,7 @@ struct token *external_declaration(struct token *token, struct symbol_list **lis
> >  
> >  	decl->ctype = ctx.ctype;
> >  	decl->ctype.modifiers |= mod;
> > +	decl->cleanup = ctx.cleanup;
> 
> Similarly, the attribute should only be applied to automatic variables,
> so this should not be needed/should be detected as an error.
> 

Yeah.  There are a couple other "cleanup" lines later in the function
that should be deleted as well, I see.

Let me test this out resend in a few days.

regards,
dan carpenter
Dan Carpenter Dec. 13, 2023, 10:14 a.m. UTC | #3
On Tue, Dec 12, 2023 at 12:39:40PM +0300, Dan Carpenter wrote:
> > > @@ -2924,6 +2945,7 @@ struct token *external_declaration(struct token *token, struct symbol_list **lis
> > >  
> > >  	decl->ctype = ctx.ctype;
> > >  	decl->ctype.modifiers |= mod;
> > > +	decl->cleanup = ctx.cleanup;
> > 
> > Similarly, the attribute should only be applied to automatic variables,
> > so this should not be needed/should be detected as an error.
> > 
> 
> Yeah.  There are a couple other "cleanup" lines later in the function
> that should be deleted as well, I see.

Hm...  Something went wrong.  When I remove this assignment then the
cleanup function isn't saved here:

void sched_exec(void)
{
	struct task_struct *p = get_current();
	struct migration_arg arg;
	int dest_cpu;

	for (class_raw_spinlock_irqsave_t scope __attribute__((__cleanup__(class_raw_spinlock_irqsave_destructor))) = class_raw_spinlock_irqsave_constructor(&p->pi_lock), *done = ((void *)0);
	     class_raw_spinlock_irqsave_lock_ptr(&scope) && !done;
	     done = (void *)1) {
		dest_cpu = p->sched_class->select_task_rq(p, task_cpu(p), 0x02);
		if (dest_cpu == debug_smp_processor_id())
			return;

		if (__builtin_expect(!!(!cpu_active(dest_cpu)), 0))
 			return;

		arg = (struct migration_arg){ p, dest_cpu };
	}
	stop_one_cpu(task_cpu(p), migration_cpu_stop, &arg);
}

regards,
dan carpenter
Luc Van Oostenryck Dec. 14, 2023, 1:05 p.m. UTC | #4
On Wed, Dec 13, 2023 at 01:14:29PM +0300, Dan Carpenter wrote:
> On Tue, Dec 12, 2023 at 12:39:40PM +0300, Dan Carpenter wrote:
> > > > @@ -2924,6 +2945,7 @@ struct token *external_declaration(struct token *token, struct symbol_list **lis
> > > >  
> > > >  	decl->ctype = ctx.ctype;
> > > >  	decl->ctype.modifiers |= mod;
> > > > +	decl->cleanup = ctx.cleanup;
> > > 
> > > Similarly, the attribute should only be applied to automatic variables,
> > > so this should not be needed/should be detected as an error.
> > > 
> > 
> > Yeah.  There are a couple other "cleanup" lines later in the function
> > that should be deleted as well, I see.
> 
> Hm...  Something went wrong.  When I remove this assignment then the
> cleanup function isn't saved here:

Mmmmhh yes, my bad.
I thought that the parsing functions followed closely the names of the
C grammar in the spec. They largely do but not in this case (they can't
because some context is needed to distinguish between 'declaration' and
'function-definition').

Would the following patch be OK for you when applied on top of your v2?
It contains:
- the attribute can be removed from the list of ignored attributes
- I prefer to add the "attribute_cleanup," on its own line
- I added some checks and a few corresponding testcases
- the s/D("__cleanup__"/D("cleanup"/
- and the removal of 'sym->cleanup = ctx.cleanup;' from typename() which
  I think is still unneeded.

Best regards,
-- Luc


diff --git a/gcc-attr-list.h b/gcc-attr-list.h
index c78001757229..928ea3889e01 100644
--- a/gcc-attr-list.h
+++ b/gcc-attr-list.h
@@ -24,7 +24,6 @@ GCC_ATTR(brk_interrupt)
 GCC_ATTR(callee_pop_aggregate_return)
 GCC_ATTR(cb)
 GCC_ATTR(cdecl)
-GCC_ATTR(cleanup)
 GCC_ATTR(cmse_nonsecure_call)
 GCC_ATTR(cmse_nonsecure_entry)
 GCC_ATTR(cold)
diff --git a/parse.c b/parse.c
index e5b5e6acc062..f868bf63a0f5 100644
--- a/parse.c
+++ b/parse.c
@@ -79,11 +79,11 @@ typedef struct token *attr_t(struct token *, struct symbol *,
 			     struct decl_state *);
 
 static attr_t
-	attribute_packed, attribute_aligned, attribute_cleanup,
-	attribute_modifier,
+	attribute_packed, attribute_aligned, attribute_modifier,
 	attribute_function,
 	attribute_bitwise,
 	attribute_address_space, attribute_context,
+	attribute_cleanup,
 	attribute_designated_init,
 	attribute_transparent_union, ignore_attribute,
 	attribute_mode, attribute_force;
@@ -542,7 +542,7 @@ static struct init_keyword {
 	/* Attributes */
 	D("packed",		&packed_op),
 	D("aligned",		&aligned_op),
-	D("__cleanup__",	&cleanup_op),
+	D("cleanup",		&cleanup_op),
 	D("nocast",		&attr_mod_op,		.mods = MOD_NOCAST),
 	D("noderef",		&attr_mod_op,		.mods = MOD_NODEREF),
 	D("safe",		&attr_mod_op,		.mods = MOD_SAFE),
@@ -1125,10 +1125,18 @@ static struct token *attribute_cleanup(struct token *token, struct symbol *attr,
 	struct expression *expr = NULL;
 
 	if (match_op(token, '(')) {
-		token = parens_expression(token, &expr, "in attribute");
+		token = token->next;
+		if (match_op(token, ')'))
+			sparse_error(token->pos, "an argument is expected for attribute 'cleanup'");
+		else if (token_type(token) != TOKEN_IDENT)
+			sparse_error(token->pos, "argument is not an identifier");
+		token = primary_expression(token, &expr);
 		if (expr && expr->type == EXPR_SYMBOL)
 			ctx->cleanup = expr;
+		return expect(token, ')', "after attribute's argument'");
 	}
+
+	sparse_error(token->pos, "an argument is expected for attribute 'cleanup'");
 	return token;
 }
 
@@ -1984,7 +1992,6 @@ struct token *typename(struct token *token, struct symbol **p, int *forced)
 	token = declarator(token, &ctx);
 	apply_modifiers(token->pos, &ctx);
 	sym->ctype = ctx.ctype;
-	sym->cleanup = ctx.cleanup;
 	sym->endpos = token->pos;
 	class = ctx.storage_class;
 	if (forced)
diff --git a/validation/parsing/attr-cleanup.c b/validation/parsing/attr-cleanup.c
new file mode 100644
index 000000000000..ac64649c2ac1
--- /dev/null
+++ b/validation/parsing/attr-cleanup.c
@@ -0,0 +1,33 @@
+#define __cleanup(F)	__attribute__((__cleanup__(F)))
+
+void fun(int *ptr);
+
+int test(int n);
+int test(int n)
+{
+	int var __attribute__((cleanup(fun))) = 1;
+	int alt __cleanup(fun) = 2;
+	int mis __cleanup(0) = 3;
+	int non __attribute__((cleanup));
+	int mis __attribute__((cleanup()));
+	int two __attribute__((cleanup(fun, fun)));
+
+        for (int i __cleanup(fun) = 0; i < n; i++)
+		;
+
+	var = 5;
+	return 0;
+}
+
+/*
+ * check-name: attr-cleanup
+ * check-command: sparse -Wunknown-attribute $file
+ *
+ * check-error-start
+parsing/attr-cleanup.c:10:17: error: argument is not an identifier
+parsing/attr-cleanup.c:11:39: error: an argument is expected for attribute 'cleanup'
+parsing/attr-cleanup.c:12:40: error: an argument is expected for attribute 'cleanup'
+parsing/attr-cleanup.c:13:43: error: Expected ) after attribute's argument'
+parsing/attr-cleanup.c:13:43: error: got ,
+ * check-error-end
+ */
Dan Carpenter Dec. 14, 2023, 1:20 p.m. UTC | #5
On Thu, Dec 14, 2023 at 02:05:22PM +0100, Luc Van Oostenryck wrote:
> On Wed, Dec 13, 2023 at 01:14:29PM +0300, Dan Carpenter wrote:
> > On Tue, Dec 12, 2023 at 12:39:40PM +0300, Dan Carpenter wrote:
> > > > > @@ -2924,6 +2945,7 @@ struct token *external_declaration(struct token *token, struct symbol_list **lis
> > > > >  
> > > > >  	decl->ctype = ctx.ctype;
> > > > >  	decl->ctype.modifiers |= mod;
> > > > > +	decl->cleanup = ctx.cleanup;
> > > > 
> > > > Similarly, the attribute should only be applied to automatic variables,
> > > > so this should not be needed/should be detected as an error.
> > > > 
> > > 
> > > Yeah.  There are a couple other "cleanup" lines later in the function
> > > that should be deleted as well, I see.
> > 
> > Hm...  Something went wrong.  When I remove this assignment then the
> > cleanup function isn't saved here:
> 
> Mmmmhh yes, my bad.
> I thought that the parsing functions followed closely the names of the
> C grammar in the spec. They largely do but not in this case (they can't
> because some context is needed to distinguish between 'declaration' and
> 'function-definition').
> 
> Would the following patch be OK for you when applied on top of your v2?
> It contains:
> - the attribute can be removed from the list of ignored attributes
> - I prefer to add the "attribute_cleanup," on its own line
> - I added some checks and a few corresponding testcases
> - the s/D("__cleanup__"/D("cleanup"/
> - and the removal of 'sym->cleanup = ctx.cleanup;' from typename() which
>   I think is still unneeded.

Yep.  Perfect.  Thanks so much!

regards,
dan carpenter
Luc Van Oostenryck Dec. 18, 2023, 1:51 p.m. UTC | #6
On Thu, Dec 14, 2023 at 04:20:20PM +0300, Dan Carpenter wrote:
> Yep.  Perfect.  Thanks so much!

Pushed now.
-- Luc
Dmitry Torokhov Jan. 17, 2024, 8:20 p.m. UTC | #7
Hi,

On Mon, Dec 18, 2023 at 02:51:32PM +0100, Luc Van Oostenryck wrote:
> On Thu, Dec 14, 2023 at 04:20:20PM +0300, Dan Carpenter wrote:
> > Yep.  Perfect.  Thanks so much!
> 
> Pushed now.

Any chance someone is looking at making context tracking working for
code annotated as __cleanup? We already have a bunch of code using
constructs like:

	...
	guard(spinlock_irqsave)(&gpio_lock);

	if (!test_bit(FLAG_REQUESTED, &desc->flags))
		return NULL;
	...

which resuls in:

$ make C=1 W=1 drivers/gpio/gpiolib.o
  CALL    scripts/checksyscalls.sh
  DESCEND objtool
  INSTALL libsubcmd_headers
  CC      drivers/gpio/gpiolib.o
  CHECK   drivers/gpio/gpiolib.c
drivers/gpio/gpiolib.c:2359:6: warning: context imbalance in 'gpiochip_dup_line_label' - different lock contexts for basic block

and I expect we'll see more and more of this.

Thanks.
Andy Shevchenko Feb. 29, 2024, 2:03 p.m. UTC | #8
On Wed, Jan 17, 2024 at 12:20:13PM -0800, Dmitry Torokhov wrote:
> On Mon, Dec 18, 2023 at 02:51:32PM +0100, Luc Van Oostenryck wrote:
> > On Thu, Dec 14, 2023 at 04:20:20PM +0300, Dan Carpenter wrote:
> > > Yep.  Perfect.  Thanks so much!
> > 
> > Pushed now.
> 
> Any chance someone is looking at making context tracking working for
> code annotated as __cleanup? We already have a bunch of code using
> constructs like:
> 
> 	...
> 	guard(spinlock_irqsave)(&gpio_lock);
> 
> 	if (!test_bit(FLAG_REQUESTED, &desc->flags))
> 		return NULL;
> 	...
> 
> which resuls in:
> 
> $ make C=1 W=1 drivers/gpio/gpiolib.o
>   CALL    scripts/checksyscalls.sh
>   DESCEND objtool
>   INSTALL libsubcmd_headers
>   CC      drivers/gpio/gpiolib.o
>   CHECK   drivers/gpio/gpiolib.c
> drivers/gpio/gpiolib.c:2359:6: warning: context imbalance in 'gpiochip_dup_line_label' - different lock contexts for basic block
> 
> and I expect we'll see more and more of this.

+1 here. It's quite annoying for every Linux kernel developer in the world
(which are at least 2k of active ones).
diff mbox series

Patch

diff --git a/parse.c b/parse.c
index 3d6fef7cb011..e5b5e6acc062 100644
--- a/parse.c
+++ b/parse.c
@@ -79,7 +79,8 @@  typedef struct token *attr_t(struct token *, struct symbol *,
 			     struct decl_state *);
 
 static attr_t
-	attribute_packed, attribute_aligned, attribute_modifier,
+	attribute_packed, attribute_aligned, attribute_cleanup,
+	attribute_modifier,
 	attribute_function,
 	attribute_bitwise,
 	attribute_address_space, attribute_context,
@@ -361,6 +362,10 @@  static struct symbol_op aligned_op = {
 	.attribute = attribute_aligned,
 };
 
+static struct symbol_op cleanup_op = {
+	.attribute = attribute_cleanup,
+};
+
 static struct symbol_op attr_mod_op = {
 	.attribute = attribute_modifier,
 };
@@ -537,6 +542,7 @@  static struct init_keyword {
 	/* Attributes */
 	D("packed",		&packed_op),
 	D("aligned",		&aligned_op),
+	D("__cleanup__",	&cleanup_op),
 	D("nocast",		&attr_mod_op,		.mods = MOD_NOCAST),
 	D("noderef",		&attr_mod_op,		.mods = MOD_NODEREF),
 	D("safe",		&attr_mod_op,		.mods = MOD_SAFE),
@@ -1114,6 +1120,18 @@  static struct token *attribute_aligned(struct token *token, struct symbol *attr,
 	return token;
 }
 
+static struct token *attribute_cleanup(struct token *token, struct symbol *attr, struct decl_state *ctx)
+{
+	struct expression *expr = NULL;
+
+	if (match_op(token, '(')) {
+		token = parens_expression(token, &expr, "in attribute");
+		if (expr && expr->type == EXPR_SYMBOL)
+			ctx->cleanup = expr;
+	}
+	return token;
+}
+
 static void apply_mod(struct position *pos, unsigned long *mods, unsigned long mod)
 {
 	if (*mods & mod & ~MOD_DUP_OK)
@@ -1899,6 +1917,7 @@  static struct token *declaration_list(struct token *token, struct symbol_list **
 	saved = ctx.ctype;
 	for (;;) {
 		struct symbol *decl = alloc_symbol(token->pos, SYM_NODE);
+		ctx.cleanup = NULL;
 		ctx.ident = &decl->ident;
 
 		token = declarator(token, &ctx);
@@ -1910,6 +1929,7 @@  static struct token *declaration_list(struct token *token, struct symbol_list **
 
 		decl->ctype = ctx.ctype;
 		decl->ctype.modifiers |= mod;
+		decl->cleanup = ctx.cleanup;
 		decl->endpos = token->pos;
 		add_symbol(list, decl);
 		if (!match_op(token, ','))
@@ -1964,6 +1984,7 @@  struct token *typename(struct token *token, struct symbol **p, int *forced)
 	token = declarator(token, &ctx);
 	apply_modifiers(token->pos, &ctx);
 	sym->ctype = ctx.ctype;
+	sym->cleanup = ctx.cleanup;
 	sym->endpos = token->pos;
 	class = ctx.storage_class;
 	if (forced)
@@ -2924,6 +2945,7 @@  struct token *external_declaration(struct token *token, struct symbol_list **lis
 
 	decl->ctype = ctx.ctype;
 	decl->ctype.modifiers |= mod;
+	decl->cleanup = ctx.cleanup;
 	decl->endpos = token->pos;
 
 	/* Just a type declaration? */
@@ -3041,6 +3063,7 @@  struct token *external_declaration(struct token *token, struct symbol_list **lis
 		ident = NULL;
 		decl = alloc_symbol(token->pos, SYM_NODE);
 		ctx.ctype = saved;
+		ctx.cleanup = NULL;
 		token = handle_attributes(token, &ctx);
 		token = declarator(token, &ctx);
 		token = handle_asm_name(token, &ctx);
@@ -3048,6 +3071,7 @@  struct token *external_declaration(struct token *token, struct symbol_list **lis
 		apply_modifiers(token->pos, &ctx);
 		decl->ctype = ctx.ctype;
 		decl->ctype.modifiers |= mod;
+		decl->cleanup = ctx.cleanup;
 		decl->endpos = token->pos;
 		if (!ident) {
 			sparse_error(token->pos, "expected identifier name in type definition");
diff --git a/symbol.h b/symbol.h
index 5270fcd73a10..88130c15d4bd 100644
--- a/symbol.h
+++ b/symbol.h
@@ -107,6 +107,7 @@  struct decl_state {
 	struct ctype ctype;
 	struct ident **ident;
 	struct symbol_op *mode;
+	struct expression *cleanup;
 	unsigned long f_modifiers;		// function attributes
 	unsigned long storage_class;
 	unsigned char prefer_abstract;
@@ -204,6 +205,7 @@  struct symbol {
 			struct statement *inline_stmt;
 			struct symbol_list *inline_symbol_list;
 			struct expression *initializer;
+			struct expression *cleanup;
 			struct entrypoint *ep;
 			struct symbol *definition;
 		};