aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2022-06-30 09:34:10 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2022-08-22 15:13:56 -0700
commitd277dd67844973e8c81c25a7016b0cdc86d41766 (patch)
treee21c663f092a1d2fab786cb95f15c107ff38379c
parentce1a6720f69e6233ec9abd4e9aae5945e05fda41 (diff)
downloadsparse-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.c24
-rw-r--r--validation/context.c15
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();