Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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: 004ded6bee11 ("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>

authored by

SeongJae Park and committed by
Andrew Morton
f9132fbc e561383a

+31 -2
+31 -2
mm/damon/core.c
··· 1431 1431 return running; 1432 1432 } 1433 1433 1434 + /* 1435 + * damon_call_handle_inactive_ctx() - handle DAMON call request that added to 1436 + * an inactive context. 1437 + * @ctx: The inactive DAMON context. 1438 + * @control: Control variable of the call request. 1439 + * 1440 + * This function is called in a case that @control is added to @ctx but @ctx is 1441 + * not running (inactive). See if @ctx handled @control or not, and cleanup 1442 + * @control if it was not handled. 1443 + * 1444 + * Returns 0 if @control was handled by @ctx, negative error code otherwise. 1445 + */ 1446 + static int damon_call_handle_inactive_ctx( 1447 + struct damon_ctx *ctx, struct damon_call_control *control) 1448 + { 1449 + struct damon_call_control *c; 1450 + 1451 + mutex_lock(&ctx->call_controls_lock); 1452 + list_for_each_entry(c, &ctx->call_controls, list) { 1453 + if (c == control) { 1454 + list_del(&control->list); 1455 + mutex_unlock(&ctx->call_controls_lock); 1456 + return -EINVAL; 1457 + } 1458 + } 1459 + mutex_unlock(&ctx->call_controls_lock); 1460 + return 0; 1461 + } 1462 + 1434 1463 /** 1435 1464 * damon_call() - Invoke a given function on DAMON worker thread (kdamond). 1436 1465 * @ctx: DAMON context to call the function for. ··· 1490 1461 list_add_tail(&control->list, &ctx->call_controls); 1491 1462 mutex_unlock(&ctx->call_controls_lock); 1492 1463 if (!damon_is_running(ctx)) 1493 - return -EINVAL; 1464 + return damon_call_handle_inactive_ctx(ctx, control); 1494 1465 if (control->repeat) 1495 1466 return 0; 1496 1467 wait_for_completion(&control->completion); ··· 2784 2755 if (ctx->ops.cleanup) 2785 2756 ctx->ops.cleanup(ctx); 2786 2757 kfree(ctx->regions_score_histogram); 2758 + kdamond_call(ctx, true); 2787 2759 2788 2760 pr_debug("kdamond (%d) finishes\n", current->pid); 2789 2761 mutex_lock(&ctx->kdamond_lock); 2790 2762 ctx->kdamond = NULL; 2791 2763 mutex_unlock(&ctx->kdamond_lock); 2792 2764 2793 - kdamond_call(ctx, true); 2794 2765 damos_walk_cancel(ctx); 2795 2766 2796 2767 mutex_lock(&damon_lock);