CVE-2026-45978
Description
In the Linux kernel, the following vulnerability has been resolved:
staging: greybus: lights: avoid NULL deref
gb_lights_light_config() stores channel_count before allocating the channels array. If kcalloc() fails, gb_lights_release() iterates the non-zero count and dereferences light->channels, which is NULL.
Allocate channels first and only then publish channels_count so the cleanup path can't walk a NULL pointer.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
In the Linux kernel's greybus lights driver, a NULL pointer dereference occurs if kcalloc fails after storing channel_count, fixed by allocating before setting count.
Vulnerability
In the Linux kernel's staging greybus lights driver, the function gb_lights_light_config() stores channel_count before allocating the channels array via kcalloc(). If kcalloc() fails, the cleanup path gb_lights_release() iterates based on the non-zero channel_count and dereferences the NULL light->channels, causing a NULL pointer dereference. This affects kernel versions prior to the fix commit [1].
Exploitation
An attacker would need to trigger a memory allocation failure in kcalloc(), likely by exhausting system memory or exploiting a separate vulnerability that limits memory availability. No special privileges or user interaction are required beyond the conditions leading to the allocation failure.
Impact
A NULL pointer dereference occurs, leading to a kernel crash (denial of service). The attacker does not gain code execution or privilege escalation; the impact is limited to system instability or a panic.
Mitigation
The fix was committed as commit a118724d7641b832fa14323e2733e28ae4834552 in the Linux kernel, and is included in stable releases following its integration. Users should update to a patched kernel version. No workaround is available; the fix reorders the operations to allocate the array before publishing the count.
AI Insight generated on May 27, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
1Patches
1606162d85f830staging: greybus: lights: avoid NULL deref
1 file changed · +6 −3
drivers/staging/greybus/light.c+6 −3 modifieddiff --git a/drivers/staging/greybus/light.c b/drivers/staging/greybus/light.c index e509fdc715dbbf..38c233a706c483 100644 --- a/drivers/staging/greybus/light.c +++ b/drivers/staging/greybus/light.c @@ -1008,14 +1008,18 @@ static int gb_lights_light_config(struct gb_lights *glights, u8 id) if (!strlen(conf.name)) return -EINVAL; - light->channels_count = conf.channel_count; light->name = kstrndup(conf.name, NAMES_MAX, GFP_KERNEL); if (!light->name) return -ENOMEM; - light->channels = kcalloc(light->channels_count, + light->channels = kcalloc(conf.channel_count, sizeof(struct gb_channel), GFP_KERNEL); if (!light->channels) return -ENOMEM; + /* + * Publish channels_count only after channels allocation so cleanup + * doesn't walk a NULL channels pointer on allocation failure. + */ + light->channels_count = conf.channel_count; /* First we collect all the configurations for all channels */ for (i = 0; i < light->channels_count; i++) { -- cgit 1.3-korg
3cbe694d235dstaging: greybus: lights: avoid NULL deref
1 file changed · +6 −3
drivers/staging/greybus/light.c+6 −3 modifieddiff --git a/drivers/staging/greybus/light.c b/drivers/staging/greybus/light.c index 9999f84016992a..eb69500e080e05 100644 --- a/drivers/staging/greybus/light.c +++ b/drivers/staging/greybus/light.c @@ -1029,14 +1029,18 @@ static int gb_lights_light_config(struct gb_lights *glights, u8 id) if (!strlen(conf.name)) return -EINVAL; - light->channels_count = conf.channel_count; light->name = kstrndup(conf.name, NAMES_MAX, GFP_KERNEL); if (!light->name) return -ENOMEM; - light->channels = kcalloc(light->channels_count, + light->channels = kcalloc(conf.channel_count, sizeof(struct gb_channel), GFP_KERNEL); if (!light->channels) return -ENOMEM; + /* + * Publish channels_count only after channels allocation so cleanup + * doesn't walk a NULL channels pointer on allocation failure. + */ + light->channels_count = conf.channel_count; /* First we collect all the configurations for all channels */ for (i = 0; i < light->channels_count; i++) { -- cgit 1.3-korg
ba5022162da6staging: greybus: lights: avoid NULL deref
1 file changed · +6 −3
drivers/staging/greybus/light.c+6 −3 modifieddiff --git a/drivers/staging/greybus/light.c b/drivers/staging/greybus/light.c index 9999f84016992a..eb69500e080e05 100644 --- a/drivers/staging/greybus/light.c +++ b/drivers/staging/greybus/light.c @@ -1029,14 +1029,18 @@ static int gb_lights_light_config(struct gb_lights *glights, u8 id) if (!strlen(conf.name)) return -EINVAL; - light->channels_count = conf.channel_count; light->name = kstrndup(conf.name, NAMES_MAX, GFP_KERNEL); if (!light->name) return -ENOMEM; - light->channels = kcalloc(light->channels_count, + light->channels = kcalloc(conf.channel_count, sizeof(struct gb_channel), GFP_KERNEL); if (!light->channels) return -ENOMEM; + /* + * Publish channels_count only after channels allocation so cleanup + * doesn't walk a NULL channels pointer on allocation failure. + */ + light->channels_count = conf.channel_count; /* First we collect all the configurations for all channels */ for (i = 0; i < light->channels_count; i++) { -- cgit 1.3-korg
65f2c608096dstaging: greybus: lights: avoid NULL deref
1 file changed · +6 −3
drivers/staging/greybus/light.c+6 −3 modifieddiff --git a/drivers/staging/greybus/light.c b/drivers/staging/greybus/light.c index 9999f84016992a..eb69500e080e05 100644 --- a/drivers/staging/greybus/light.c +++ b/drivers/staging/greybus/light.c @@ -1029,14 +1029,18 @@ static int gb_lights_light_config(struct gb_lights *glights, u8 id) if (!strlen(conf.name)) return -EINVAL; - light->channels_count = conf.channel_count; light->name = kstrndup(conf.name, NAMES_MAX, GFP_KERNEL); if (!light->name) return -ENOMEM; - light->channels = kcalloc(light->channels_count, + light->channels = kcalloc(conf.channel_count, sizeof(struct gb_channel), GFP_KERNEL); if (!light->channels) return -ENOMEM; + /* + * Publish channels_count only after channels allocation so cleanup + * doesn't walk a NULL channels pointer on allocation failure. + */ + light->channels_count = conf.channel_count; /* First we collect all the configurations for all channels */ for (i = 0; i < light->channels_count; i++) { -- cgit 1.3-korg
01b91cb3e748staging: greybus: lights: avoid NULL deref
1 file changed · +6 −3
drivers/staging/greybus/light.c+6 −3 modifieddiff --git a/drivers/staging/greybus/light.c b/drivers/staging/greybus/light.c index e509fdc715dbbf..38c233a706c483 100644 --- a/drivers/staging/greybus/light.c +++ b/drivers/staging/greybus/light.c @@ -1008,14 +1008,18 @@ static int gb_lights_light_config(struct gb_lights *glights, u8 id) if (!strlen(conf.name)) return -EINVAL; - light->channels_count = conf.channel_count; light->name = kstrndup(conf.name, NAMES_MAX, GFP_KERNEL); if (!light->name) return -ENOMEM; - light->channels = kcalloc(light->channels_count, + light->channels = kcalloc(conf.channel_count, sizeof(struct gb_channel), GFP_KERNEL); if (!light->channels) return -ENOMEM; + /* + * Publish channels_count only after channels allocation so cleanup + * doesn't walk a NULL channels pointer on allocation failure. + */ + light->channels_count = conf.channel_count; /* First we collect all the configurations for all channels */ for (i = 0; i < light->channels_count; i++) { -- cgit 1.3-korg
da46264a7016staging: greybus: lights: avoid NULL deref
1 file changed · +6 −3
drivers/staging/greybus/light.c+6 −3 modifieddiff --git a/drivers/staging/greybus/light.c b/drivers/staging/greybus/light.c index e509fdc715dbbf..38c233a706c483 100644 --- a/drivers/staging/greybus/light.c +++ b/drivers/staging/greybus/light.c @@ -1008,14 +1008,18 @@ static int gb_lights_light_config(struct gb_lights *glights, u8 id) if (!strlen(conf.name)) return -EINVAL; - light->channels_count = conf.channel_count; light->name = kstrndup(conf.name, NAMES_MAX, GFP_KERNEL); if (!light->name) return -ENOMEM; - light->channels = kcalloc(light->channels_count, + light->channels = kcalloc(conf.channel_count, sizeof(struct gb_channel), GFP_KERNEL); if (!light->channels) return -ENOMEM; + /* + * Publish channels_count only after channels allocation so cleanup + * doesn't walk a NULL channels pointer on allocation failure. + */ + light->channels_count = conf.channel_count; /* First we collect all the configurations for all channels */ for (i = 0; i < light->channels_count; i++) { -- cgit 1.3-korg
efcffd9a6ad8staging: greybus: lights: avoid NULL deref
1 file changed · +6 −3
drivers/staging/greybus/light.c+6 −3 modifieddiff --git a/drivers/staging/greybus/light.c b/drivers/staging/greybus/light.c index e509fdc715dbbf..38c233a706c483 100644 --- a/drivers/staging/greybus/light.c +++ b/drivers/staging/greybus/light.c @@ -1008,14 +1008,18 @@ static int gb_lights_light_config(struct gb_lights *glights, u8 id) if (!strlen(conf.name)) return -EINVAL; - light->channels_count = conf.channel_count; light->name = kstrndup(conf.name, NAMES_MAX, GFP_KERNEL); if (!light->name) return -ENOMEM; - light->channels = kcalloc(light->channels_count, + light->channels = kcalloc(conf.channel_count, sizeof(struct gb_channel), GFP_KERNEL); if (!light->channels) return -ENOMEM; + /* + * Publish channels_count only after channels allocation so cleanup + * doesn't walk a NULL channels pointer on allocation failure. + */ + light->channels_count = conf.channel_count; /* First we collect all the configurations for all channels */ for (i = 0; i < light->channels_count; i++) { -- cgit 1.3-korg
a118724d7641staging: greybus: lights: avoid NULL deref
1 file changed · +6 −3
drivers/staging/greybus/light.c+6 −3 modifieddiff --git a/drivers/staging/greybus/light.c b/drivers/staging/greybus/light.c index 7352d7deb8ba03..af91913794ee65 100644 --- a/drivers/staging/greybus/light.c +++ b/drivers/staging/greybus/light.c @@ -1030,14 +1030,18 @@ static int gb_lights_light_config(struct gb_lights *glights, u8 id) if (!strlen(conf.name)) return -EINVAL; - light->channels_count = conf.channel_count; light->name = kstrndup(conf.name, NAMES_MAX, GFP_KERNEL); if (!light->name) return -ENOMEM; - light->channels = kcalloc(light->channels_count, + light->channels = kcalloc(conf.channel_count, sizeof(struct gb_channel), GFP_KERNEL); if (!light->channels) return -ENOMEM; + /* + * Publish channels_count only after channels allocation so cleanup + * doesn't walk a NULL channels pointer on allocation failure. + */ + light->channels_count = conf.channel_count; /* First we collect all the configurations for all channels */ for (i = 0; i < light->channels_count; i++) { -- cgit 1.3-korg
efcffd9a6ad8staging: greybus: lights: avoid NULL deref
1 file changed · +6 −3
drivers/staging/greybus/light.c+6 −3 modifieddiff --git a/drivers/staging/greybus/light.c b/drivers/staging/greybus/light.c index e509fdc715dbbf..38c233a706c483 100644 --- a/drivers/staging/greybus/light.c +++ b/drivers/staging/greybus/light.c @@ -1008,14 +1008,18 @@ static int gb_lights_light_config(struct gb_lights *glights, u8 id) if (!strlen(conf.name)) return -EINVAL; - light->channels_count = conf.channel_count; light->name = kstrndup(conf.name, NAMES_MAX, GFP_KERNEL); if (!light->name) return -ENOMEM; - light->channels = kcalloc(light->channels_count, + light->channels = kcalloc(conf.channel_count, sizeof(struct gb_channel), GFP_KERNEL); if (!light->channels) return -ENOMEM; + /* + * Publish channels_count only after channels allocation so cleanup + * doesn't walk a NULL channels pointer on allocation failure. + */ + light->channels_count = conf.channel_count; /* First we collect all the configurations for all channels */ for (i = 0; i < light->channels_count; i++) { -- cgit 1.3-korg
01b91cb3e748staging: greybus: lights: avoid NULL deref
1 file changed · +6 −3
drivers/staging/greybus/light.c+6 −3 modifieddiff --git a/drivers/staging/greybus/light.c b/drivers/staging/greybus/light.c index e509fdc715dbbf..38c233a706c483 100644 --- a/drivers/staging/greybus/light.c +++ b/drivers/staging/greybus/light.c @@ -1008,14 +1008,18 @@ static int gb_lights_light_config(struct gb_lights *glights, u8 id) if (!strlen(conf.name)) return -EINVAL; - light->channels_count = conf.channel_count; light->name = kstrndup(conf.name, NAMES_MAX, GFP_KERNEL); if (!light->name) return -ENOMEM; - light->channels = kcalloc(light->channels_count, + light->channels = kcalloc(conf.channel_count, sizeof(struct gb_channel), GFP_KERNEL); if (!light->channels) return -ENOMEM; + /* + * Publish channels_count only after channels allocation so cleanup + * doesn't walk a NULL channels pointer on allocation failure. + */ + light->channels_count = conf.channel_count; /* First we collect all the configurations for all channels */ for (i = 0; i < light->channels_count; i++) { -- cgit 1.3-korg
06162d85f830staging: greybus: lights: avoid NULL deref
1 file changed · +6 −3
drivers/staging/greybus/light.c+6 −3 modifieddiff --git a/drivers/staging/greybus/light.c b/drivers/staging/greybus/light.c index e509fdc715dbbf..38c233a706c483 100644 --- a/drivers/staging/greybus/light.c +++ b/drivers/staging/greybus/light.c @@ -1008,14 +1008,18 @@ static int gb_lights_light_config(struct gb_lights *glights, u8 id) if (!strlen(conf.name)) return -EINVAL; - light->channels_count = conf.channel_count; light->name = kstrndup(conf.name, NAMES_MAX, GFP_KERNEL); if (!light->name) return -ENOMEM; - light->channels = kcalloc(light->channels_count, + light->channels = kcalloc(conf.channel_count, sizeof(struct gb_channel), GFP_KERNEL); if (!light->channels) return -ENOMEM; + /* + * Publish channels_count only after channels allocation so cleanup + * doesn't walk a NULL channels pointer on allocation failure. + */ + light->channels_count = conf.channel_count; /* First we collect all the configurations for all channels */ for (i = 0; i < light->channels_count; i++) { -- cgit 1.3-korg
3cbe694d235dstaging: greybus: lights: avoid NULL deref
1 file changed · +6 −3
drivers/staging/greybus/light.c+6 −3 modifieddiff --git a/drivers/staging/greybus/light.c b/drivers/staging/greybus/light.c index 9999f84016992a..eb69500e080e05 100644 --- a/drivers/staging/greybus/light.c +++ b/drivers/staging/greybus/light.c @@ -1029,14 +1029,18 @@ static int gb_lights_light_config(struct gb_lights *glights, u8 id) if (!strlen(conf.name)) return -EINVAL; - light->channels_count = conf.channel_count; light->name = kstrndup(conf.name, NAMES_MAX, GFP_KERNEL); if (!light->name) return -ENOMEM; - light->channels = kcalloc(light->channels_count, + light->channels = kcalloc(conf.channel_count, sizeof(struct gb_channel), GFP_KERNEL); if (!light->channels) return -ENOMEM; + /* + * Publish channels_count only after channels allocation so cleanup + * doesn't walk a NULL channels pointer on allocation failure. + */ + light->channels_count = conf.channel_count; /* First we collect all the configurations for all channels */ for (i = 0; i < light->channels_count; i++) { -- cgit 1.3-korg
a118724d7641staging: greybus: lights: avoid NULL deref
1 file changed · +6 −3
drivers/staging/greybus/light.c+6 −3 modifieddiff --git a/drivers/staging/greybus/light.c b/drivers/staging/greybus/light.c index 7352d7deb8ba03..af91913794ee65 100644 --- a/drivers/staging/greybus/light.c +++ b/drivers/staging/greybus/light.c @@ -1030,14 +1030,18 @@ static int gb_lights_light_config(struct gb_lights *glights, u8 id) if (!strlen(conf.name)) return -EINVAL; - light->channels_count = conf.channel_count; light->name = kstrndup(conf.name, NAMES_MAX, GFP_KERNEL); if (!light->name) return -ENOMEM; - light->channels = kcalloc(light->channels_count, + light->channels = kcalloc(conf.channel_count, sizeof(struct gb_channel), GFP_KERNEL); if (!light->channels) return -ENOMEM; + /* + * Publish channels_count only after channels allocation so cleanup + * doesn't walk a NULL channels pointer on allocation failure. + */ + light->channels_count = conf.channel_count; /* First we collect all the configurations for all channels */ for (i = 0; i < light->channels_count; i++) { -- cgit 1.3-korg
ba5022162da6staging: greybus: lights: avoid NULL deref
1 file changed · +6 −3
drivers/staging/greybus/light.c+6 −3 modifieddiff --git a/drivers/staging/greybus/light.c b/drivers/staging/greybus/light.c index 9999f84016992a..eb69500e080e05 100644 --- a/drivers/staging/greybus/light.c +++ b/drivers/staging/greybus/light.c @@ -1029,14 +1029,18 @@ static int gb_lights_light_config(struct gb_lights *glights, u8 id) if (!strlen(conf.name)) return -EINVAL; - light->channels_count = conf.channel_count; light->name = kstrndup(conf.name, NAMES_MAX, GFP_KERNEL); if (!light->name) return -ENOMEM; - light->channels = kcalloc(light->channels_count, + light->channels = kcalloc(conf.channel_count, sizeof(struct gb_channel), GFP_KERNEL); if (!light->channels) return -ENOMEM; + /* + * Publish channels_count only after channels allocation so cleanup + * doesn't walk a NULL channels pointer on allocation failure. + */ + light->channels_count = conf.channel_count; /* First we collect all the configurations for all channels */ for (i = 0; i < light->channels_count; i++) { -- cgit 1.3-korg
da46264a7016staging: greybus: lights: avoid NULL deref
1 file changed · +6 −3
drivers/staging/greybus/light.c+6 −3 modifieddiff --git a/drivers/staging/greybus/light.c b/drivers/staging/greybus/light.c index e509fdc715dbbf..38c233a706c483 100644 --- a/drivers/staging/greybus/light.c +++ b/drivers/staging/greybus/light.c @@ -1008,14 +1008,18 @@ static int gb_lights_light_config(struct gb_lights *glights, u8 id) if (!strlen(conf.name)) return -EINVAL; - light->channels_count = conf.channel_count; light->name = kstrndup(conf.name, NAMES_MAX, GFP_KERNEL); if (!light->name) return -ENOMEM; - light->channels = kcalloc(light->channels_count, + light->channels = kcalloc(conf.channel_count, sizeof(struct gb_channel), GFP_KERNEL); if (!light->channels) return -ENOMEM; + /* + * Publish channels_count only after channels allocation so cleanup + * doesn't walk a NULL channels pointer on allocation failure. + */ + light->channels_count = conf.channel_count; /* First we collect all the configurations for all channels */ for (i = 0; i < light->channels_count; i++) { -- cgit 1.3-korg
65f2c608096dstaging: greybus: lights: avoid NULL deref
1 file changed · +6 −3
drivers/staging/greybus/light.c+6 −3 modifieddiff --git a/drivers/staging/greybus/light.c b/drivers/staging/greybus/light.c index 9999f84016992a..eb69500e080e05 100644 --- a/drivers/staging/greybus/light.c +++ b/drivers/staging/greybus/light.c @@ -1029,14 +1029,18 @@ static int gb_lights_light_config(struct gb_lights *glights, u8 id) if (!strlen(conf.name)) return -EINVAL; - light->channels_count = conf.channel_count; light->name = kstrndup(conf.name, NAMES_MAX, GFP_KERNEL); if (!light->name) return -ENOMEM; - light->channels = kcalloc(light->channels_count, + light->channels = kcalloc(conf.channel_count, sizeof(struct gb_channel), GFP_KERNEL); if (!light->channels) return -ENOMEM; + /* + * Publish channels_count only after channels allocation so cleanup + * doesn't walk a NULL channels pointer on allocation failure. + */ + light->channels_count = conf.channel_count; /* First we collect all the configurations for all channels */ for (i = 0; i < light->channels_count; i++) { -- cgit 1.3-korg
Vulnerability mechanics
Root cause
"Improper ordering of initialization: channels_count is set before the channels array is allocated, so a failed allocation leaves a non-zero count paired with a NULL pointer."
Attack vector
An attacker who can trigger a memory allocation failure (e.g., by exhausting system memory) while the Greybus lights driver is configuring a light can cause a NULL pointer dereference. The function `gb_lights_light_config()` sets `light->channels_count` to a non-zero value before calling `kcalloc()`. If `kcalloc()` returns NULL, the error path returns `-ENOMEM`, but `channels_count` remains non-zero. Later, when `gb_lights_release()` is called during cleanup, it iterates `for (i = 0; i < light->channels_count; i++)` and dereferences `light->channels[i]`, which is a NULL pointer [patch_id=2660779].
Affected code
The vulnerability is in `drivers/staging/greybus/light.c` in the function `gb_lights_light_config()`. The bug is that `light->channels_count` is assigned from `conf.channel_count` before `light->channels` is allocated via `kcalloc()`. If the allocation fails, the cleanup path in `gb_lights_release()` iterates using the non-zero `channels_count` and dereferences the NULL `light->channels` pointer [patch_id=2660779].
What the fix does
The patch moves the assignment `light->channels_count = conf.channel_count` to after the `kcalloc()` allocation succeeds [patch_id=2660779]. Previously `channels_count` was set before allocation, so a failed `kcalloc()` left a non-zero count paired with a NULL `channels` pointer. By deferring the assignment until after allocation, if `kcalloc()` fails the `channels_count` remains at its initial value (zero), and `gb_lights_release()` will not iterate over the NULL pointer [patch_id=2660779].
Preconditions
- inputMemory allocation failure for kcalloc (e.g., system memory exhaustion)
- configThe Greybus lights driver must be in use and configuring a light device
Generated on May 27, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
8- git.kernel.org/stable/c/01b91cb3e748032fd96bbe0043812b426a52f091nvd
- git.kernel.org/stable/c/06162d85f830582da6e9e5fcf9c9504d6da9ae0bnvd
- git.kernel.org/stable/c/3cbe694d235d96f628ec7dc6ae4d8bdddb768699nvd
- git.kernel.org/stable/c/65f2c608096d766540953d9b170d216aa3b5eb95nvd
- git.kernel.org/stable/c/a118724d7641b832fa14323e2733e28ae4834552nvd
- git.kernel.org/stable/c/ba5022162da63059bae36c4fd84d7031f582c71fnvd
- git.kernel.org/stable/c/da46264a7016034a5bbbad034c012ef218b7d0afnvd
- git.kernel.org/stable/c/efcffd9a6ad8d190651498d5eda53bfc7cf683a7nvd
News mentions
0No linked articles in our index yet.