* [PATCH 0/3] HID: Fix some memory leaks in drivers/hid
@ 2026-02-17 16:01 Günther Noack
2026-02-17 16:01 ` [PATCH 1/3] HID: apple: avoid memory leak in apple_report_fixup() Günther Noack
` (3 more replies)
0 siblings, 4 replies; 12+ messages in thread
From: Günther Noack @ 2026-02-17 16:01 UTC (permalink / raw)
To: Jiri Kosina
Cc: Benjamin Tissoires, linux-input, linux-kernel, Günther Noack
Hello!
These patches fix a few memory leaks in HID report descriptor fixups.
FWIW, a good ad-hoc way to look for usages of allocation functions in
these is:
awk '/static.*report_fixup.*/,/^}/ { print FILENAME, $0 }' drivers/hid/hid-*.c \
| grep -E '(malloc|kzalloc|kcalloc|kmemdup)'
The devm_* variants are safe in this context, because they tie the
allocated memory to the lifetime of the driver.
For transparency, I generated these commits with Gemini-CLI,
starting with this prompt:
We are working in the Linux kernel. In the HID drivers in
`drivers/hid/hid-*.c`, the `report_fixup` driver hook is a function
that gets a byte buffer (with size) as input and that may modify that
byte buffer, and optionally return a pointer to a new byte buffer and
update the size. The returned value is *not* memory-managed by the
caller though and will not be freed subsequently. When the
`report_fixup` implementation allocates a new buffer and returns that,
that will not get freed by the caller. Validate this assessment and
fix up all HID drivers where that mistake is made.
(and then a little bit of additional nudging for the details).
—Günther
Günther Noack (3):
HID: apple: avoid memory leak in apple_report_fixup()
HID: magicmouse: avoid memory leak in magicmouse_report_fixup()
HID: asus: avoid memory leak in asus_report_fixup()
drivers/hid/hid-apple.c | 4 +---
drivers/hid/hid-asus.c | 15 +++++++++++----
drivers/hid/hid-magicmouse.c | 4 +---
3 files changed, 13 insertions(+), 10 deletions(-)
--
2.53.0.335.g19a08e0c02-goog
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 1/3] HID: apple: avoid memory leak in apple_report_fixup()
2026-02-17 16:01 [PATCH 0/3] HID: Fix some memory leaks in drivers/hid Günther Noack
@ 2026-02-17 16:01 ` Günther Noack
2026-02-17 18:22 ` Benjamin Tissoires
2026-02-17 16:01 ` [PATCH 2/3] HID: magicmouse: avoid memory leak in magicmouse_report_fixup() Günther Noack
` (2 subsequent siblings)
3 siblings, 1 reply; 12+ messages in thread
From: Günther Noack @ 2026-02-17 16:01 UTC (permalink / raw)
To: Jiri Kosina
Cc: Benjamin Tissoires, linux-input, linux-kernel, Günther Noack
The apple_report_fixup() function was allocating a new buffer with
kmemdup() but never freeing it. Since the caller of report_fixup() already
provides a writable buffer and allows returning a pointer within that
buffer, we can just modify the descriptor in-place and return the adjusted
pointer.
Assisted-by: Gemini-CLI:Google Gemini 3
Signed-off-by: Günther Noack <gnoack@google.com>
---
drivers/hid/hid-apple.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c
index 233e367cce1d..894adc23367b 100644
--- a/drivers/hid/hid-apple.c
+++ b/drivers/hid/hid-apple.c
@@ -686,9 +686,7 @@ static const __u8 *apple_report_fixup(struct hid_device *hdev, __u8 *rdesc,
hid_info(hdev,
"fixing up Magic Keyboard battery report descriptor\n");
*rsize = *rsize - 1;
- rdesc = kmemdup(rdesc + 1, *rsize, GFP_KERNEL);
- if (!rdesc)
- return NULL;
+ rdesc = rdesc + 1;
rdesc[0] = 0x05;
rdesc[1] = 0x01;
--
2.53.0.335.g19a08e0c02-goog
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 2/3] HID: magicmouse: avoid memory leak in magicmouse_report_fixup()
2026-02-17 16:01 [PATCH 0/3] HID: Fix some memory leaks in drivers/hid Günther Noack
2026-02-17 16:01 ` [PATCH 1/3] HID: apple: avoid memory leak in apple_report_fixup() Günther Noack
@ 2026-02-17 16:01 ` Günther Noack
2026-02-17 16:01 ` [PATCH 3/3] HID: asus: avoid memory leak in asus_report_fixup() Günther Noack
2026-02-17 18:36 ` [PATCH 0/3] HID: Fix some memory leaks in drivers/hid Benjamin Tissoires
3 siblings, 0 replies; 12+ messages in thread
From: Günther Noack @ 2026-02-17 16:01 UTC (permalink / raw)
To: Jiri Kosina
Cc: Benjamin Tissoires, linux-input, linux-kernel, Günther Noack
The magicmouse_report_fixup() function was allocating a new buffer with
kmemdup() but never freeing it. Since the caller already provides a
writable buffer and allows returning a pointer within it, modify the
descriptor in-place and return the adjusted pointer.
Assisted-by: Gemini-CLI:Google Gemini 3
Signed-off-by: Günther Noack <gnoack@google.com>
---
drivers/hid/hid-magicmouse.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c
index 91f621ceb924..17908d52c027 100644
--- a/drivers/hid/hid-magicmouse.c
+++ b/drivers/hid/hid-magicmouse.c
@@ -994,9 +994,7 @@ static const __u8 *magicmouse_report_fixup(struct hid_device *hdev, __u8 *rdesc,
hid_info(hdev,
"fixing up magicmouse battery report descriptor\n");
*rsize = *rsize - 1;
- rdesc = kmemdup(rdesc + 1, *rsize, GFP_KERNEL);
- if (!rdesc)
- return NULL;
+ rdesc = rdesc + 1;
rdesc[0] = 0x05;
rdesc[1] = 0x01;
--
2.53.0.335.g19a08e0c02-goog
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 3/3] HID: asus: avoid memory leak in asus_report_fixup()
2026-02-17 16:01 [PATCH 0/3] HID: Fix some memory leaks in drivers/hid Günther Noack
2026-02-17 16:01 ` [PATCH 1/3] HID: apple: avoid memory leak in apple_report_fixup() Günther Noack
2026-02-17 16:01 ` [PATCH 2/3] HID: magicmouse: avoid memory leak in magicmouse_report_fixup() Günther Noack
@ 2026-02-17 16:01 ` Günther Noack
2026-02-17 18:31 ` Benjamin Tissoires
2026-02-17 18:36 ` [PATCH 0/3] HID: Fix some memory leaks in drivers/hid Benjamin Tissoires
3 siblings, 1 reply; 12+ messages in thread
From: Günther Noack @ 2026-02-17 16:01 UTC (permalink / raw)
To: Jiri Kosina
Cc: Benjamin Tissoires, linux-input, linux-kernel, Günther Noack
The asus_report_fixup() function was allocating a new buffer with kmemdup()
when growing the report descriptor but never freeing it. Switch to
devm_kzalloc() to ensure the memory is managed and freed automatically when
the device is removed.
Also fix a harmless out-of-bounds read by copying only the original
descriptor size.
Assisted-by: Gemini-CLI:Google Gemini 3
Signed-off-by: Günther Noack <gnoack@google.com>
---
drivers/hid/hid-asus.c | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)
diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
index 8ffcd12038e8..7a08e964b9cc 100644
--- a/drivers/hid/hid-asus.c
+++ b/drivers/hid/hid-asus.c
@@ -1399,14 +1399,21 @@ static const __u8 *asus_report_fixup(struct hid_device *hdev, __u8 *rdesc,
*/
if (*rsize == rsize_orig &&
rdesc[offs] == 0x09 && rdesc[offs + 1] == 0x76) {
- *rsize = rsize_orig + 1;
- rdesc = kmemdup(rdesc, *rsize, GFP_KERNEL);
- if (!rdesc)
- return NULL;
+ __u8 *new_rdesc;
+
+ new_rdesc = devm_kzalloc(&hdev->dev, rsize_orig + 1,
+ GFP_KERNEL);
+ if (!new_rdesc)
+ return rdesc;
hid_info(hdev, "Fixing up %s keyb report descriptor\n",
drvdata->quirks & QUIRK_T100CHI ?
"T100CHI" : "T90CHI");
+
+ memcpy(new_rdesc, rdesc, rsize_orig);
+ *rsize = rsize_orig + 1;
+ rdesc = new_rdesc;
+
memmove(rdesc + offs + 4, rdesc + offs + 2, 12);
rdesc[offs] = 0x19;
rdesc[offs + 1] = 0x00;
--
2.53.0.335.g19a08e0c02-goog
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH 1/3] HID: apple: avoid memory leak in apple_report_fixup()
2026-02-17 16:01 ` [PATCH 1/3] HID: apple: avoid memory leak in apple_report_fixup() Günther Noack
@ 2026-02-17 18:22 ` Benjamin Tissoires
2026-02-17 19:42 ` Günther Noack
0 siblings, 1 reply; 12+ messages in thread
From: Benjamin Tissoires @ 2026-02-17 18:22 UTC (permalink / raw)
To: Günther Noack; +Cc: Jiri Kosina, linux-input, linux-kernel
On Feb 17 2026, Günther Noack wrote:
> The apple_report_fixup() function was allocating a new buffer with
> kmemdup() but never freeing it. Since the caller of report_fixup() already
> provides a writable buffer and allows returning a pointer within that
> buffer, we can just modify the descriptor in-place and return the adjusted
> pointer.
>
> Assisted-by: Gemini-CLI:Google Gemini 3
> Signed-off-by: Günther Noack <gnoack@google.com>
> ---
> drivers/hid/hid-apple.c | 4 +---
> 1 file changed, 1 insertion(+), 3 deletions(-)
>
> diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c
> index 233e367cce1d..894adc23367b 100644
> --- a/drivers/hid/hid-apple.c
> +++ b/drivers/hid/hid-apple.c
> @@ -686,9 +686,7 @@ static const __u8 *apple_report_fixup(struct hid_device *hdev, __u8 *rdesc,
> hid_info(hdev,
> "fixing up Magic Keyboard battery report descriptor\n");
> *rsize = *rsize - 1;
> - rdesc = kmemdup(rdesc + 1, *rsize, GFP_KERNEL);
> - if (!rdesc)
> - return NULL;
> + rdesc = rdesc + 1;
I might be wrong, but later we call free(dev->rdesc) on device removal.
AFAICT, incrementing the pointer is undefined behavior.
What we should do instead is probably a krealloc instead of a kmemdup.
Same for all 3 patches.
Cheers,
Benjamin
>
> rdesc[0] = 0x05;
> rdesc[1] = 0x01;
> --
> 2.53.0.335.g19a08e0c02-goog
>
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 3/3] HID: asus: avoid memory leak in asus_report_fixup()
2026-02-17 16:01 ` [PATCH 3/3] HID: asus: avoid memory leak in asus_report_fixup() Günther Noack
@ 2026-02-17 18:31 ` Benjamin Tissoires
2026-02-17 19:51 ` Günther Noack
0 siblings, 1 reply; 12+ messages in thread
From: Benjamin Tissoires @ 2026-02-17 18:31 UTC (permalink / raw)
To: Günther Noack; +Cc: Jiri Kosina, linux-input, linux-kernel
On Feb 17 2026, Günther Noack wrote:
> The asus_report_fixup() function was allocating a new buffer with kmemdup()
> when growing the report descriptor but never freeing it. Switch to
> devm_kzalloc() to ensure the memory is managed and freed automatically when
> the device is removed.
Actually this one is even worse: you can't use devm_kzalloc because
hid-core.c will later call kfree(dev->rdesc) if dev->rdesc is different
from the one provided by the low level driver. So we are going to have
a double free.
I really wonder if this was ever tested.
Cheers,
Benjamin
>
> Also fix a harmless out-of-bounds read by copying only the original
> descriptor size.
>
> Assisted-by: Gemini-CLI:Google Gemini 3
> Signed-off-by: Günther Noack <gnoack@google.com>
> ---
> drivers/hid/hid-asus.c | 15 +++++++++++----
> 1 file changed, 11 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
> index 8ffcd12038e8..7a08e964b9cc 100644
> --- a/drivers/hid/hid-asus.c
> +++ b/drivers/hid/hid-asus.c
> @@ -1399,14 +1399,21 @@ static const __u8 *asus_report_fixup(struct hid_device *hdev, __u8 *rdesc,
> */
> if (*rsize == rsize_orig &&
> rdesc[offs] == 0x09 && rdesc[offs + 1] == 0x76) {
> - *rsize = rsize_orig + 1;
> - rdesc = kmemdup(rdesc, *rsize, GFP_KERNEL);
> - if (!rdesc)
> - return NULL;
> + __u8 *new_rdesc;
> +
> + new_rdesc = devm_kzalloc(&hdev->dev, rsize_orig + 1,
> + GFP_KERNEL);
> + if (!new_rdesc)
> + return rdesc;
>
> hid_info(hdev, "Fixing up %s keyb report descriptor\n",
> drvdata->quirks & QUIRK_T100CHI ?
> "T100CHI" : "T90CHI");
> +
> + memcpy(new_rdesc, rdesc, rsize_orig);
> + *rsize = rsize_orig + 1;
> + rdesc = new_rdesc;
> +
> memmove(rdesc + offs + 4, rdesc + offs + 2, 12);
> rdesc[offs] = 0x19;
> rdesc[offs + 1] = 0x00;
> --
> 2.53.0.335.g19a08e0c02-goog
>
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 0/3] HID: Fix some memory leaks in drivers/hid
2026-02-17 16:01 [PATCH 0/3] HID: Fix some memory leaks in drivers/hid Günther Noack
` (2 preceding siblings ...)
2026-02-17 16:01 ` [PATCH 3/3] HID: asus: avoid memory leak in asus_report_fixup() Günther Noack
@ 2026-02-17 18:36 ` Benjamin Tissoires
2026-02-17 20:08 ` Günther Noack
3 siblings, 1 reply; 12+ messages in thread
From: Benjamin Tissoires @ 2026-02-17 18:36 UTC (permalink / raw)
To: Günther Noack; +Cc: Jiri Kosina, linux-input, linux-kernel
On Feb 17 2026, Günther Noack wrote:
> Hello!
>
> These patches fix a few memory leaks in HID report descriptor fixups.
>
> FWIW, a good ad-hoc way to look for usages of allocation functions in
> these is:
>
> awk '/static.*report_fixup.*/,/^}/ { print FILENAME, $0 }' drivers/hid/hid-*.c \
> | grep -E '(malloc|kzalloc|kcalloc|kmemdup)'
>
> The devm_* variants are safe in this context, because they tie the
> allocated memory to the lifetime of the driver.
No. Look at hid_close_report() in drivers/hid/hid-core.c.
HID still hasn't fully migrated to devm, so as a rule of thumb, if you
change a kzalloc into a devm_kzalloc, you are getting into troubles
unless you fix the all the kfree path.
>
> For transparency, I generated these commits with Gemini-CLI,
> starting with this prompt:
>
> We are working in the Linux kernel. In the HID drivers in
> `drivers/hid/hid-*.c`, the `report_fixup` driver hook is a function
> that gets a byte buffer (with size) as input and that may modify that
> byte buffer, and optionally return a pointer to a new byte buffer and
> update the size. The returned value is *not* memory-managed by the
> caller though and will not be freed subsequently. When the
If the memory is *not* managed, why would gemini converts kzalloc into
devm variants without changing the kfree paths????
> `report_fixup` implementation allocates a new buffer and returns that,
> that will not get freed by the caller.
This is wrong. See hid_close_report(): if the new rdesc (after fixup)
differs from the one initially set, there is an explicit call to
kfree().
-> there is no memleak AFAICT, and your prompt is wrong.
Cheers,
Benjamin
> Validate this assessment and
> fix up all HID drivers where that mistake is made.
>
> (and then a little bit of additional nudging for the details).
>
> —Günther
>
>
> Günther Noack (3):
> HID: apple: avoid memory leak in apple_report_fixup()
> HID: magicmouse: avoid memory leak in magicmouse_report_fixup()
> HID: asus: avoid memory leak in asus_report_fixup()
>
> drivers/hid/hid-apple.c | 4 +---
> drivers/hid/hid-asus.c | 15 +++++++++++----
> drivers/hid/hid-magicmouse.c | 4 +---
> 3 files changed, 13 insertions(+), 10 deletions(-)
>
> --
> 2.53.0.335.g19a08e0c02-goog
>
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 1/3] HID: apple: avoid memory leak in apple_report_fixup()
2026-02-17 18:22 ` Benjamin Tissoires
@ 2026-02-17 19:42 ` Günther Noack
2026-02-18 19:04 ` Benjamin Tissoires
0 siblings, 1 reply; 12+ messages in thread
From: Günther Noack @ 2026-02-17 19:42 UTC (permalink / raw)
To: Benjamin Tissoires; +Cc: Jiri Kosina, linux-input, linux-kernel
Hello Benjamin!
On Tue, Feb 17, 2026 at 07:22:20PM +0100, Benjamin Tissoires wrote:
> On Feb 17 2026, Günther Noack wrote:
> > The apple_report_fixup() function was allocating a new buffer with
> > kmemdup() but never freeing it. Since the caller of report_fixup() already
> > provides a writable buffer and allows returning a pointer within that
> > buffer, we can just modify the descriptor in-place and return the adjusted
> > pointer.
> >
> > Assisted-by: Gemini-CLI:Google Gemini 3
> > Signed-off-by: Günther Noack <gnoack@google.com>
> > ---
> > drivers/hid/hid-apple.c | 4 +---
> > 1 file changed, 1 insertion(+), 3 deletions(-)
> >
> > diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c
> > index 233e367cce1d..894adc23367b 100644
> > --- a/drivers/hid/hid-apple.c
> > +++ b/drivers/hid/hid-apple.c
> > @@ -686,9 +686,7 @@ static const __u8 *apple_report_fixup(struct hid_device *hdev, __u8 *rdesc,
> > hid_info(hdev,
> > "fixing up Magic Keyboard battery report descriptor\n");
> > *rsize = *rsize - 1;
> > - rdesc = kmemdup(rdesc + 1, *rsize, GFP_KERNEL);
> > - if (!rdesc)
> > - return NULL;
> > + rdesc = rdesc + 1;
>
> I might be wrong, but later we call free(dev->rdesc) on device removal.
> AFAICT, incrementing the pointer is undefined behavior.
>
> What we should do instead is probably a krealloc instead of a kmemdup.
>
> Same for all 3 patches.
Thanks for the review.
Let me try to address your three responses in one reply.
I am happy to accept it if I am wrong about this, but I don't
understand your reasoning. (This should go without saying, but maybe
is worth reiterating, I would not have sent this if I had not
convinced myself independently of LLM-assisted reasoning.)
Let me explain my reasoning based on the place where .report_fixup()
is called from, which is hid_open_report() in hid-core.c:
start = device->bpf_rdesc; /* (1) */
if (WARN_ON(!start))
return -ENODEV;
size = device->bpf_rsize;
if (device->driver->report_fixup) {
/*
* device->driver->report_fixup() needs to work
* on a copy of our report descriptor so it can
* change it.
*/
__u8 *buf = kmemdup(start, size, GFP_KERNEL); /* (2) */
if (buf == NULL)
return -ENOMEM;
start = device->driver->report_fixup(device, buf, &size); /* (3) */
/*
* The second kmemdup is required in case report_fixup() returns
* a static read-only memory, but we have no idea if that memory
* needs to be cleaned up or not at the end.
*/
start = kmemdup(start, size, GFP_KERNEL); /* (4) */
kfree(buf); /* (5) */
if (start == NULL)
return -ENOMEM;
}
device->rdesc = start;
device->rsize = size;
This function uses a slightly elaborate scheme to call .report_fixup:
(1) start is assigned to the original device->bpf_rdesc
(2) buf is assigned to a copy of the 'start' buffer (deallocated in (5)).
. It is done because buf is meant to be mutated by .report_fixup()
. (3) start = ...->report_fixup(..., buf, ...)
. (4) start = kmemdup(start, ...)
(5) deallocate buf
Importantly:
(a) The buffer buf passed to report_fixup() is a copy of the report
descriptor whose lifetime spans only from (2) to (5).
(b) The result of .report_fixup(), start, is immediately discarded in
(4) and reassigned to the start variable again.
From (b), we can see that the result of .report_fixup() does *not* get
deallocated by the caller, and thus, when the driver wants to return a
newly allocated array, is must also hold a reference to it so that it
can deallocate it later.
From (a), we can see that the report_fixup hook is free to manipulate
the contents of the buffer that it receives, but if we were to
*reallocate* it within report_fixup, as you are suggesting above, it
could become a double free:
* During realloc, the allocator would potentially have to move the
buffer to a place where there is enough space, freeing up the old
place and allocating a new place. [1]
* In (5), we would then pass the original (now deallocated) buf
pointer to kfree, leading to a double free.
If I were to describe the current interface of the hook
.report_fixup(dev, rdesc, rsize), it would be:
* report_fixup may modify rdesc[0] to rdesc[rsize-1]
* report_fixup may *not* deallocate rdesc
(ownership of rdesc stays with the caller)
* specifically, it may also not reallocate rdesc
(because that may imply a deallocation)
* report_fixup returns a pointer to a buffer for which it can
guarantee that it lives long enough for the kmemdup in (4), but
which will *not* be deallocated by the caller (see (b) above). The
three techniques I have found for that are:
* returning a subsection of the rdesc that it received
* returning a pointer into a statically allocated array
(e.g. motion_fixup() and ps3remote_fixup() in hid-sony.c)
* allocating it with a devm_*() function. My understanding was
that this ties it to the lifetime of the device. (e.g. the
QUIRK_G752_KEYBOARD case in hid-asus.c)
Honestly, I still think that this reasoning holds, but I am happy to
be convinced otherwise. Please let me know what you think.
—Günther
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 3/3] HID: asus: avoid memory leak in asus_report_fixup()
2026-02-17 18:31 ` Benjamin Tissoires
@ 2026-02-17 19:51 ` Günther Noack
0 siblings, 0 replies; 12+ messages in thread
From: Günther Noack @ 2026-02-17 19:51 UTC (permalink / raw)
To: Benjamin Tissoires; +Cc: Jiri Kosina, linux-input, linux-kernel
Hello!
On Tue, Feb 17, 2026 at 07:31:23PM +0100, Benjamin Tissoires wrote:
> On Feb 17 2026, Günther Noack wrote:
> > The asus_report_fixup() function was allocating a new buffer with kmemdup()
> > when growing the report descriptor but never freeing it. Switch to
> > devm_kzalloc() to ensure the memory is managed and freed automatically when
> > the device is removed.
>
> Actually this one is even worse: you can't use devm_kzalloc because
> hid-core.c will later call kfree(dev->rdesc) if dev->rdesc is different
> from the one provided by the low level driver. So we are going to have
> a double free.
The buffer returned by report_fixup() is duplicated first before
hid-core stores it in dev->rdesc. The pointer that report_fixup()
returns is not managed by the caller.
I elaborated in the response to the other patch in [1]. You can see
it in the source code in the position marked with (4).
[1] https://lore.kernel.org/all/aZTEnPEHcWEkoTJR@google.com/
> I really wonder if this was ever tested.
I only convinced myself by staring at the code, because I do not
happen to have the matching USB devices here. What it your usual
approach to verifying such changes? raw-gadget?
—Günther
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 0/3] HID: Fix some memory leaks in drivers/hid
2026-02-17 18:36 ` [PATCH 0/3] HID: Fix some memory leaks in drivers/hid Benjamin Tissoires
@ 2026-02-17 20:08 ` Günther Noack
0 siblings, 0 replies; 12+ messages in thread
From: Günther Noack @ 2026-02-17 20:08 UTC (permalink / raw)
To: Benjamin Tissoires; +Cc: Jiri Kosina, linux-input, linux-kernel
Hello!
On Tue, Feb 17, 2026 at 07:36:46PM +0100, Benjamin Tissoires wrote:
> On Feb 17 2026, Günther Noack wrote:
> > These patches fix a few memory leaks in HID report descriptor fixups.
> >
> > FWIW, a good ad-hoc way to look for usages of allocation functions in
> > these is:
> >
> > awk '/static.*report_fixup.*/,/^}/ { print FILENAME, $0 }' drivers/hid/hid-*.c \
> > | grep -E '(malloc|kzalloc|kcalloc|kmemdup)'
> >
> > The devm_* variants are safe in this context, because they tie the
> > allocated memory to the lifetime of the driver.
>
> No. Look at hid_close_report() in drivers/hid/hid-core.c.
>
> HID still hasn't fully migrated to devm, so as a rule of thumb, if you
> change a kzalloc into a devm_kzalloc, you are getting into troubles
> unless you fix the all the kfree path.
OK, I have not verified where the devm-allocated objects get freed up.
If devm_*() is not possible here, then the drivers hid-asus.c and
hid-gembird.c have two additional memory leaks, because they do that.
$ awk '/static.*report_fixup.*/,/^}/ { print FILENAME, $0 }' drivers/hid/hid-*.c | grep -E 'alloc'
drivers/hid/hid-asus.c new_rdesc = devm_kzalloc(&hdev->dev, new_size, GFP_KERNEL);
drivers/hid/hid-gembird.c new_rdesc = devm_kzalloc(&hdev->dev, new_size, GFP_KERNEL);
(That is without my threee patches)
> > For transparency, I generated these commits with Gemini-CLI,
> > starting with this prompt:
> >
> > We are working in the Linux kernel. In the HID drivers in
> > `drivers/hid/hid-*.c`, the `report_fixup` driver hook is a function
> > that gets a byte buffer (with size) as input and that may modify that
> > byte buffer, and optionally return a pointer to a new byte buffer and
> > update the size. The returned value is *not* memory-managed by the
> > caller though and will not be freed subsequently. When the
>
> If the memory is *not* managed, why would gemini converts kzalloc into
> devm variants without changing the kfree paths????
I'm not sure I understand the question, it's not clear to me what you
mean by the "kfree paths".
I have seen usages of devm in other HID drivers and I was under the
impression that devm_* allocations would work in the HID subsystem to
allocate objects which are then freed automatically at a later point
when the device gets removed. Is that inaccurate?
> > `report_fixup` implementation allocates a new buffer and returns that,
> > that will not get freed by the caller.
>
> This is wrong. See hid_close_report(): if the new rdesc (after fixup)
> differs from the one initially set, there is an explicit call to
> kfree().
>
> -> there is no memleak AFAICT, and your prompt is wrong.
See my discussion in [1]. The pointer returned by report_fixup() is
immediately discarded in the position marked with (4). This is still
in the hid_open_report() function where the leak happens.
Let me know whether this makes sense. I'm happy to be corrected, but
so far, I still have the feeling that my reasoning is sound.
—Günther
[1] https://lore.kernel.org/all/aZTEnPEHcWEkoTJR@google.com/
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 1/3] HID: apple: avoid memory leak in apple_report_fixup()
2026-02-17 19:42 ` Günther Noack
@ 2026-02-18 19:04 ` Benjamin Tissoires
2026-02-19 15:47 ` Günther Noack
0 siblings, 1 reply; 12+ messages in thread
From: Benjamin Tissoires @ 2026-02-18 19:04 UTC (permalink / raw)
To: Günther Noack; +Cc: Jiri Kosina, linux-input, linux-kernel
On Feb 17 2026, Günther Noack wrote:
> Hello Benjamin!
>
> On Tue, Feb 17, 2026 at 07:22:20PM +0100, Benjamin Tissoires wrote:
> > On Feb 17 2026, Günther Noack wrote:
> > > The apple_report_fixup() function was allocating a new buffer with
> > > kmemdup() but never freeing it. Since the caller of report_fixup() already
> > > provides a writable buffer and allows returning a pointer within that
> > > buffer, we can just modify the descriptor in-place and return the adjusted
> > > pointer.
> > >
> > > Assisted-by: Gemini-CLI:Google Gemini 3
> > > Signed-off-by: Günther Noack <gnoack@google.com>
> > > ---
> > > drivers/hid/hid-apple.c | 4 +---
> > > 1 file changed, 1 insertion(+), 3 deletions(-)
> > >
> > > diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c
> > > index 233e367cce1d..894adc23367b 100644
> > > --- a/drivers/hid/hid-apple.c
> > > +++ b/drivers/hid/hid-apple.c
> > > @@ -686,9 +686,7 @@ static const __u8 *apple_report_fixup(struct hid_device *hdev, __u8 *rdesc,
> > > hid_info(hdev,
> > > "fixing up Magic Keyboard battery report descriptor\n");
> > > *rsize = *rsize - 1;
> > > - rdesc = kmemdup(rdesc + 1, *rsize, GFP_KERNEL);
> > > - if (!rdesc)
> > > - return NULL;
> > > + rdesc = rdesc + 1;
> >
> > I might be wrong, but later we call free(dev->rdesc) on device removal.
> > AFAICT, incrementing the pointer is undefined behavior.
> >
> > What we should do instead is probably a krealloc instead of a kmemdup.
> >
> > Same for all 3 patches.
>
> Thanks for the review.
>
> Let me try to address your three responses in one reply.
>
> I am happy to accept it if I am wrong about this, but I don't
> understand your reasoning. (This should go without saying, but maybe
> is worth reiterating, I would not have sent this if I had not
> convinced myself independently of LLM-assisted reasoning.)
>
> Let me explain my reasoning based on the place where .report_fixup()
> is called from, which is hid_open_report() in hid-core.c:
>
>
> start = device->bpf_rdesc; /* (1) */
> if (WARN_ON(!start))
> return -ENODEV;
> size = device->bpf_rsize;
>
> if (device->driver->report_fixup) {
> /*
> * device->driver->report_fixup() needs to work
> * on a copy of our report descriptor so it can
> * change it.
> */
> __u8 *buf = kmemdup(start, size, GFP_KERNEL); /* (2) */
>
> if (buf == NULL)
> return -ENOMEM;
>
> start = device->driver->report_fixup(device, buf, &size); /* (3) */
>
> /*
> * The second kmemdup is required in case report_fixup() returns
> * a static read-only memory, but we have no idea if that memory
> * needs to be cleaned up or not at the end.
> */
> start = kmemdup(start, size, GFP_KERNEL); /* (4) */
> kfree(buf); /* (5) */
> if (start == NULL)
> return -ENOMEM;
> }
>
> device->rdesc = start;
> device->rsize = size;
>
>
> This function uses a slightly elaborate scheme to call .report_fixup:
>
> (1) start is assigned to the original device->bpf_rdesc
> (2) buf is assigned to a copy of the 'start' buffer (deallocated in (5)).
> . It is done because buf is meant to be mutated by .report_fixup()
> . (3) start = ...->report_fixup(..., buf, ...)
> . (4) start = kmemdup(start, ...)
> (5) deallocate buf
Ouch. Yeah, sorry. I wrote that code and it seemed I completely paged
it out. Your code is actually correct (all three) but it would be nice
to have a longer commit message explaining this above.
The main point of this alloc before calling fixup is because some
drivers are using a static array as the new report descriptor. So we can
not free it later on. Working on a known copy allows to handle the kfree
correctly.
So yes, sorry, returning rdesc+1 in 1/3 and 2/3 is correct, and using a
devm_kzalloc is too in 3/3.
Cheers,
Benjamin
>
> Importantly:
>
> (a) The buffer buf passed to report_fixup() is a copy of the report
> descriptor whose lifetime spans only from (2) to (5).
> (b) The result of .report_fixup(), start, is immediately discarded in
> (4) and reassigned to the start variable again.
>
> From (b), we can see that the result of .report_fixup() does *not* get
> deallocated by the caller, and thus, when the driver wants to return a
> newly allocated array, is must also hold a reference to it so that it
> can deallocate it later.
>
> From (a), we can see that the report_fixup hook is free to manipulate
> the contents of the buffer that it receives, but if we were to
> *reallocate* it within report_fixup, as you are suggesting above, it
> could become a double free:
>
> * During realloc, the allocator would potentially have to move the
> buffer to a place where there is enough space, freeing up the old
> place and allocating a new place. [1]
> * In (5), we would then pass the original (now deallocated) buf
> pointer to kfree, leading to a double free.
>
> If I were to describe the current interface of the hook
> .report_fixup(dev, rdesc, rsize), it would be:
>
> * report_fixup may modify rdesc[0] to rdesc[rsize-1]
> * report_fixup may *not* deallocate rdesc
> (ownership of rdesc stays with the caller)
> * specifically, it may also not reallocate rdesc
> (because that may imply a deallocation)
> * report_fixup returns a pointer to a buffer for which it can
> guarantee that it lives long enough for the kmemdup in (4), but
> which will *not* be deallocated by the caller (see (b) above). The
> three techniques I have found for that are:
> * returning a subsection of the rdesc that it received
> * returning a pointer into a statically allocated array
> (e.g. motion_fixup() and ps3remote_fixup() in hid-sony.c)
> * allocating it with a devm_*() function. My understanding was
> that this ties it to the lifetime of the device. (e.g. the
> QUIRK_G752_KEYBOARD case in hid-asus.c)
>
> Honestly, I still think that this reasoning holds, but I am happy to
> be convinced otherwise. Please let me know what you think.
>
> —Günther
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 1/3] HID: apple: avoid memory leak in apple_report_fixup()
2026-02-18 19:04 ` Benjamin Tissoires
@ 2026-02-19 15:47 ` Günther Noack
0 siblings, 0 replies; 12+ messages in thread
From: Günther Noack @ 2026-02-19 15:47 UTC (permalink / raw)
To: Benjamin Tissoires; +Cc: Jiri Kosina, linux-input, linux-kernel
On Wed, Feb 18, 2026 at 08:04:52PM +0100, Benjamin Tissoires wrote:
> Ouch. Yeah, sorry. I wrote that code and it seemed I completely paged
> it out. Your code is actually correct (all three) but it would be nice
> to have a longer commit message explaining this above.
>
> The main point of this alloc before calling fixup is because some
> drivers are using a static array as the new report descriptor. So we can
> not free it later on. Working on a known copy allows to handle the kfree
> correctly.
>
> So yes, sorry, returning rdesc+1 in 1/3 and 2/3 is correct, and using a
> devm_kzalloc is too in 3/3.
Ah OK, thanks for the review! I sent you an updated version where I
am trying to express it more clearly in the commit messages.
I also added a commit that documents the expected allocation
properties in hid.h where the .report_fixup() field is defined (feel
free to ignore this one, if it feels like it's too much).
Link to V2:
https://lore.kernel.org/all/20260219154338.786625-2-gnoack@google.com/
—Günther
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2026-02-19 15:47 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-02-17 16:01 [PATCH 0/3] HID: Fix some memory leaks in drivers/hid Günther Noack
2026-02-17 16:01 ` [PATCH 1/3] HID: apple: avoid memory leak in apple_report_fixup() Günther Noack
2026-02-17 18:22 ` Benjamin Tissoires
2026-02-17 19:42 ` Günther Noack
2026-02-18 19:04 ` Benjamin Tissoires
2026-02-19 15:47 ` Günther Noack
2026-02-17 16:01 ` [PATCH 2/3] HID: magicmouse: avoid memory leak in magicmouse_report_fixup() Günther Noack
2026-02-17 16:01 ` [PATCH 3/3] HID: asus: avoid memory leak in asus_report_fixup() Günther Noack
2026-02-17 18:31 ` Benjamin Tissoires
2026-02-17 19:51 ` Günther Noack
2026-02-17 18:36 ` [PATCH 0/3] HID: Fix some memory leaks in drivers/hid Benjamin Tissoires
2026-02-17 20:08 ` Günther Noack
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox