Files
qemu/hw/core/qdev-clock.c
hemanshu.khilari.foss 7913b3b946 hw/core/qdev-clock: Fix potential null pointer dereference
qdev_get_clocklist() function returns a pointer to the NamedClockList
struct. This function is called in qdev_alias_clock() and the returned
pointer is immediately dereferenced without a null check.

Passing a clock name that doesn't exist to qdev_get_clocklist() is a
programming error, and so this change is not fixing a bug, only making
the reporting of that programming error a bit more helpful and bringing
it in to line with qdev_get_clock_in() and qdev_get_clock_out().

Cc: luc@lmichel.fr
Cc: peter.maydell@linaro.org
Cc: hemanshu_dev@proton.me
Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/2342
Signed-off-by: hemanshu.khilari.foss <hemanshu.khilari.foss@gmail.com>
Message-id: 20260531153354.88909-2-hemanshu.khilari.foss@gmail.com
Reviewed-by: Luc Michel <luc@lmichel.fr>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
2026-06-10 15:42:07 +01:00

192 lines
5.2 KiB
C

/*
* Device's clock input and output
*
* Copyright GreenSocs 2016-2020
*
* Authors:
* Frederic Konrad
* Damien Hedde
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "qemu/error-report.h"
#include "hw/core/qdev-clock.h"
#include "hw/core/qdev.h"
#include "qapi/error.h"
/*
* qdev_init_clocklist:
* Add a new clock in a device
*/
static NamedClockList *qdev_init_clocklist(DeviceState *dev, const char *name,
bool alias, bool output, Clock *clk)
{
NamedClockList *ncl;
/*
* Clock must be added before realize() so that we can compute the
* clock's canonical path during device_realize().
*/
assert(!dev->realized);
/*
* The ncl structure is freed by qdev_finalize_clocklist() which will
* be called during @dev's device_finalize().
*/
ncl = g_new0(NamedClockList, 1);
ncl->name = g_strdup(name);
ncl->alias = alias;
ncl->output = output;
ncl->clock = clk;
QLIST_INSERT_HEAD(&dev->clocks, ncl, node);
return ncl;
}
void qdev_finalize_clocklist(DeviceState *dev)
{
/* called by @dev's device_finalize() */
NamedClockList *ncl, *ncl_next;
QLIST_FOREACH_SAFE(ncl, &dev->clocks, node, ncl_next) {
QLIST_REMOVE(ncl, node);
if (!ncl->alias) {
/*
* We kept a reference on the input clock to ensure it lives up to
* this point; it is used by the monitor to show the frequency.
*/
object_unref(OBJECT(ncl->clock));
}
g_free(ncl->name);
g_free(ncl);
}
}
Clock *qdev_init_clock_out(DeviceState *dev, const char *name)
{
Clock *clk = CLOCK(object_new(TYPE_CLOCK));
object_property_add_child(OBJECT(dev), name, OBJECT(clk));
qdev_init_clocklist(dev, name, false, true, clk);
return clk;
}
Clock *qdev_init_clock_in(DeviceState *dev, const char *name,
ClockCallback *callback, void *opaque,
unsigned int events)
{
Clock *clk = CLOCK(object_new(TYPE_CLOCK));
object_property_add_child(OBJECT(dev), name, OBJECT(clk));
qdev_init_clocklist(dev, name, false, false, clk);
if (callback) {
clock_set_callback(clk, callback, opaque, events);
}
return clk;
}
void qdev_init_clocks(DeviceState *dev, const ClockPortInitArray clocks)
{
const struct ClockPortInitElem *elem;
for (elem = &clocks[0]; elem->name != NULL; elem++) {
Clock **clkp;
/* offset cannot be inside the DeviceState part */
assert(elem->offset > sizeof(DeviceState));
clkp = ((void *)dev) + elem->offset;
if (elem->is_output) {
*clkp = qdev_init_clock_out(dev, elem->name);
} else {
*clkp = qdev_init_clock_in(dev, elem->name, elem->callback, dev,
elem->callback_events);
}
}
}
static NamedClockList *qdev_get_clocklist(DeviceState *dev, const char *name)
{
NamedClockList *ncl;
QLIST_FOREACH(ncl, &dev->clocks, node) {
if (strcmp(name, ncl->name) == 0) {
return ncl;
}
}
return NULL;
}
Clock *qdev_get_clock_in(DeviceState *dev, const char *name)
{
NamedClockList *ncl;
assert(name);
ncl = qdev_get_clocklist(dev, name);
if (!ncl) {
error_report("Can not find clock-in '%s' for device type '%s'",
name, object_get_typename(OBJECT(dev)));
abort();
}
assert(!ncl->output);
return ncl->clock;
}
Clock *qdev_get_clock_out(DeviceState *dev, const char *name)
{
NamedClockList *ncl;
assert(name);
ncl = qdev_get_clocklist(dev, name);
if (!ncl) {
error_report("Can not find clock-out '%s' for device type '%s'",
name, object_get_typename(OBJECT(dev)));
abort();
}
assert(ncl->output);
return ncl->clock;
}
Clock *qdev_alias_clock(DeviceState *dev, const char *name,
DeviceState *alias_dev, const char *alias_name)
{
NamedClockList *ncl = qdev_get_clocklist(dev, name);
Clock *clk;
if (!ncl) {
error_report("Can not find clock '%s' for device type '%s'",
name, object_get_typename(OBJECT(dev)));
abort();
}
clk = ncl->clock;
ncl = qdev_init_clocklist(alias_dev, alias_name, true, ncl->output, clk);
object_property_add_link(OBJECT(alias_dev), alias_name,
TYPE_CLOCK,
(Object **) &ncl->clock,
NULL, OBJ_PROP_LINK_STRONG);
/*
* Since the link property has the OBJ_PROP_LINK_STRONG flag, the clk
* object reference count gets decremented on property deletion.
* However object_property_add_link does not increment it since it
* doesn't know the linked object. Increment it here to ensure the
* aliased clock stays alive during this device life-time.
*/
object_ref(OBJECT(clk));
return clk;
}
void qdev_connect_clock_in(DeviceState *dev, const char *name, Clock *source)
{
assert(!dev->realized);
clock_set_source(qdev_get_clock_in(dev, name), source);
}