cocci.inria.fr archive mirror
 help / color / mirror / Atom feed
* [cocci] Finding functions that do not unlock mutex in the same function
@ 2023-10-19 22:00 Martin Schröder
  2023-10-20  7:58 ` Julia Lawall
  0 siblings, 1 reply; 12+ messages in thread
From: Martin Schröder @ 2023-10-19 22:00 UTC (permalink / raw)
  To: cocci

[-- Attachment #1: Type: text/plain, Size: 2231 bytes --]

How can I adjust the following code to find all cases where a function
locks a mutex but does not call unlock in the same function:

```
virtual report
virtual patch

@ r exists @
identifier f;
expression l;
expression r, e;
position p;
@@
f@p(...){
... when != k_mutex_lock(...)
r = k_mutex_lock(l, ...)
... when != e = r
if(e){
... when != k_mutex_unlock(l)
    when any
    when strict
when exists
}
(
return ...;
)
}

@ script:python depends on r@
p << r.p;
f << r.f;
@@

// This Python script runs if a locked mutex is not unlocked in all code
paths.
msg = "In function '%s', there's a path without unlocking the mutex. Ensure
that the mutex is always unlocked before returning or exiting the
function." % f
coccilib.report.print_report(p[0], msg)
```
It catches some cases but I'm still not able to make it catch everything
but the first error checking if statement:

```
int invalid_a(int a1, int a2){
int r = k_mutex_lock(mutex, K_MSEC(200));
if(0 != r){
return 0;
}
if(1){
return 1;
}
return 0;
}

int invalid_b(int a1, int a2){
int r = k_mutex_lock(mutex, K_MSEC(200));
if(0 != r){
return 0;
}
return 0;
}


int valid_b(int a1, int a2){
int r = k_mutex_lock(mutex, K_MSEC(200));
if(0 != r){
return 0;
}
k_mutex_unlock(&mutex);
return 0;
}

int invalid_c(int a1, int a2){
int r = k_mutex_lock(mutex, K_MSEC(200));
int b = foo;
if(0 != r){
return 0;
}
if(0 != b){
return 0;
}
k_mutex_unlock(&mutex);
return 0;
}
```

Result:
tests/coccinelle/mutex_lock_unlock.c:1:4-13: In function 'invalid_a',
there's a path without unlocking the mutex. Ensure that the mutex is always
unlocked before returning or exiting the function.
tests/coccinelle/mutex_lock_unlock.c:12:4-13: In function 'invalid_b',
there's a path without unlocking the mutex. Ensure that the mutex is always
unlocked before returning or exiting the function.

Invalid_c is not detected.

-- 
Best regards,

Martin Schröder
*Embedded Firmware Consultant*
Connect on LinkedIn <https://www.linkedin.com/in/martinschroder/>
*Book online meeting <https://calendly.com/martinschroder/call>*

Swedish Embedded Consulting Group AB
W. https://swedishembedded.com

[-- Attachment #2: Type: text/html, Size: 3732 bytes --]

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

* Re: [cocci] Finding functions that do not unlock mutex in the same function
  2023-10-19 22:00 [cocci] Finding functions that do not unlock mutex in the same function Martin Schröder
@ 2023-10-20  7:58 ` Julia Lawall
  2023-10-20 15:00   ` Markus Elfring
  0 siblings, 1 reply; 12+ messages in thread
From: Julia Lawall @ 2023-10-20  7:58 UTC (permalink / raw)
  To: Martin Schröder; +Cc: cocci

[-- Attachment #1: Type: text/plain, Size: 2589 bytes --]



On Fri, 20 Oct 2023, Martin Schröder wrote:

> How can I adjust the following code to find all cases where a function locks a mutex but does not call unlock in the same function:
>
> ```
> virtual report
> virtual patch
>
> @ r exists @
> identifier f;
> expression l;
> expression r, e;
> position p;
> @@
> f@p(...){
> ... when != k_mutex_lock(...)

Drop the above two lines.

> r = k_mutex_lock(l, ...)
> ... when != e = r
> if(e){
> ... when != k_mutex_unlock(l)
>     when any
>     when strict
>     when exists

when exists is not needed here, since you have that already.

> }

I think you could just put

... when any
    when !=  k_mutex_unlock(l)

and drop the rest.

> (
> return ...;
> )
> }

Does that work the way you wanted?

julia


>
> @ script:python depends on r@
> p << r.p;
> f << r.f;
> @@
>
> // This Python script runs if a locked mutex is not unlocked in all code paths.
> msg = "In function '%s', there's a path without unlocking the mutex. Ensure that the mutex is always unlocked before returning or exiting the function." % f
> coccilib.report.print_report(p[0], msg)
> ```
> It catches some cases but I'm still not able to make it catch everything but the first error checking if statement:
>
> ```
> int invalid_a(int a1, int a2){
> int r = k_mutex_lock(mutex, K_MSEC(200));
> if(0 != r){
> return 0;
> }
> if(1){
> return 1;
> }
> return 0;
> }
>
> int invalid_b(int a1, int a2){
> int r = k_mutex_lock(mutex, K_MSEC(200));
> if(0 != r){
> return 0;
> }
> return 0;
> }
>
>
> int valid_b(int a1, int a2){
> int r = k_mutex_lock(mutex, K_MSEC(200));
> if(0 != r){
> return 0;
> }
> k_mutex_unlock(&mutex);
> return 0;
> }
>
> int invalid_c(int a1, int a2){
> int r = k_mutex_lock(mutex, K_MSEC(200));
> int b = foo;
> if(0 != r){
> return 0;
> }
> if(0 != b){
> return 0;
> }
> k_mutex_unlock(&mutex);
> return 0;
> }
> ```
>
> Result:
> tests/coccinelle/mutex_lock_unlock.c:1:4-13: In function 'invalid_a', there's a path without unlocking the mutex. Ensure that the mutex is always unlocked before returning or exiting the function.
> tests/coccinelle/mutex_lock_unlock.c:12:4-13: In function 'invalid_b', there's a path without unlocking the mutex. Ensure that the mutex is always unlocked before returning or exiting the function.
>
> Invalid_c is not detected.
>
> --
> Best regards,
>
> [AIorK4wH6aPZe4eYWheYhio282OdbmPN-eMyvcew6PbeQLd1K_qGuSNTnBuq0VT6bSD79PDVrzgSr5c]
> Martin Schröder
> Embedded Firmware Consultant
> Connect on LinkedIn
> Book online meeting
>
> Swedish Embedded Consulting Group AB
> W. https://swedishembedded.com
>
>
>
>

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

* Re: [cocci] Finding functions that do not unlock mutex in the same function
  2023-10-20  7:58 ` Julia Lawall
@ 2023-10-20 15:00   ` Markus Elfring
  2023-10-21 21:50     ` Martin Schröder
  0 siblings, 1 reply; 12+ messages in thread
From: Markus Elfring @ 2023-10-20 15:00 UTC (permalink / raw)
  To: Julia Lawall, Martin Schröder, cocci

> I think you could just put
>
> ... when any
>     when !=  k_mutex_unlock(l)
>
> and drop the rest.

How do you think about to check the desired consistency of lock scopes
in more detail?

Will any software extensions become helpful for further source code analyses?

Regards,
Markus

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

* Re: [cocci] Finding functions that do not unlock mutex in the same function
  2023-10-20 15:00   ` Markus Elfring
@ 2023-10-21 21:50     ` Martin Schröder
  2023-10-22  8:44       ` Markus Elfring
  2023-10-24  5:52       ` Julia Lawall
  0 siblings, 2 replies; 12+ messages in thread
From: Martin Schröder @ 2023-10-21 21:50 UTC (permalink / raw)
  To: Markus Elfring; +Cc: Julia Lawall, cocci

[-- Attachment #1: Type: text/plain, Size: 1772 bytes --]

Thanks Julia.

My final script that seems to work looks like this:

virtual report
virtual patch

@ r exists @
expression l;
identifier r, e;
position p;
@@
r = k_mutex_lock(l, ...)
... when any
if(r){ ... }
... when != e = r
when any
if(e){
... when any
when != k_mutex_unlock(l)
return@p ...;
}

First I match k_mutex_lock. Then there can be any statements between that
and the first check.

If locking fails then in the first check it is valid to return. Not sure
how to capture that this first if statement must return in that case.

Then there can follow additional if statements and in all of them there
must be an unlock if there is also a return.

The sample code looks like this:

struct k_mutex lock;

int function(){

int r = k_mutex_lock(&lock, K_FOREVER);
if(r){

return -ETIMEDOUT; // valid to not unlock here

}
r = produce_something();
if(r){

k_mutex_unlock(&lock);

return -EIO;

}
r = produce_something();
if(r){

return -EINVAL; // here an unlock is missing

}
k_mutex_unlock(&lock);
return 0;

}

On Fri, 20 Oct 2023 at 17:01, Markus Elfring <Markus.Elfring@web.de> wrote:

> > I think you could just put
> >
> > ... when any
> >     when !=  k_mutex_unlock(l)
> >
> > and drop the rest.
>
> How do you think about to check the desired consistency of lock scopes
> in more detail?
>
> Will any software extensions become helpful for further source code
> analyses?
>
> Regards,
> Markus
>


-- 
Best regards,

Martin Schröder
*Embedded Firmware Consultant*
Connect on LinkedIn <https://www.linkedin.com/in/martinschroder/>
*Book online meeting <https://calendly.com/martinschroder/call>*

Swedish Embedded Consulting Group AB
W. https://swedishembedded.com

[-- Attachment #2: Type: text/html, Size: 3763 bytes --]

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

* Re: [cocci] Finding functions that do not unlock mutex in the same function
  2023-10-21 21:50     ` Martin Schröder
@ 2023-10-22  8:44       ` Markus Elfring
  2023-10-22 15:26         ` Martin Schröder
  2023-10-24  5:52       ` Julia Lawall
  1 sibling, 1 reply; 12+ messages in thread
From: Markus Elfring @ 2023-10-22  8:44 UTC (permalink / raw)
  To: Martin Schröder, Julia Lawall, cocci

> My final script that seems to work looks like this:
>
> virtual report
> virtual patch
>
> @ r exists @
> expression l;
> identifier r, e;
> position p;
> @@
> r = k_mutex_lock(l, ...)
> ... when any
> if(r){ ... }
> ... when != e = r
> when any
> if(e){
> ... when any
> when != k_mutex_unlock(l)
> return@p ...;
> }
>
> First I match k_mutex_lock. Then there can be any statements between that and the first check.

I suggest to take further case distinctions better into account
also for such a source code analysis approach.

I imagine that you would like to restrict the code which may be used
between a function call and a corresponding return value check.
Should the (compound) statement be restricted also for the corresponding if branch?


> If locking fails then in the first check it is valid to return.

Such a control flow possibility is reasonable.


> Not sure how to capture that this first if statement must return in that case.

How do you think about to add any SmPL disjunctions and to adjust the usage
of SmPL ellipses?


> Then there can follow additional if statements and in all of them there must be an unlock if there is also a return.

* Why do you try to exclude the assignment of the item “r” to other items?

* Would you occasionally like to support (lock) scopes which would be distributed over
  multiple function implementations?

Regards,
Markus

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

* Re: [cocci] Finding functions that do not unlock mutex in the same function
  2023-10-22  8:44       ` Markus Elfring
@ 2023-10-22 15:26         ` Martin Schröder
  2023-10-23  6:55           ` Markus Elfring
  2023-10-23 16:09           ` Markus Elfring
  0 siblings, 2 replies; 12+ messages in thread
From: Martin Schröder @ 2023-10-22 15:26 UTC (permalink / raw)
  To: Markus Elfring; +Cc: Julia Lawall, cocci

[-- Attachment #1: Type: text/plain, Size: 2575 bytes --]

I explicitly want to disallow lock scopes that are distributed over
multiple functions. Instead, semaphore should be used in such cases. I want
to create a rule that ensures that 1. proper unlocking takes place across
all exit paths and 2. that both locking and unlocking happen in the same
function.

Markus, how would you recommend using disjunctions?

Here is my slightly updated rule:

@ r exists @
expression l;
identifier r, e;
position p;
@@
r = k_mutex_lock(l, ...)
... when any
if(r){ ... }
... when != e = r
when any
if(e){
... when any
when != k_mutex_unlock(l)
return@p ...;
}
... when != k_mutex_unlock(l)
k_mutex_unlock(l);



On Sun, 22 Oct 2023 at 10:44, Markus Elfring <Markus.Elfring@web.de> wrote:

> > My final script that seems to work looks like this:
> >
> > virtual report
> > virtual patch
> >
> > @ r exists @
> > expression l;
> > identifier r, e;
> > position p;
> > @@
> > r = k_mutex_lock(l, ...)
> > ... when any
> > if(r){ ... }
> > ... when != e = r
> > when any
> > if(e){
> > ... when any
> > when != k_mutex_unlock(l)
> > return@p ...;
> > }
> >
> > First I match k_mutex_lock. Then there can be any statements between
> that and the first check.
>
> I suggest to take further case distinctions better into account
> also for such a source code analysis approach.
>
> I imagine that you would like to restrict the code which may be used
> between a function call and a corresponding return value check.
> Should the (compound) statement be restricted also for the corresponding
> if branch?
>
>
> > If locking fails then in the first check it is valid to return.
>
> Such a control flow possibility is reasonable.
>
>
> > Not sure how to capture that this first if statement must return in that
> case.
>
> How do you think about to add any SmPL disjunctions and to adjust the usage
> of SmPL ellipses?
>
>
> > Then there can follow additional if statements and in all of them there
> must be an unlock if there is also a return.
>
> * Why do you try to exclude the assignment of the item “r” to other items?
>
> * Would you occasionally like to support (lock) scopes which would be
> distributed over
>   multiple function implementations?
>
> Regards,
> Markus
>


-- 
Best regards,

Martin Schröder
*Embedded Firmware Consultant*
Connect on LinkedIn <https://www.linkedin.com/in/martinschroder/>
*Book online meeting <https://calendly.com/martinschroder/call>*

Swedish Embedded Consulting Group AB
W. https://swedishembedded.com

[-- Attachment #2: Type: text/html, Size: 4250 bytes --]

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

* Re: [cocci] Finding functions that do not unlock mutex in the same function
  2023-10-22 15:26         ` Martin Schröder
@ 2023-10-23  6:55           ` Markus Elfring
  2023-10-23 16:09           ` Markus Elfring
  1 sibling, 0 replies; 12+ messages in thread
From: Markus Elfring @ 2023-10-23  6:55 UTC (permalink / raw)
  To: Martin Schröder, Julia Lawall, cocci

> I explicitly want to disallow lock scopes that are distributed over multiple functions.
> Instead, semaphore should be used in such cases.

Have you got any special preferences for locking primitives?


Did you get the impression that the proper handling of various scopes triggers
further development challenges also for the safe application of the semantic patch language
(Coccinelle software)?
https://en.wikipedia.org/wiki/Scope_(computer_science)


> Markus, how would you recommend using disjunctions? 

Please take another look at known information sources.
Do any SmPL script examples indicate how desirable case distinctions can be achieved with them?


> Here is my slightly updated rule:

I would appreciate further answers for presented questions.

Regards,
Markus

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

* Re: [cocci] Finding functions that do not unlock mutex in the same function
  2023-10-22 15:26         ` Martin Schröder
  2023-10-23  6:55           ` Markus Elfring
@ 2023-10-23 16:09           ` Markus Elfring
  1 sibling, 0 replies; 12+ messages in thread
From: Markus Elfring @ 2023-10-23 16:09 UTC (permalink / raw)
  To: Martin Schröder; +Cc: Julia Lawall, cocci

> I explicitly want to disallow lock scopes that are distributed over multiple functions.

It seems that you are trying to restrict the desired control flow analysis
to intra-procedural means.


> Here is my slightly updated rule:

You would like to achieve something with a single SmPL rule so far.
I suggest to take additional software design options into account
for possible solution approaches.
How do you think about to work with more data processing steps?

The source code positions can be determined for lock and unlock function calls.

* Would you like to perform any advanced data processing by the means
  of supported programming languages?
  + OCaml
  + Python

* Can a corresponding report show how consistent the expected lock scopes would be?

Regards,
Markus

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

* Re: [cocci] Finding functions that do not unlock mutex in the same function
  2023-10-21 21:50     ` Martin Schröder
  2023-10-22  8:44       ` Markus Elfring
@ 2023-10-24  5:52       ` Julia Lawall
  2023-10-25  6:08         ` [cocci] Dependencies between key words “goto” and “return”? Markus Elfring
  1 sibling, 1 reply; 12+ messages in thread
From: Julia Lawall @ 2023-10-24  5:52 UTC (permalink / raw)
  To: Martin Schröder; +Cc: Markus Elfring, cocci

[-- Attachment #1: Type: text/plain, Size: 2230 bytes --]



On Sat, 21 Oct 2023, Martin Schröder wrote:

> Thanks Julia.
> My final script that seems to work looks like this:
>
> virtual report
> virtual patch
>
> @ r exists @
> expression l;
> identifier r, e;
> position p;
> @@
> r = k_mutex_lock(l, ...)
> ... when any
> if(r){ ... }

Put a return ...; after the ... to require the branch to return.  This
will cover the case where there is a goto that leads to the return.

> ... when != e = r
> when any

I would consider it to be more readable to line up the when lines.

julia

> if(e){
> ... when any
> when != k_mutex_unlock(l)
> return@p ...;
> }
>
> First I match k_mutex_lock. Then there can be any statements between that and the first check.
>
> If locking fails then in the first check it is valid to return. Not sure how to capture that this first if statement must return in that case. 
>
> Then there can follow additional if statements and in all of them there must be an unlock if there is also a return.
>
> The sample code looks like this:
>
> struct k_mutex lock;
>
> int function(){
>       int r = k_mutex_lock(&lock, K_FOREVER);
>       if(r){
>             return -ETIMEDOUT; // valid to not unlock here
>
>       }
>       r = produce_something();
>       if(r){
>             k_mutex_unlock(&lock);
>
>             return -EIO;
>
>       }
>       r = produce_something();
>       if(r){
>             return -EINVAL; // here an unlock is missing
>
>       }
>       k_mutex_unlock(&lock);
>       return 0;
>
> }
>
> On Fri, 20 Oct 2023 at 17:01, Markus Elfring <Markus.Elfring@web.de> wrote:
>       > I think you could just put
>       >
>       > ... when any
>       >     when !=  k_mutex_unlock(l)
>       >
>       > and drop the rest.
>
>       How do you think about to check the desired consistency of lock scopes
>       in more detail?
>
>       Will any software extensions become helpful for further source code analyses?
>
>       Regards,
>       Markus
>
>
>
> --
> Best regards,
>
> [AIorK4wH6aPZe4eYWheYhio282OdbmPN-eMyvcew6PbeQLd1K_qGuSNTnBuq0VT6bSD79PDVrzgSr5c]
> Martin Schröder
> Embedded Firmware Consultant
> Connect on LinkedIn
> Book online meeting
>
> Swedish Embedded Consulting Group AB
> W. https://swedishembedded.com
>
>
>
>

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

* Re: [cocci] Dependencies between key words “goto” and “return”?
  2023-10-24  5:52       ` Julia Lawall
@ 2023-10-25  6:08         ` Markus Elfring
  2023-10-25  6:31           ` Julia Lawall
  0 siblings, 1 reply; 12+ messages in thread
From: Markus Elfring @ 2023-10-25  6:08 UTC (permalink / raw)
  To: Julia Lawall, cocci; +Cc: Martin Schröder

>> if(r){ ... }
>
> Put a return ...; after the ... to require the branch to return.

Can any more restrictions become relevant for such SmPL code?


> This will cover the case where there is a goto that leads to the return.

Would you like to explain this information a bit more?

Regards,
Markus

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

* Re: [cocci] Dependencies between key words “goto” and “return”?
  2023-10-25  6:08         ` [cocci] Dependencies between key words “goto” and “return”? Markus Elfring
@ 2023-10-25  6:31           ` Julia Lawall
  2023-10-25  6:45             ` Markus Elfring
  0 siblings, 1 reply; 12+ messages in thread
From: Julia Lawall @ 2023-10-25  6:31 UTC (permalink / raw)
  To: Markus Elfring; +Cc: cocci, Martin Schröder



On Wed, 25 Oct 2023, Markus Elfring wrote:

> >> if(r){ ... }
> >
> > Put a return ...; after the ... to require the branch to return.
>
> Can any more restrictions become relevant for such SmPL code?
>
>
> > This will cover the case where there is a goto that leads to the return.
>
> Would you like to explain this information a bit more?

The ... follows the control-flow patch over the goto until it reaches a
return.

julia

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

* Re: [cocci] Dependencies between key words “goto” and “return”?
  2023-10-25  6:31           ` Julia Lawall
@ 2023-10-25  6:45             ` Markus Elfring
  0 siblings, 0 replies; 12+ messages in thread
From: Markus Elfring @ 2023-10-25  6:45 UTC (permalink / raw)
  To: Julia Lawall, cocci; +Cc: Martin Schröder

>>>> if(r){ ... }
>>> This will cover the case where there is a goto that leads to the return.
>>
>> Would you like to explain this information a bit more?
>
> The ... follows the control-flow patch over the goto until it reaches a return.

* Would you like to refer to “code paths” here?

* Goto statements can occasionally jump to source code places
  that would be connected to other items than a return statement.

Regards,
Markus

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

end of thread, other threads:[~2023-10-25  6:45 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-10-19 22:00 [cocci] Finding functions that do not unlock mutex in the same function Martin Schröder
2023-10-20  7:58 ` Julia Lawall
2023-10-20 15:00   ` Markus Elfring
2023-10-21 21:50     ` Martin Schröder
2023-10-22  8:44       ` Markus Elfring
2023-10-22 15:26         ` Martin Schröder
2023-10-23  6:55           ` Markus Elfring
2023-10-23 16:09           ` Markus Elfring
2023-10-24  5:52       ` Julia Lawall
2023-10-25  6:08         ` [cocci] Dependencies between key words “goto” and “return”? Markus Elfring
2023-10-25  6:31           ` Julia Lawall
2023-10-25  6:45             ` Markus Elfring

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).