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