diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2022-06-30 09:34:10 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2022-08-22 15:13:56 -0700 |
commit | d277dd67844973e8c81c25a7016b0cdc86d41766 (patch) | |
tree | e21c663f092a1d2fab786cb95f15c107ff38379c | |
parent | ce1a6720f69e6233ec9abd4e9aae5945e05fda41 (diff) | |
download | sparse-d277dd67844973e8c81c25a7016b0cdc86d41766.tar.gz |
add "conditional context increment" model
Use a negative output parameter to the "context" attribute to say that
the context is increased conditionally.
Useful for "trylock()" kinds of functions that return success if the
lock operation was successful.
This is what the kernel commit 4a557a5d1a61 ("sparse: introduce
conditional lock acquire function attribute") started doing for
refcount_dec_and_lock() and friends.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | linearize.c | 24 | ||||
-rw-r--r-- | validation/context.c | 15 |
2 files changed, 37 insertions, 2 deletions
diff --git a/linearize.c b/linearize.c index d9aed61b..8dd005af 100644 --- a/linearize.c +++ b/linearize.c @@ -1537,6 +1537,8 @@ static pseudo_t linearize_call_expression(struct entrypoint *ep, struct expressi add_one_insn(ep, insn); if (ctype) { + struct basic_block *post_call = NULL; + FOR_EACH_PTR(ctype->contexts, context) { int in = context->in; int out = context->out; @@ -1547,8 +1549,21 @@ static pseudo_t linearize_call_expression(struct entrypoint *ep, struct expressi in = 0; } if (out < 0) { - check = 0; - out = 0; + struct basic_block *set_context; + if (retval == VOID) { + warning(expr->pos, "nonsensical conditional output context with no condition"); + break; + } + if (check || in) { + warning(expr->pos, "nonsensical conditional input context"); + break; + } + if (!post_call) + post_call = alloc_basic_block(ep, expr->pos); + set_context = alloc_basic_block(ep, expr->pos); + add_branch(ep, retval, set_context, post_call); + set_activeblock(ep, set_context); + out = -out; } context_diff = out - in; if (check || context_diff) { @@ -1560,6 +1575,11 @@ static pseudo_t linearize_call_expression(struct entrypoint *ep, struct expressi } } END_FOR_EACH_PTR(context); + if (post_call) { + add_goto(ep, post_call); + set_activeblock(ep, post_call); + } + if (ctype->modifiers & MOD_NORETURN) add_unreachable(ep); } diff --git a/validation/context.c b/validation/context.c index b9500dc7..f8962f19 100644 --- a/validation/context.c +++ b/validation/context.c @@ -10,6 +10,10 @@ static void r(void) __attribute__((context(1,0))) __context__(-1); } +// Negative output means "conditional positive output" +extern int cond_get(void) __attribute((context(0,-1))); +extern void nonsensical_cond_get(void) __attribute((context(0,-1))); + extern int _ca(int fail); #define ca(fail) __cond_lock(_ca(fail)) @@ -19,6 +23,17 @@ static void good_paired1(void) r(); } +static void good_conditional(void) +{ + if (cond_get()) + r(); +} + +static void nonsensical_conditional(void) +{ + nonsensical_cond_get(); +} + static void good_paired2(void) { a(); |