Linux LED subsystem development
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Fenglin Wu" <fenglin.wu@oss.qualcomm.com>
Cc: linux-leds@vger.kernel.org, lee@kernel.org
Subject: Re: [PATCH v4] leds: rgb: leds-qcom-lpg: Fix LED color balancing in HW pattern mode
Date: Tue, 30 Jun 2026 03:34:15 +0000	[thread overview]
Message-ID: <20260630033416.4406E1F00A3A@smtp.kernel.org> (raw)
In-Reply-To: <20260629-lpg-rgb-color-balance-fix-v4-1-4db8592fb3c5@oss.qualcomm.com>

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Use-after-free and double-free of LED pattern data due to missing ownership transfer
- [Medium] Subsystem guidelines violation: mixing goto and scope-based cleanup
--

commit 902ea70efa572e9fe23362e3de394a74dc4bce83
Author: Fenglin Wu <fenglin.wu@oss.qualcomm.com>

    leds: rgb: leds-qcom-lpg: Fix LED color balancing in HW pattern mode
    
    Currently, when the LED is configured as a RGB LED or a multi-color
    LED device, the same pattern is programmed for all LED channels
    regardless of the sub-led intensities when triggered by HW pattern.
    It results that the LED device is always working in a white-balanced
    mode regardless of the intensity settings.
    
    To fix this, scale the pattern data according to the sub-led intensity
    and program the HW pattern separately for each LPG channel.

> diff --git a/drivers/leds/rgb/leds-qcom-lpg.c b/drivers/leds/rgb/leds-qcom-lpg.c
> index d7d6518de30f2..e407742ec3b31 100644
> --- a/drivers/leds/rgb/leds-qcom-lpg.c
> +++ b/drivers/leds/rgb/leds-qcom-lpg.c

[ ... ]

> @@ -995,15 +1005,16 @@ static int lpg_pattern_set(struct lpg_led *led, struct led_pattern *led_pattern,
>  	if (len % 2)
>  		return -EINVAL;
>  
> -	pattern = kzalloc_objs(*pattern, len / 2);
> +	struct led_pattern *pattern __free(kfree) = kzalloc_objs(*pattern, len / 2);
> +
>  	if (!pattern)
>  		return -ENOMEM;
>  
>  	for (i = 0; i < len; i += 2) {

[ ... ]

>  	if (lpg->lut_base || lpg->lut_sdam) {
> -		lo_pause = pattern[0].delta_t;
> -		hi_pause = pattern[actual_len - 1].delta_t;
> +		prep->lo_pause = pattern[0].delta_t;
> +		prep->hi_pause = pattern[actual_len - 1].delta_t;
>  	} else {
>  		if (delta_t != pattern[0].delta_t || delta_t != pattern[actual_len - 1].delta_t)
> -			goto out_free_pattern;
> +			return -EINVAL;
> +		prep->lo_pause = 0;
> +		prep->hi_pause = 0;
>  	}
>  
> +	prep->data = pattern;
> +	prep->len = actual_len;
> +	prep->delta_t = delta_t;
> +	prep->ping_pong = ping_pong;
>  
> -	mutex_lock(&lpg->lock);
> +	return 0;
> +}

[Severity: High]
Does this code inadvertently free the pattern buffer? Since pattern was
allocated with the __free(kfree) attribute, it will be automatically freed
upon returning from lpg_prepare_pattern(). The caller receives a dangling
pointer in prep->data. Should this use no_free_ptr(pattern) to transfer
ownership to the lpg_pattern structure?

> +
> +static int lpg_pattern_set(struct lpg_led *led, struct led_pattern *led_pattern,
> +			   u32 len, int repeat)
> +{
> +	struct lpg_channel *chan;
> +	struct lpg *lpg = led->lpg;
> +	struct lpg_pattern pattern;
> +	unsigned int lo_idx;
> +	unsigned int hi_idx;
> +	unsigned int i;
> +	int ret;
> +
> +	ret = lpg_prepare_pattern(lpg, led_pattern, len, repeat, &pattern);
> +	if (ret < 0)
> +		return ret;
> +
> +	guard(mutex)(&lpg->lock);
>  
>  	if (lpg->lut_base)
> -		ret = lpg_lut_store(lpg, pattern, actual_len, &lo_idx, &hi_idx);
> +		ret = lpg_lut_store(lpg, pattern.data, pattern.len, &lo_idx, &hi_idx);
>  	else
> -		ret = lpg_lut_store_sdam(lpg, pattern, actual_len, &lo_idx, &hi_idx);
> +		ret = lpg_lut_store_sdam(lpg, pattern.data, pattern.len, &lo_idx, &hi_idx);
>  
>  	if (ret < 0)
> -		goto out_unlock;
> +		goto free_pattern;

[Severity: Medium]
Is it okay to mix guard(mutex) and goto-based error handling in the same
function? The subsystem guidelines for cleanup recommend converting all
resources in a function to scope-based cleanup or converting none of them,
as mixing the two approaches creates confusing ownership semantics.

[ ... ]

> -out_unlock:
> -	mutex_unlock(&lpg->lock);
> -out_free_pattern:
> -	kfree(pattern);
> -
> +free_pattern:
> +	kfree(pattern.data);

[Severity: High]
Will this result in a double-free? If pattern.data was already freed when
lpg_prepare_pattern() returned due to the __free() attribute, calling
kfree() here on the same pointer could lead to memory corruption.

>  	return ret;
>  }

[ ... ]

>  static int lpg_pattern_mc_set(struct led_classdev *cdev,
> -			      struct led_pattern *pattern, u32 len,
> +			      struct led_pattern *led_pattern, u32 len,
>  			      int repeat)
>  {
>  	struct led_classdev_mc *mc = lcdev_to_mccdev(cdev);
>  	struct lpg_led *led = container_of(mc, struct lpg_led, mcdev);
> +	struct lpg *lpg = led->lpg;
> +	struct lpg_channel *chan;
> +	struct lpg_pattern pattern;
>  	unsigned int triled_mask = 0;
> -	int ret, i;
> +	unsigned int lo_idx;
> +	unsigned int hi_idx;
> +	unsigned int scale;
> +	unsigned int i, j;
> +	int ret;
> +
> +	ret = lpg_prepare_pattern(lpg, led_pattern, len, repeat, &pattern);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Allocate buffer for the per-channel scaled pattern copy */
> +	struct led_pattern *scaled __free(kfree) =
> +			   kmalloc_array(pattern.len, sizeof(*scaled), GFP_KERNEL);
> +	if (!scaled) {
> +		kfree(pattern.data);
> +		return -ENOMEM;
> +	}
>  
>  	for (i = 0; i < led->num_channels; i++)
>  		triled_mask |= led->channels[i]->triled_mask;
> -	triled_set(led->lpg, triled_mask, 0);
> +	triled_set(lpg, triled_mask, 0);
>  
> -	ret = lpg_pattern_set(led, pattern, len, repeat);
> +	led_mc_calc_color_components(mc, LED_FULL);
> +
> +	/*
> +	 * Each channel gets its own LUT block scaled by subled_info[i].brightness
> +	 * so the pattern respects the configured colour balance.
> +	 */
> +	guard(mutex)(&lpg->lock);

[Severity: Medium]
This function also mixes __free(kfree), guard(mutex) and goto-based
cleanup labels. Does this violate the subsystem cleanup guidelines?

> +
> +	for (i = 0; i < led->num_channels; i++) {
> +		chan = led->channels[i];
> +		scale = mc->subled_info[i].brightness;
> +
> +		for (j = 0; j < pattern.len; j++) {
> +			scaled[j].brightness = DIV_ROUND_CLOSEST(
> +				(u32)pattern.data[j].brightness * scale, LED_FULL);

[Severity: High]
Could this result in a use-after-free? Because lpg_prepare_pattern() uses
scope-based cleanup on the pattern buffer without transferring ownership,
the memory backing pattern.data is already freed by the time we attempt to
read its contents here.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260629-lpg-rgb-color-balance-fix-v4-1-4db8592fb3c5@oss.qualcomm.com?part=1

      reply	other threads:[~2026-06-30  3:34 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-30  3:25 [PATCH v4] leds: rgb: leds-qcom-lpg: Fix LED color balancing in HW pattern mode Fenglin Wu
2026-06-30  3:34 ` sashiko-bot [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260630033416.4406E1F00A3A@smtp.kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=fenglin.wu@oss.qualcomm.com \
    --cc=lee@kernel.org \
    --cc=linux-leds@vger.kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox