linux_dsm_epyc7002/drivers/gpu/drm/tidss/tidss_encoder.c
Tomi Valkeinen 9da67433f6 drm/tidss: fix crash related to accessing freed memory
tidss uses devm_kzalloc to allocate DRM plane, encoder and crtc objects.
This is not correct as the lifetime of those objects should be longer
than the underlying device's.

When unloading tidss module, the devm_kzalloc'ed objects have already
been freed when tidss_release() is called, and the driver will accesses
freed memory possibly causing a crash, a kernel WARN, or other undefined
behavior, and also KASAN will give a bug.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20200415092006.26675-1-tomi.valkeinen@ti.com
Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
2020-04-20 10:07:35 +03:00

97 lines
2.3 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
* Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
*/
#include <linux/export.h>
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_panel.h>
#include <drm/drm_of.h>
#include "tidss_crtc.h"
#include "tidss_drv.h"
#include "tidss_encoder.h"
static int tidss_encoder_atomic_check(struct drm_encoder *encoder,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
{
struct drm_device *ddev = encoder->dev;
struct tidss_crtc_state *tcrtc_state = to_tidss_crtc_state(crtc_state);
struct drm_display_info *di = &conn_state->connector->display_info;
struct drm_bridge *bridge;
bool bus_flags_set = false;
dev_dbg(ddev->dev, "%s\n", __func__);
/*
* Take the bus_flags from the first bridge that defines
* bridge timings, or from the connector's display_info if no
* bridge defines the timings.
*/
drm_for_each_bridge_in_chain(encoder, bridge) {
if (!bridge->timings)
continue;
tcrtc_state->bus_flags = bridge->timings->input_bus_flags;
bus_flags_set = true;
break;
}
if (!di->bus_formats || di->num_bus_formats == 0) {
dev_err(ddev->dev, "%s: No bus_formats in connected display\n",
__func__);
return -EINVAL;
}
// XXX any cleaner way to set bus format and flags?
tcrtc_state->bus_format = di->bus_formats[0];
if (!bus_flags_set)
tcrtc_state->bus_flags = di->bus_flags;
return 0;
}
static void tidss_encoder_destroy(struct drm_encoder *encoder)
{
drm_encoder_cleanup(encoder);
kfree(encoder);
}
static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
.atomic_check = tidss_encoder_atomic_check,
};
static const struct drm_encoder_funcs encoder_funcs = {
.destroy = tidss_encoder_destroy,
};
struct drm_encoder *tidss_encoder_create(struct tidss_device *tidss,
u32 encoder_type, u32 possible_crtcs)
{
struct drm_encoder *enc;
int ret;
enc = kzalloc(sizeof(*enc), GFP_KERNEL);
if (!enc)
return ERR_PTR(-ENOMEM);
enc->possible_crtcs = possible_crtcs;
ret = drm_encoder_init(&tidss->ddev, enc, &encoder_funcs,
encoder_type, NULL);
if (ret < 0) {
kfree(enc);
return ERR_PTR(ret);
}
drm_encoder_helper_add(enc, &encoder_helper_funcs);
dev_dbg(tidss->dev, "Encoder create done\n");
return enc;
}