From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CDF2535C6A8; Fri, 9 Oct 2026 14:24:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1791555886; cv=none; b=XoH+hGGH/G5BCDzHpUh08AmKmaoeE+cIchvHlXP9QzO+K7JDAtQzvLt61NlnQ3uu2V+d0XKxbIusYJ0REC93SaVMV/SxmoGkeYI0+zrIloWdsGPPyHMuGQpr+m0UJfTsOxvPuxiov+Q2lFmKLYZ4p2kNPCTr5Jhox8vQcj0IwEs= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1791555886; c=relaxed/simple; bh=E9R6iSxhowzFEbr42aQbW2BTS1wYnH1VGSCI8dlpzVE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=mpyTWynDg7ioW1H8e11uqWLgG6+MGTBjYyLgWMzepOX0BVQvGueSvWX7BJ5CGBP7Fv5R6pgffduH3fbFfdeQhpOiOwIYtk7+CeRL5GEvnjEWpEeAIRxspHpqs8Kw0GwzD7M6hQMO/7M5s8gk3WNuIU8rgq2wOLfT2P0nSk1J+Qg= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linuxfoundation.org header.i=@linuxfoundation.org header.b=mjYWHuEy; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linuxfoundation.org header.i=@linuxfoundation.org header.b="mjYWHuEy" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 3142D1F000FF; Fri, 9 Oct 2026 14:24:45 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linuxfoundation.org; s=korg; t=1791555885; bh=ME9VyHxOGBlzybNWtActmgOsy97FPQexj+UlI0uyH5A=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=mjYWHuEypbGda8UrLYmdip6FiQJkLKDdODv+djf/wmF+1OQU2+ZaeNxN90tuYmuno Wrt7embUxTll3RPcYnXIHp7VU53BkOOaTNkiuX8HNOrm/Ir+lM3hGMHovNw1b0KMD0 ACMGcZo3VbmtJ098xHCREl+MyOccTgQyXUx35ZtY= From: Greg Kroah-Hartman To: stable@vger.kernel.org Cc: Greg Kroah-Hartman , patches@lists.linux.dev, Sashiko AI , Aamir Ahmed , Alexandre Belloni , Sasha Levin Subject: [PATCH 7.2 085/428] rtc: ac100: Fix clock provider use-after-free on probe failure Date: Fri, 9 Oct 2026 16:07:02 +0200 Message-ID: <20261009140948.047436912@linuxfoundation.org> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261009140946.008206689@linuxfoundation.org> References: <20261009140946.008206689@linuxfoundation.org> User-Agent: quilt/0.69 X-stable: review X-Patchwork-Hint: ignore Precedence: bulk X-Mailing-List: patches@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit 7.2-stable review patch. If anyone has any objections, please let me know. ------------------ From: Aamir Ahmed [ Upstream commit fcf0b58e976e83adf246b014518e49c662b96f5e ] ac100_rtc_register_clks() registers the RTC-32k clock with clk_hw_register_fixed_rate() and the clock provider with of_clk_add_hw_provider(). Neither is device managed, and both are released only from ac100_rtc_remove(). The driver core does not call remove() when probe() fails: really_probe() reaches probe_failed below the device_remove() call and goes straight to releasing the device's managed resources. ac100_rtc_probe() ends with return devm_rtc_register_device(chip->rtc); so if that fails after the clocks have been registered, the provider is left on the global of_clk_providers list holding chip->clk_data, which was allocated with devm_kzalloc() and is freed while probe() unwinds. A later lookup on this device tree node then reads freed memory in of_clk_hw_onecell_get(). Consumers that do exactly that exist in tree: the wifi power sequence nodes on sun8i-a83t-bananapi-m3 and sun8i-a83t-cubietruck-plus take <&ac100_rtc 1>, and the sun9i-a80 boards route osc32k through <&ac100_rtc 0>. The window is narrow, as devm_rtc_register_device() can only fail with -ENOMEM here, but the provider is left dangling whenever it does. The RTC-32k clock is leaked on the same path. Since clk_core_lookup() searches a global list that is not scoped per device, __clk_register() rejects the duplicate name with -EEXIST, so the leak also makes a later probe of the same device fail. The error path that returns -EINVAL when the ADDA 4M parent clock cannot be found leaks the RTC-32k clock in the same way. No provider has been registered at that point, so that one is a leak rather than a use-after-free. Register both with devm_clk_hw_register_fixed_rate() and devm_of_clk_add_hw_provider() so that they are released whenever the device goes away, on a failed probe as well as on unbind. Devres releases in reverse order of acquisition, so the provider is removed before chip->clk_data is freed, and the clkout children are now unregistered before their parent rather than after it. ac100_rtc_unregister_clks() and the remove callback then have nothing left to do and are removed. Fixes: d00a18a42c14 ("rtc: ac100: Add RTC driver for X-Powers AC100") Reported-by: Sashiko AI Closes: https://lore.kernel.org/linux-rtc/20260905184936.155E11F00A3A@smtp.kernel.org/ Assisted-by: LLM Signed-off-by: Aamir Ahmed Link: https://patch.msgid.link/AS8P251MB0001A489CC45A591ABBB9BDCC8B42@AS8P251MB0001.EURP251.PROD.OUTLOOK.COM Signed-off-by: Alexandre Belloni Signed-off-by: Sasha Levin --- drivers/rtc/rtc-ac100.c | 36 +++++++----------------------------- 1 file changed, 7 insertions(+), 29 deletions(-) diff --git a/drivers/rtc/rtc-ac100.c b/drivers/rtc/rtc-ac100.c index a2f465438fd05..c579c4bf5578d 100644 --- a/drivers/rtc/rtc-ac100.c +++ b/drivers/rtc/rtc-ac100.c @@ -319,10 +319,10 @@ static int ac100_rtc_register_clks(struct ac100_rtc_dev *chip) chip->clk_data->num = AC100_CLKOUT_NUM; - chip->rtc_32k_clk = clk_hw_register_fixed_rate(chip->dev, - AC100_RTC_32K_NAME, - NULL, 0, - AC100_RTC_32K_RATE); + chip->rtc_32k_clk = devm_clk_hw_register_fixed_rate(chip->dev, + AC100_RTC_32K_NAME, + NULL, 0, + AC100_RTC_32K_RATE); if (IS_ERR(chip->rtc_32k_clk)) { ret = PTR_ERR(chip->rtc_32k_clk); dev_err(chip->dev, "Failed to register RTC-32k clock: %d\n", @@ -356,28 +356,14 @@ static int ac100_rtc_register_clks(struct ac100_rtc_dev *chip) if (ret) { dev_err(chip->dev, "Failed to register clk '%s': %d\n", init.name, ret); - goto err_unregister_rtc_32k; + return ret; } chip->clk_data->hws[i] = &clk->hw; } - ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, chip->clk_data); - if (ret) - goto err_unregister_rtc_32k; - - return 0; - -err_unregister_rtc_32k: - clk_unregister_fixed_rate(chip->rtc_32k_clk->clk); - - return ret; -} - -static void ac100_rtc_unregister_clks(struct ac100_rtc_dev *chip) -{ - of_clk_del_provider(chip->dev->of_node); - clk_unregister_fixed_rate(chip->rtc_32k_clk->clk); + return devm_of_clk_add_hw_provider(chip->dev, of_clk_hw_onecell_get, + chip->clk_data); } /* @@ -615,13 +601,6 @@ static int ac100_rtc_probe(struct platform_device *pdev) return devm_rtc_register_device(chip->rtc); } -static void ac100_rtc_remove(struct platform_device *pdev) -{ - struct ac100_rtc_dev *chip = platform_get_drvdata(pdev); - - ac100_rtc_unregister_clks(chip); -} - static const struct of_device_id ac100_rtc_match[] = { { .compatible = "x-powers,ac100-rtc" }, { }, @@ -630,7 +609,6 @@ MODULE_DEVICE_TABLE(of, ac100_rtc_match); static struct platform_driver ac100_rtc_driver = { .probe = ac100_rtc_probe, - .remove = ac100_rtc_remove, .driver = { .name = "ac100-rtc", .of_match_table = of_match_ptr(ac100_rtc_match), -- 2.53.0