[server] Prevent double delete of callback resources in SurfaceInterface
Summary: When destroying a SurfaceInterface all callbacks are getting destroyed. This used to iterate over the callbacks and performing wl_resource_destroy on them. This triggered the destroy handler which removes the resource from the callback list. Which means removing from the list we are iterating on. This could result in a double delete or accessing invalid memory. This change copies all callbacks to a temporary list and clears the normal lists. So the destroy handler does no longer modify the lists currently being iterated on. Test Plan: Added a test case which crashed with previous code Reviewers: #plasma Subscribers: plasma-devel Tags: #plasma Differential Revision: https://phabricator.kde.org/D1677
This commit is contained in:
parent
46cf9fc36c
commit
d1a09838e1
2 changed files with 51 additions and 8 deletions
|
@ -58,6 +58,7 @@ private Q_SLOTS:
|
||||||
void testDamageTracking();
|
void testDamageTracking();
|
||||||
void testSurfaceAt();
|
void testSurfaceAt();
|
||||||
void testDestroyAttachedBuffer();
|
void testDestroyAttachedBuffer();
|
||||||
|
void testDestroyWithPendingCallback();
|
||||||
void testDisconnect();
|
void testDisconnect();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -874,6 +875,44 @@ void TestWaylandSurface::testDestroyAttachedBuffer()
|
||||||
QVERIFY(!serverSurface->buffer());
|
QVERIFY(!serverSurface->buffer());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void TestWaylandSurface::testDestroyWithPendingCallback()
|
||||||
|
{
|
||||||
|
// this test tries to verify that destroying a surface with a pending callback works correctly
|
||||||
|
// first create surface
|
||||||
|
using namespace KWayland::Client;
|
||||||
|
using namespace KWayland::Server;
|
||||||
|
QScopedPointer<Surface> s(m_compositor->createSurface());
|
||||||
|
QVERIFY(!s.isNull());
|
||||||
|
QVERIFY(s->isValid());
|
||||||
|
QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated);
|
||||||
|
QVERIFY(surfaceCreatedSpy.isValid());
|
||||||
|
QVERIFY(surfaceCreatedSpy.wait());
|
||||||
|
auto serverSurface = surfaceCreatedSpy.first().first().value<SurfaceInterface*>();
|
||||||
|
QVERIFY(serverSurface);
|
||||||
|
|
||||||
|
// now render to it
|
||||||
|
QImage img(QSize(10, 10), QImage::Format_ARGB32);
|
||||||
|
img.fill(Qt::black);
|
||||||
|
auto b = m_shm->createBuffer(img);
|
||||||
|
s->attachBuffer(b);
|
||||||
|
s->damage(QRect(0, 0, 10, 10));
|
||||||
|
// add some frame callbacks
|
||||||
|
for (int i = 0; i < 1000; i++) {
|
||||||
|
wl_surface_frame(*s);
|
||||||
|
}
|
||||||
|
s->commit(KWayland::Client::Surface::CommitFlag::FrameCallback);
|
||||||
|
QSignalSpy damagedSpy(serverSurface, &SurfaceInterface::damaged);
|
||||||
|
QVERIFY(damagedSpy.isValid());
|
||||||
|
QVERIFY(damagedSpy.wait());
|
||||||
|
|
||||||
|
// now try to destroy the Surface again
|
||||||
|
QSignalSpy destroyedSpy(serverSurface, &QObject::destroyed);
|
||||||
|
QVERIFY(destroyedSpy.isValid());
|
||||||
|
s.reset();
|
||||||
|
QVERIFY(destroyedSpy.wait());
|
||||||
|
}
|
||||||
|
|
||||||
void TestWaylandSurface::testDisconnect()
|
void TestWaylandSurface::testDisconnect()
|
||||||
{
|
{
|
||||||
// this test verifies that the server side correctly tears down the resources when the client disconnects
|
// this test verifies that the server side correctly tears down the resources when the client disconnects
|
||||||
|
|
|
@ -219,14 +219,18 @@ void SurfaceInterface::frameRendered(quint32 msec)
|
||||||
|
|
||||||
void SurfaceInterface::Private::destroy()
|
void SurfaceInterface::Private::destroy()
|
||||||
{
|
{
|
||||||
for (wl_resource *c : current.callbacks) {
|
// copy all existing callbacks to new list and clear existing lists
|
||||||
wl_resource_destroy(c);
|
// the wl_resource_destroy on the callback resource goes into destroyFrameCallback
|
||||||
}
|
// which would modify the list we are iterating on
|
||||||
for (wl_resource *c : pending.callbacks) {
|
QList<wl_resource *> callbacksToDestroy;
|
||||||
wl_resource_destroy(c);
|
callbacksToDestroy << current.callbacks;
|
||||||
}
|
current.callbacks.clear();
|
||||||
for (wl_resource *c : subSurfacePending.callbacks) {
|
callbacksToDestroy << pending.callbacks;
|
||||||
wl_resource_destroy(c);
|
pending.callbacks.clear();
|
||||||
|
callbacksToDestroy << subSurfacePending.callbacks;
|
||||||
|
subSurfacePending.callbacks.clear();
|
||||||
|
for (auto it = callbacksToDestroy.constBegin(), end = callbacksToDestroy.constEnd(); it != end; it++) {
|
||||||
|
wl_resource_destroy(*it);
|
||||||
}
|
}
|
||||||
if (current.buffer) {
|
if (current.buffer) {
|
||||||
current.buffer->unref();
|
current.buffer->unref();
|
||||||
|
|
Loading…
Reference in a new issue