[PATCH] cmd: led: reject unknown LED state instead of silently returning success
Quentin Schulz
quentin.schulz at cherry.de
Fri Jul 10 17:34:19 CEST 2026
Hi Naveen,
Please do not top-post but rather answer inline, like I'm going to do now.
On 7/10/26 5:16 PM, Naveen Kumar Chaudhary wrote:
> After the commit 72675b063b6e changed led states to an enum, I honestly
> don't see any problem (others can correct me). The current
Well, if we add another operation to enum led_state_t without adding a
new mapping in state_label[] and the user writes e.g. "led
does-not-exist" on the command line, we'll have a null-pointer
dereference in get_led_cmd(). So today it's fine, sure. It's not
future-proof though. We can add a simple check on state_label[i] being
non-NULL before passing to strncmp and this would be covered.
> implementation is more readable than if-else ladder, specially when the
> list grows for whatever reason.
>
I disagree but that's matter of taste so I don't care too much here :)
> However, thanks to your comment, I went through the implementation once
> more and found that the below define in led.c seems leftover and buggy
> to me and needs to be removed :
>
> #define LED_TOGGLE LEDST_COUNT
>
> Thoughts?
>
I think that's a leftover from the legacy LED API that we got rid of
last release. It's not used anyway so can be safely removed.
Cheers,
Quentin
> Regards,
> Naveen
>
> On Wed 08 Jul 06:00 PM, Quentin Schulz wrote:
>> Hi Naveen,
>>
>> On 7/8/26 4:32 PM, Naveen Kumar Chaudhary wrote:
>>> get_led_cmd() searches state_label[] for a matching name and returns
>>> -1 on no match, but its declared return type is enum led_state_t.
>>> All enumerators of that enum are non-negative, so the compiler is
>>> free to pick an unsigned underlying type (and does so with
>>> -fshort-enums, which several U-Boot targets enable). In that case
>>> -1 is reinterpreted as a large positive value and any subsequent
>>> comparison against -1 silently misbehaves.
>>>
>>> In do_led() the returned value is stored into an enum led_state_t
>>> and dispatched through a switch that has no default arm and no case
>>> for the sentinel. When the user supplies an unknown state name the
>>> switch falls through, ret was already set to 0 by led_get_by_label(),
>>> and the command reports success without doing anything.
>>>
>>> Return LEDST_COUNT from get_led_cmd() on no match. LEDST_COUNT is
>>> already a valid enumerator, so this avoids all questions about the
>>> enum's underlying representation. In do_led(), when a state argument
>>> was actually supplied (argc > 2) and the lookup produced LEDST_COUNT,
>>> reject it with a diagnostic and CMD_RET_USAGE; the existing
>>> LEDST_COUNT switch arm continues to handle the "no state argument"
>>> case where it means "show current state".
>>>
>>
>> That looks correct to me, but I think get_led_cmd() is overengineered and
>> actually still flawed. Indeed, we iterate up to LEDST_COUNT-1 times on the
>> state_label[] array, but nothing guarantees the array is that big (it is
>> right now) so we could theoretically have out-of-bounds access in the
>> future. It also isn't handling the case when there's no matching string at a
>> given index (e.g. holes in the array or padding at the end due to the last
>> element in the array not being LEDST_COUNT-1), where state_label[i] would be
>> NULL and strncmp doesn't seem to be handling that case (it isn't in
>> lib/string.c for example).
>>
>> I think the answer is to instead get rid of this and drastically simplify.
>>
>> Remove get_led_cmd() and state_label[] and do something like the following
>> in do_led() (NOT TESTED):
>>
>> """
>> diff --git a/cmd/led.c b/cmd/led.c
>> index 296c07b3b38b..d1fc9dc8cdbd 100644
>> --- a/cmd/led.c
>> +++ b/cmd/led.c
>> @@ -9,27 +9,6 @@
>> #include <led.h>
>> #include <dm/uclass-internal.h>
>>
>> -#define LED_TOGGLE LEDST_COUNT
>> -
>> -static const char *const state_label[] = {
>> - [LEDST_OFF] = "off",
>> - [LEDST_ON] = "on",
>> - [LEDST_TOGGLE] = "toggle",
>> - [LEDST_BLINK] = "blink",
>> -};
>> -
>> -enum led_state_t get_led_cmd(char *var)
>> -{
>> - int i;
>> -
>> - for (i = 0; i < LEDST_COUNT; i++) {
>> - if (!strncmp(var, state_label[i], strlen(var)))
>> - return i;
>> - }
>> -
>> - return -1;
>> -}
>> -
>> static int show_led_state(struct udevice *dev)
>> {
>> int ret;
>> @@ -83,12 +62,24 @@ int do_led(struct cmd_tbl *cmdtp, int flag, int argc,
>> char *const argv[])
>> if (strncmp(led_label, "list", 4) == 0)
>> return list_leds();
>>
>> - cmd = argc > 2 ? get_led_cmd(argv[2]) : LEDST_COUNT;
>> - if (cmd == LEDST_BLINK) {
>> + if (argc == 2) {
>> + cmd = LEDST_COUNT;
>> + } else if (!strncmp(argv[2], "off", strlen(argv[2]))) {
>> + cmd = LEDST_OFF;
>> + } else if (!strncmp(argv[2], "on", strlen(argv[2]))) {
>> + cmd = LEDST_ON;
>> + } else if (!strncmp(argv[2], "toggle", strlen(argv[2]))) {
>> + cmd = LEDST_TOGGLE;
>> + } else if (!strncmp(argv[2], "blink", strlen(argv[2]))) {
>> if (argc < 4)
>> return CMD_RET_USAGE;
>> +
>> + cmd = LEDST_BLINK;
>> freq_ms = dectoul(argv[3], NULL);
>> + } else {
>> + return CMD_RET_USAGE;
>> }
>> +
>> ret = led_get_by_label(led_label, &dev);
>> if (ret) {
>> printf("LED '%s' not found (err=%d)\n", led_label, ret);
>> """
>>
>> What do you think?
>>
>> I wouldn't necessarily add a printf for when the user mistypes, just return
>> CMD_RET_USAGE where we list what's possible. If you really want to keep it
>> at least replace "state" with "operation" as it's something we do on the LED
>> not its state (e.g. one can request "toggle" which definitely isn't a
>> state).
>>
>> Please also add
>> Fixes: ffe2052d6e8a ("dm: led: Add a new 'led' command")
>>
>> to your commit log also as that's the commit introducing the error.
>>
>> Cheers,
>> Quentin
>>
>>> Signed-off-by: Naveen Kumar Chaudhary <naveen.osdev at gmail.com>
>>> ---
>>> cmd/led.c | 6 +++++-
>>> 1 file changed, 5 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/cmd/led.c b/cmd/led.c
>>> index 296c07b3b38..d547276e480 100644
>>> --- a/cmd/led.c
>>> +++ b/cmd/led.c
>>> @@ -27,7 +27,7 @@ enum led_state_t get_led_cmd(char *var)
>>> return i;
>>> }
>>> - return -1;
>>> + return LEDST_COUNT;
>>> }
>>> static int show_led_state(struct udevice *dev)
>>> @@ -84,6 +84,10 @@ int do_led(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
>>> return list_leds();
>>> cmd = argc > 2 ? get_led_cmd(argv[2]) : LEDST_COUNT;
>>> + if (argc > 2 && cmd == LEDST_COUNT) {
>>> + printf("Unknown LED state '%s'\n", argv[2]);
>>> + return CMD_RET_USAGE;
>>> + }
>>> if (cmd == LEDST_BLINK) {
>>> if (argc < 4)
>>> return CMD_RET_USAGE;
>>
More information about the U-Boot
mailing list