Re: [cocci] [RFC] Coccinelle: Use IS_ERR_OR_NULL

Julia Lawall <[email protected]> Wed, 4 Mar 2026 19:12:10 +0100 (CET)
Newsgroups fr.inria.cocci
Message-ID <[email protected]>

On Wed, 4 Mar 2026, Philipp Hahn wrote:

> Hello,
>
> after a colleague of mine recently gave a talk about Coccinelle, I had look
> myself and created the below semantic patch to find and convert uses of
> IS_ERR() plus NULL check to IS_ERR_OR_NULL() in the Linux kernel.
>
> On 7.0.0-rc2 the patch applies to 79 files and finds most places I identified
> manually. Fantastic!
>
> There are some cases where the checks are part of a larger expression:
> - mm/kmemleak.c:1095
> - mm/kmemleak.c:1155
> - mm/kmemleak.c:1173
> - mm/kmemleak.c:1290
> - mm/kmemleak.c:1328
> - mm/kmemleak.c:1241
> - mm/kmemleak.c:1310
> - mm/kmemleak.c:1258
> - net/mac80211/tx.c:4831
> - net/netlink/af_netlink.c:2670
> - security/keys/internal.h:367
> So far I've been unable to teach Coccinelle to also generate patches for
> these. Is there some easy solution to fix my patch to also handle those cases?

The problem is the associativity of && which is not what you would hope
for in these cases.  You can write:

@@
expression e,e1;
@@

- e1 && e && !IS_ERR(e)
+ e1 && !IS_ERR_OR_NULL(e)

It doesn't catch net/mac80211/tx.c:4831 and security/keys/internal.h:367
though, which are different from the others.

>
> There are several cases where `!ptr && WARN_ON[_ONCE](IS_ERR(ptr))` is
> used; they are not found and changed.
> - arch/x86/kernel/callthunks.c:215 WARN_ON_ONCE
> - drivers/clk/clk.c:4561 WARN_ON_ONCE
> - drivers/interconnect/core.c:793 WARN_ON
> - drivers/reset/core.c:718 WARN_ON
> I don't expect Cocinelle to create a patch there and would ask the maintainers,
> if they prefer the WARN_ON[_ONCE] to remain or would prefer changing it to
> IS_ERR_OR_NULL.

You can always make another rule that will do this.

> drivers/clocksource/mips-gic-timer.c:283 looks suspicious: ret != clk
> I'll ask the maintainer if this is on purpose or an error.
>
>
> How are new semantic patches normally handled:
> - are they first discussed on some mailing list?
> - should I then create the individual patches myself and send them to the
>   subsystem maintainers myself?
> - when should the semantic patch itself be included with the Linux kernel?

You can send semantic patches to whereever get_maintainer tells you to
send them.  It's nice to know when the semantic patch has led to patches
that have been accepted.

Thanks for trying Coccinelle.

julia

> Thank you for the feedback.
>
> Philipp Hahn
> ---
>  scripts/coccinelle/api/is_err_or_null.cocci | 86 +++++++++++++++++++++
>  1 file changed, 86 insertions(+)
>  create mode 100644 scripts/coccinelle/api/is_err_or_null.cocci
>
> diff --git a/scripts/coccinelle/api/is_err_or_null.cocci b/scripts/coccinelle/api/is_err_or_null.cocci
> new file mode 100644
> index 0000000000000..942ba95a206f9
> --- /dev/null
> +++ b/scripts/coccinelle/api/is_err_or_null.cocci
> @@ -0,0 +1,86 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +///
> +/// Use IF_ERR_OR_NULL() instead of IS_ERR() plus a check for (not) NULL
> +///
> +// Copyright: (C) 2026 Philipp Hahn, FRITZ! Technology GmbH.
> +// Confidence: Medium
> +// Options: --no-includes --include-headers
> +// Keywords: IS_ERR, IS_ERR_OR_NULL
> +
> +virtual patch
> +virtual report
> +virtual org
> +
> +@p1 depends on patch@
> +expression E;
> +@@
> +(
> +-	E != NULL && !IS_ERR(E)
> ++	!IS_ERR_OR_NULL(E)
> +|
> +-	E == NULL || IS_ERR(E)
> ++	!IS_ERR_OR_NULL(E)
> +|
> +-	!IS_ERR(E) && E != NULL
> ++	IS_ERR_OR_NULL(E)
> +|
> +-	IS_ERR(E) || E == NULL
> ++	IS_ERR_OR_NULL(E)
> +)
> +
> +@r1 depends on report || org@
> +expression E;
> +position p;
> +@@
> +(
> + 	E != NULL && ... && !IS_ERR@p(E)
> +|
> + 	E == NULL || ... || IS_ERR@p(E)
> +|
> + 	!IS_ERR@p(E) && ... && E != NULL
> +|
> + 	IS_ERR@p(E) || ... || E == NULL
> +)
> +
> +@script:python depends on report@
> +p << r1.p;
> +@@
> +coccilib.report.print_report(p[0], "opportunity for IS_ERR_OR_NULL()")
> +
> +@script:python depends on org@
> +p << r1.p;
> +@@
> +coccilib.org.print_todo(p[0], "opportunity for IS_ERR_OR_NULL()")
> +
> +@p2 depends on patch@
> +identifier I;
> +expression E;
> +@@
> +(
> +-	(I = E) != NULL && !IS_ERR(I)
> ++	!IS_ERR_OR_NULL((I = E))
> +|
> +-	(I = E) == NULL || IS_ERR(I)
> ++	IS_ERR_OR_NULL((I = E))
> +)
> +
> +@r2 depends on report || org@
> +identifier I;
> +expression E;
> +position p;
> +@@
> +(
> +*	(I = E) != NULL && ... && !IS_ERR@p(I)
> +|
> +*	(I = E) == NULL || ... || IS_ERR@p(I)
> +)
> +
> +@script:python depends on report@
> +p << r2.p;
> +@@
> +coccilib.report.print_report(p[0], "opportunity for IS_ERR_OR_NULL()")
> +
> +@script:python depends on org@
> +p << r2.p;
> +@@
> +coccilib.org.print_todo(p[0], "opportunity for IS_ERR_OR_NULL()")
> --
> 2.43.0
>
>