Message ID | 20221124113023.4121023-1-liaoyu15@huawei.com |
---|---|
State | New |
Headers | show |
Series | drm/nouveau/acpi: Fix memory leak in nouveau_acpi_edid() | expand |
On Thu, Nov 24, 2022 at 12:33 PM Yu Liao <liaoyu15@huawei.com> wrote: > > The ACPI buffer memory 'edid' should be freed as the buffer is not used > after kmemdup(). But we can't free 'edid' directly because it doesn't > point to acpi_object which should be passed to kfree(). Make > acpi_video_get_edid() get the address of acpi_object instead, so we can > free it to prevent memory leak. > > Fixes: 24b102d3488c ("drm/nouveau: we can't free ACPI EDID, so make a copy that we can") > Reported-by: Hanjun Guo <guohanjun@huawei.com> > Signed-off-by: Yu Liao <liaoyu15@huawei.com> > --- > drivers/acpi/acpi_video.c | 4 ++-- > drivers/gpu/drm/nouveau/nouveau_acpi.c | 8 ++++++-- > include/acpi/video.h | 5 +++-- > 3 files changed, 11 insertions(+), 6 deletions(-) > > diff --git a/drivers/acpi/acpi_video.c b/drivers/acpi/acpi_video.c > index 32953646caeb..f050a755efef 100644 > --- a/drivers/acpi/acpi_video.c > +++ b/drivers/acpi/acpi_video.c > @@ -1441,7 +1441,7 @@ acpi_video_switch_brightness(struct work_struct *work) > } > > int acpi_video_get_edid(struct acpi_device *device, int type, int device_id, > - void **edid) > + union acpi_object **edid) I don't think you need to make this change. > { > struct acpi_video_bus *video; > struct acpi_video_device *video_device; > @@ -1500,7 +1500,7 @@ int acpi_video_get_edid(struct acpi_device *device, int type, int device_id, > } > } > > - *edid = buffer->buffer.pointer; > + *edid = buffer; Because this would still be valid without the previous one. However, why can't you just free the buffer after storing the buffer.pointer value under the address pointed to by edid? > return length; > } > > diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.c b/drivers/gpu/drm/nouveau/nouveau_acpi.c > index 8cf096f841a9..075ecad31572 100644 > --- a/drivers/gpu/drm/nouveau/nouveau_acpi.c > +++ b/drivers/gpu/drm/nouveau/nouveau_acpi.c > @@ -365,7 +365,8 @@ nouveau_acpi_edid(struct drm_device *dev, struct drm_connector *connector) > { > struct acpi_device *acpidev; > int type, ret; > - void *edid; > + union acpi_object *edid; > + void *ptr; > > switch (connector->connector_type) { > case DRM_MODE_CONNECTOR_LVDS: > @@ -384,7 +385,10 @@ nouveau_acpi_edid(struct drm_device *dev, struct drm_connector *connector) > if (ret < 0) > return NULL; > > - return kmemdup(edid, EDID_LENGTH, GFP_KERNEL); > + ptr = kmemdup(edid->buffer.pointer, EDID_LENGTH, GFP_KERNEL); Here you ccould do ptr = kmemdup(((union acpi_object *)edid)->buffer.pointer, EDID_LENGTH, GFP_KERNEL); > + kfree(edid); But anyway I don't think you need to defer freeing the buffer allocated by acpi_video_get_edid() till this point. > + > + return ptr; > } > > bool nouveau_acpi_video_backlight_use_native(void) > diff --git a/include/acpi/video.h b/include/acpi/video.h > index a275c35e5249..868749f95a34 100644 > --- a/include/acpi/video.h > +++ b/include/acpi/video.h > @@ -19,6 +19,7 @@ struct acpi_video_device_brightness { > }; > > struct acpi_device; > +union acpi_object; > > #define ACPI_VIDEO_CLASS "video" > > @@ -57,7 +58,7 @@ extern int acpi_video_register(void); > extern void acpi_video_unregister(void); > extern void acpi_video_register_backlight(void); > extern int acpi_video_get_edid(struct acpi_device *device, int type, > - int device_id, void **edid); > + int device_id, union acpi_object **edid); > extern enum acpi_backlight_type acpi_video_get_backlight_type(void); > extern bool acpi_video_backlight_use_native(void); > /* > @@ -73,7 +74,7 @@ static inline int acpi_video_register(void) { return -ENODEV; } > static inline void acpi_video_unregister(void) { return; } > static inline void acpi_video_register_backlight(void) { return; } > static inline int acpi_video_get_edid(struct acpi_device *device, int type, > - int device_id, void **edid) > + int device_id, union acpi_object **edid) > { > return -ENODEV; > } > -- > 2.25.1 >
diff --git a/drivers/acpi/acpi_video.c b/drivers/acpi/acpi_video.c index 32953646caeb..f050a755efef 100644 --- a/drivers/acpi/acpi_video.c +++ b/drivers/acpi/acpi_video.c @@ -1441,7 +1441,7 @@ acpi_video_switch_brightness(struct work_struct *work) } int acpi_video_get_edid(struct acpi_device *device, int type, int device_id, - void **edid) + union acpi_object **edid) { struct acpi_video_bus *video; struct acpi_video_device *video_device; @@ -1500,7 +1500,7 @@ int acpi_video_get_edid(struct acpi_device *device, int type, int device_id, } } - *edid = buffer->buffer.pointer; + *edid = buffer; return length; } diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.c b/drivers/gpu/drm/nouveau/nouveau_acpi.c index 8cf096f841a9..075ecad31572 100644 --- a/drivers/gpu/drm/nouveau/nouveau_acpi.c +++ b/drivers/gpu/drm/nouveau/nouveau_acpi.c @@ -365,7 +365,8 @@ nouveau_acpi_edid(struct drm_device *dev, struct drm_connector *connector) { struct acpi_device *acpidev; int type, ret; - void *edid; + union acpi_object *edid; + void *ptr; switch (connector->connector_type) { case DRM_MODE_CONNECTOR_LVDS: @@ -384,7 +385,10 @@ nouveau_acpi_edid(struct drm_device *dev, struct drm_connector *connector) if (ret < 0) return NULL; - return kmemdup(edid, EDID_LENGTH, GFP_KERNEL); + ptr = kmemdup(edid->buffer.pointer, EDID_LENGTH, GFP_KERNEL); + kfree(edid); + + return ptr; } bool nouveau_acpi_video_backlight_use_native(void) diff --git a/include/acpi/video.h b/include/acpi/video.h index a275c35e5249..868749f95a34 100644 --- a/include/acpi/video.h +++ b/include/acpi/video.h @@ -19,6 +19,7 @@ struct acpi_video_device_brightness { }; struct acpi_device; +union acpi_object; #define ACPI_VIDEO_CLASS "video" @@ -57,7 +58,7 @@ extern int acpi_video_register(void); extern void acpi_video_unregister(void); extern void acpi_video_register_backlight(void); extern int acpi_video_get_edid(struct acpi_device *device, int type, - int device_id, void **edid); + int device_id, union acpi_object **edid); extern enum acpi_backlight_type acpi_video_get_backlight_type(void); extern bool acpi_video_backlight_use_native(void); /* @@ -73,7 +74,7 @@ static inline int acpi_video_register(void) { return -ENODEV; } static inline void acpi_video_unregister(void) { return; } static inline void acpi_video_register_backlight(void) { return; } static inline int acpi_video_get_edid(struct acpi_device *device, int type, - int device_id, void **edid) + int device_id, union acpi_object **edid) { return -ENODEV; }
The ACPI buffer memory 'edid' should be freed as the buffer is not used after kmemdup(). But we can't free 'edid' directly because it doesn't point to acpi_object which should be passed to kfree(). Make acpi_video_get_edid() get the address of acpi_object instead, so we can free it to prevent memory leak. Fixes: 24b102d3488c ("drm/nouveau: we can't free ACPI EDID, so make a copy that we can") Reported-by: Hanjun Guo <guohanjun@huawei.com> Signed-off-by: Yu Liao <liaoyu15@huawei.com> --- drivers/acpi/acpi_video.c | 4 ++-- drivers/gpu/drm/nouveau/nouveau_acpi.c | 8 ++++++-- include/acpi/video.h | 5 +++-- 3 files changed, 11 insertions(+), 6 deletions(-)