Bug 226963 - DisplayList::ImageBuffer calls virtual function erroneously in destructor
Summary: DisplayList::ImageBuffer calls virtual function erroneously in destructor
Status: ASSIGNED
Alias: None
Product: WebKit
Classification: Unclassified
Component: Canvas (show other bugs)
Version: WebKit Nightly Build
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: Kimmo Kinnunen
URL:
Keywords: InRadar
Depends on:
Blocks: 226917
  Show dependency treegraph
 
Reported: 2021-06-14 03:23 PDT by Kimmo Kinnunen
Modified: 2022-02-19 08:16 PST (History)
5 users (show)

See Also:


Attachments
Patch (8.77 KB, patch)
2021-06-14 04:37 PDT, Kimmo Kinnunen
ews-feeder: commit-queue-
Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Kimmo Kinnunen 2021-06-14 03:23:29 PDT
ConcreteImageBuffer calls virtual function erroneously in destructor

Calls flushDrawingContext().

Virtual function call ends up calling the currently destructed class version of the function, not the final overridden function.

In case of all ConcreteImageBuffer inheritance chains, this is semi-surprising behavioral difference in case of RemoteImageBufferProxy which overrides flush.
The code that is produced is incorrect as per logic.
As it stands today, the code doesn't crash as the ~RemoteImageBufferProxy flushes and that causes early-out in ConcreteImageBuffer::flushDrawingContext().

However, if ~RemoteImageBufferProxy does not cause the early out, it appears that the ConcreteImageBuffer::flushDrawingContext() seems to crash in a non-trivial convoluted way.

As this is surprising and typically incorrect pattern, it should be mostly not done.
Comment 1 Kimmo Kinnunen 2021-06-14 03:28:00 PDT
Correction: DisplayList::ImageBuffer, not ConcreteImageBuffer, calls the virtual function.
Comment 2 Kimmo Kinnunen 2021-06-14 04:37:31 PDT
Created attachment 431309 [details]
Patch
Comment 3 Radar WebKit Bug Importer 2021-06-21 03:24:31 PDT
<rdar://problem/79554863>
Comment 4 Said Abou-Hallawa 2021-06-21 14:46:04 PDT
Comment on attachment 431309 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=431309&action=review

> Source/WebCore/ChangeLog:27
> +        Make the destructor public because it'd be surprising that the instances
> +        are intended to be holdable via `ImageBuffer` reference and via the final class
> +        references but not via the intermediate class references.

I do not understand what this statement means. Also I think this destructor of DisplayList::ImageBuffer can be protected now.

> Source/WebCore/platform/graphics/PlatformImageBuffer.h:87
> +class DisplayListUnacceleratedImageBuffer final : public DisplayList::ImageBuffer<UnacceleratedImageBufferBackend> {
> +    using Base = DisplayList::ImageBuffer<UnacceleratedImageBufferBackend>;
> +    using Base::Base;
> +public:
> +    static auto create(const WebCore::FloatSize& size, float resolutionScale, const WebCore::DestinationColorSpace& colorSpace, WebCore::PixelFormat pixelFormat, const HostWindow* hostWindow = nullptr)
> +    {
> +        return Base::create<DisplayListUnacceleratedImageBuffer>(size, resolutionScale, colorSpace, pixelFormat, hostWindow);
> +    }
> +    ~DisplayListUnacceleratedImageBuffer() final
> +    {
> +        flushDrawingContext();
> +    }
> +};
> +
> +class DisplayListAcceleratedImageBuffer final : public DisplayList::ImageBuffer<AcceleratedImageBufferBackend> {
> +    using Base = DisplayList::ImageBuffer<AcceleratedImageBufferBackend>;
> +    using Base::Base;
> +public:
> +    static auto create(const WebCore::FloatSize& size, float resolutionScale, const WebCore::DestinationColorSpace& colorSpace, WebCore::PixelFormat pixelFormat, const HostWindow* hostWindow = nullptr)
> +    {
> +        return Base::create<DisplayListAcceleratedImageBuffer>(size, resolutionScale, colorSpace, pixelFormat, hostWindow);
> +    }
> +    ~DisplayListAcceleratedImageBuffer() final
> +    {
> +        flushDrawingContext();
> +    }
> +};

This repeated code can be simplified by using templates:

// maybe in a new file or in DisplayListImageBuffer.h
namespace WebCore {
namespace DisplayList {

template<typename BackendType>
class ConcreteImageBuffer final : public ImageBuffer<BackendType> {
    using Base = ImageBuffer<BackendType>;
    using Base::Base;
public:
    static auto create(const WebCore::FloatSize& size, float resolutionScale, const WebCore::DestinationColorSpace& colorSpace, WebCore::PixelFormat pixelFormat, const HostWindow* hostWindow = nullptr)
    {
        return Base::template create<ConcreteImageBuffer>(size, resolutionScale, colorSpace, pixelFormat, hostWindow);
    }
    
    ~ConcreteImageBuffer() final
    {
        Base::flushDrawingContext();
    }
};

} // DisplayList
} // namespace WebCore

// And in this file:
using DisplayListUnacceleratedImageBuffer = DisplayList::ConcreteImageBuffer<UnacceleratedImageBufferBackend>;
using DisplayListAcceleratedImageBuffer = DisplayList::ConcreteImageBuffer<AcceleratedImageBufferBackend>;

> Source/WebCore/platform/graphics/displaylists/DisplayListImageBuffer.h:63
> -    ~ImageBuffer()
> +    ~ImageBuffer() override

What 'override' adds here? I am not sure what is the webkit coding style for overriding virtual destructor. I found most of the classes add virtual before the overridden destructor, e.g. BitmapImage, RenderLayerModelObject, RenderImage, CachedImage. So following this pattern this code should be:

    virtual ~ImageBuffer() { ... }

I also think this destructor can be protected now.

> Source/WebKit/WebProcess/GPU/graphics/RemoteImageBufferProxy.h:66
> -    ~RemoteImageBufferProxy()
> +    ~RemoteImageBufferProxy() override

I think this should be 

virtual ~RemoteImageBufferProxy() { ... }
Comment 5 Dean Jackson 2022-02-19 08:16:05 PST
Comment on attachment 431309 [details]
Patch

Clearing old review flag. Please rebase and mark for review if necessary.