mirror of
https://github.com/torvalds/linux.git
synced 2026-05-30 18:13:41 +02:00
mm/damon/core: remove call_control in inactive contexts
If damon_call() is executed against a DAMON context that is not running,
the function returns error while keeping the damon_call_control object
linked to the context's call_controls list. Let's suppose the object is
deallocated after the damon_call(), and yet another damon_call() is
executed against the same context. The function tries to add the new
damon_call_control object to the call_controls list, which still has the
pointer to the previous damon_call_control object, which is deallocated.
As a result, use-after-free happens.
This can actually be triggered using the DAMON sysfs interface. It is not
easily exploitable since it requires the sysfs write permission and making
a definitely weird file writes, though. Please refer to the report for
more details about the issue reproduction steps.
Fix the issue by making two changes. Firstly, move the final
kdamond_call() for cancelling all existing damon_call() requests from
terminating DAMON context to be done before the ctx->kdamond reset. This
makes any code that sees NULL ctx->kdamond can safely assume the context
may not access damon_call() requests anymore. Secondly, let damon_call()
to cleanup the damon_call_control objects that were added to the
already-terminated DAMON context, before returning the error.
Link: https://lkml.kernel.org/r/20251231012315.75835-1-sj@kernel.org
Fixes: 004ded6bee ("mm/damon: accept parallel damon_call() requests")
Signed-off-by: SeongJae Park <sj@kernel.org>
Reported-by: JaeJoon Jung <rgbi3307@gmail.com>
Closes: https://lore.kernel.org/20251224094401.20384-1-rgbi3307@gmail.com
Cc: <stable@vger.kernel.org> # 6.17.x
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
This commit is contained in:
parent
e561383a39
commit
f9132fbc2e
|
|
@ -1431,6 +1431,35 @@ bool damon_is_running(struct damon_ctx *ctx)
|
||||||
return running;
|
return running;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* damon_call_handle_inactive_ctx() - handle DAMON call request that added to
|
||||||
|
* an inactive context.
|
||||||
|
* @ctx: The inactive DAMON context.
|
||||||
|
* @control: Control variable of the call request.
|
||||||
|
*
|
||||||
|
* This function is called in a case that @control is added to @ctx but @ctx is
|
||||||
|
* not running (inactive). See if @ctx handled @control or not, and cleanup
|
||||||
|
* @control if it was not handled.
|
||||||
|
*
|
||||||
|
* Returns 0 if @control was handled by @ctx, negative error code otherwise.
|
||||||
|
*/
|
||||||
|
static int damon_call_handle_inactive_ctx(
|
||||||
|
struct damon_ctx *ctx, struct damon_call_control *control)
|
||||||
|
{
|
||||||
|
struct damon_call_control *c;
|
||||||
|
|
||||||
|
mutex_lock(&ctx->call_controls_lock);
|
||||||
|
list_for_each_entry(c, &ctx->call_controls, list) {
|
||||||
|
if (c == control) {
|
||||||
|
list_del(&control->list);
|
||||||
|
mutex_unlock(&ctx->call_controls_lock);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mutex_unlock(&ctx->call_controls_lock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* damon_call() - Invoke a given function on DAMON worker thread (kdamond).
|
* damon_call() - Invoke a given function on DAMON worker thread (kdamond).
|
||||||
* @ctx: DAMON context to call the function for.
|
* @ctx: DAMON context to call the function for.
|
||||||
|
|
@ -1461,7 +1490,7 @@ int damon_call(struct damon_ctx *ctx, struct damon_call_control *control)
|
||||||
list_add_tail(&control->list, &ctx->call_controls);
|
list_add_tail(&control->list, &ctx->call_controls);
|
||||||
mutex_unlock(&ctx->call_controls_lock);
|
mutex_unlock(&ctx->call_controls_lock);
|
||||||
if (!damon_is_running(ctx))
|
if (!damon_is_running(ctx))
|
||||||
return -EINVAL;
|
return damon_call_handle_inactive_ctx(ctx, control);
|
||||||
if (control->repeat)
|
if (control->repeat)
|
||||||
return 0;
|
return 0;
|
||||||
wait_for_completion(&control->completion);
|
wait_for_completion(&control->completion);
|
||||||
|
|
@ -2755,13 +2784,13 @@ static int kdamond_fn(void *data)
|
||||||
if (ctx->ops.cleanup)
|
if (ctx->ops.cleanup)
|
||||||
ctx->ops.cleanup(ctx);
|
ctx->ops.cleanup(ctx);
|
||||||
kfree(ctx->regions_score_histogram);
|
kfree(ctx->regions_score_histogram);
|
||||||
|
kdamond_call(ctx, true);
|
||||||
|
|
||||||
pr_debug("kdamond (%d) finishes\n", current->pid);
|
pr_debug("kdamond (%d) finishes\n", current->pid);
|
||||||
mutex_lock(&ctx->kdamond_lock);
|
mutex_lock(&ctx->kdamond_lock);
|
||||||
ctx->kdamond = NULL;
|
ctx->kdamond = NULL;
|
||||||
mutex_unlock(&ctx->kdamond_lock);
|
mutex_unlock(&ctx->kdamond_lock);
|
||||||
|
|
||||||
kdamond_call(ctx, true);
|
|
||||||
damos_walk_cancel(ctx);
|
damos_walk_cancel(ctx);
|
||||||
|
|
||||||
mutex_lock(&damon_lock);
|
mutex_lock(&damon_lock);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user