Title: [278307] trunk/Source/WebCore
Revision
278307
Author
cdu...@apple.com
Date
2021-06-01 08:09:47 -0700 (Tue, 01 Jun 2021)

Log Message

Fix thread safety issues in WaveShaperProcessor
https://bugs.webkit.org/show_bug.cgi?id=226478

Reviewed by Youenn Fablet.

Adopt thread safety analysis annotations in WaveShaperProcessor and fix bugs
found by clang. In particular, the following issues were fixed:
- WaveShaperDSPKernel::latencyTime() was failing to grab the lock before accessing
  the WaveShaperProcessor's oversample on the rendering thread, even though
  oversample gets modified on the main thread.
- WaveShaperNode::propagatesSilence() was failing to grab the lock before accessing
  the WaveShaperProcessor's curve on the rendering thread, even though the curve
  gets modified on the main thread.

* Modules/webaudio/AudioBasicProcessorNode.h:
(WebCore::AudioBasicProcessorNode::processor const):
* Modules/webaudio/WaveShaperDSPKernel.cpp:
(WebCore::WaveShaperDSPKernel::process):
(WebCore::WaveShaperDSPKernel::processCurve):
(WebCore::WaveShaperDSPKernel::latencyTime const):
* Modules/webaudio/WaveShaperDSPKernel.h:
* Modules/webaudio/WaveShaperNode.cpp:
(WebCore::WaveShaperNode::create):
(WebCore::WaveShaperNode::setCurveForBindings):
(WebCore::WaveShaperNode::curveForBindings):
(WebCore::WaveShaperNode::setOversampleForBindings):
(WebCore::WaveShaperNode::oversampleForBindings const):
(WebCore::WaveShaperNode::propagatesSilence const):
* Modules/webaudio/WaveShaperNode.h:
* Modules/webaudio/WaveShaperNode.idl:
* Modules/webaudio/WaveShaperProcessor.cpp:
(WebCore::WaveShaperProcessor::setCurveForBindings):
(WebCore::WaveShaperProcessor::setOversampleForBindings):
(WebCore::WaveShaperProcessor::process):
* Modules/webaudio/WaveShaperProcessor.h:

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (278306 => 278307)


--- trunk/Source/WebCore/ChangeLog	2021-06-01 14:56:04 UTC (rev 278306)
+++ trunk/Source/WebCore/ChangeLog	2021-06-01 15:09:47 UTC (rev 278307)
@@ -1,5 +1,43 @@
 2021-06-01  Chris Dumez  <cdu...@apple.com>
 
+        Fix thread safety issues in WaveShaperProcessor
+        https://bugs.webkit.org/show_bug.cgi?id=226478
+
+        Reviewed by Youenn Fablet.
+
+        Adopt thread safety analysis annotations in WaveShaperProcessor and fix bugs
+        found by clang. In particular, the following issues were fixed:
+        - WaveShaperDSPKernel::latencyTime() was failing to grab the lock before accessing
+          the WaveShaperProcessor's oversample on the rendering thread, even though
+          oversample gets modified on the main thread.
+        - WaveShaperNode::propagatesSilence() was failing to grab the lock before accessing
+          the WaveShaperProcessor's curve on the rendering thread, even though the curve
+          gets modified on the main thread.
+
+        * Modules/webaudio/AudioBasicProcessorNode.h:
+        (WebCore::AudioBasicProcessorNode::processor const):
+        * Modules/webaudio/WaveShaperDSPKernel.cpp:
+        (WebCore::WaveShaperDSPKernel::process):
+        (WebCore::WaveShaperDSPKernel::processCurve):
+        (WebCore::WaveShaperDSPKernel::latencyTime const):
+        * Modules/webaudio/WaveShaperDSPKernel.h:
+        * Modules/webaudio/WaveShaperNode.cpp:
+        (WebCore::WaveShaperNode::create):
+        (WebCore::WaveShaperNode::setCurveForBindings):
+        (WebCore::WaveShaperNode::curveForBindings):
+        (WebCore::WaveShaperNode::setOversampleForBindings):
+        (WebCore::WaveShaperNode::oversampleForBindings const):
+        (WebCore::WaveShaperNode::propagatesSilence const):
+        * Modules/webaudio/WaveShaperNode.h:
+        * Modules/webaudio/WaveShaperNode.idl:
+        * Modules/webaudio/WaveShaperProcessor.cpp:
+        (WebCore::WaveShaperProcessor::setCurveForBindings):
+        (WebCore::WaveShaperProcessor::setOversampleForBindings):
+        (WebCore::WaveShaperProcessor::process):
+        * Modules/webaudio/WaveShaperProcessor.h:
+
+2021-06-01  Chris Dumez  <cdu...@apple.com>
+
         Fix thread safety issues in MediaElementAudioSourceNode
         https://bugs.webkit.org/show_bug.cgi?id=226475
 

Modified: trunk/Source/WebCore/Modules/webaudio/AudioBasicProcessorNode.h (278306 => 278307)


--- trunk/Source/WebCore/Modules/webaudio/AudioBasicProcessorNode.h	2021-06-01 14:56:04 UTC (rev 278306)
+++ trunk/Source/WebCore/Modules/webaudio/AudioBasicProcessorNode.h	2021-06-01 15:09:47 UTC (rev 278307)
@@ -58,6 +58,8 @@
     bool requiresTailProcessing() const override;
 
     AudioProcessor* processor() { return m_processor.get(); }
+    const AudioProcessor* processor() const { return m_processor.get(); }
+
     std::unique_ptr<AudioProcessor> m_processor;
 };
 

Modified: trunk/Source/WebCore/Modules/webaudio/WaveShaperDSPKernel.cpp (278306 => 278307)


--- trunk/Source/WebCore/Modules/webaudio/WaveShaperDSPKernel.cpp	2021-06-01 14:56:04 UTC (rev 278306)
+++ trunk/Source/WebCore/Modules/webaudio/WaveShaperDSPKernel.cpp	2021-06-01 15:09:47 UTC (rev 278307)
@@ -59,6 +59,7 @@
 
 void WaveShaperDSPKernel::process(const float* source, float* destination, size_t framesToProcess)
 {
+    assertIsHeld(waveShaperProcessor()->processLock());
     switch (waveShaperProcessor()->oversample()) {
     case WaveShaperProcessor::OverSampleNone:
         processCurve(source, destination, framesToProcess);
@@ -79,6 +80,7 @@
 {
     ASSERT(source && destination && waveShaperProcessor());
 
+    assertIsHeld(waveShaperProcessor()->processLock());
     Float32Array* curve = waveShaperProcessor()->curve();
     if (!curve) {
         // Act as "straight wire" pass-through if no curve is set.
@@ -163,10 +165,13 @@
 
 double WaveShaperDSPKernel::latencyTime() const
 {
+    if (!waveShaperProcessor()->processLock().tryLock())
+        return std::numeric_limits<double>::infinity();
+
+    Locker locker { AdoptLock, waveShaperProcessor()->processLock() };
+
     size_t latencyFrames = 0;
-    WaveShaperDSPKernel* kernel = const_cast<WaveShaperDSPKernel*>(this);
-
-    switch (kernel->waveShaperProcessor()->oversample()) {
+    switch (waveShaperProcessor()->oversample()) {
     case WaveShaperProcessor::OverSampleNone:
         break;
     case WaveShaperProcessor::OverSample2x:

Modified: trunk/Source/WebCore/Modules/webaudio/WaveShaperDSPKernel.h (278306 => 278307)


--- trunk/Source/WebCore/Modules/webaudio/WaveShaperDSPKernel.h	2021-06-01 14:56:04 UTC (rev 278306)
+++ trunk/Source/WebCore/Modules/webaudio/WaveShaperDSPKernel.h	2021-06-01 15:09:47 UTC (rev 278307)
@@ -40,10 +40,10 @@
     explicit WaveShaperDSPKernel(WaveShaperProcessor*);
 
     // AudioDSPKernel
-    void process(const float* source, float* dest, size_t framesToProcess) override;
-    void reset() override;
-    double tailTime() const override { return 0; }
-    double latencyTime() const override;
+    void process(const float* source, float* dest, size_t framesToProcess) final;
+    void reset() final;
+    double tailTime() const final { return 0; }
+    double latencyTime() const final;
 
     // Oversampling requires more resources, so let's only allocate them if needed.
     void lazyInitializeOversampling();
@@ -59,6 +59,7 @@
     bool requiresTailProcessing() const final;
 
     WaveShaperProcessor* waveShaperProcessor() { return static_cast<WaveShaperProcessor*>(processor()); }
+    const WaveShaperProcessor* waveShaperProcessor() const { return static_cast<const WaveShaperProcessor*>(processor()); }
 
     // Oversampling.
     std::unique_ptr<AudioFloatArray> m_tempBuffer;

Modified: trunk/Source/WebCore/Modules/webaudio/WaveShaperNode.cpp (278306 => 278307)


--- trunk/Source/WebCore/Modules/webaudio/WaveShaperNode.cpp	2021-06-01 14:56:04 UTC (rev 278306)
+++ trunk/Source/WebCore/Modules/webaudio/WaveShaperNode.cpp	2021-06-01 15:09:47 UTC (rev 278307)
@@ -53,12 +53,12 @@
         return result.releaseException();
 
     if (curve) {
-        result = node->setCurve(WTFMove(curve));
+        result = node->setCurveForBindings(WTFMove(curve));
         if (result.hasException())
             return result.releaseException();
     }
 
-    node->setOversample(options.oversample);
+    node->setOversampleForBindings(options.oversample);
 
     return node;
 }
@@ -71,7 +71,7 @@
     initialize();
 }
 
-ExceptionOr<void> WaveShaperNode::setCurve(RefPtr<Float32Array>&& curve)
+ExceptionOr<void> WaveShaperNode::setCurveForBindings(RefPtr<Float32Array>&& curve)
 {
     ASSERT(isMainThread()); 
     DEBUG_LOG(LOGIDENTIFIER);
@@ -85,13 +85,14 @@
         curve = WTFMove(clonedCurve);
     }
 
-    waveShaperProcessor()->setCurve(curve.get());
+    waveShaperProcessor()->setCurveForBindings(curve.get());
     return { };
 }
 
-Float32Array* WaveShaperNode::curve()
+Float32Array* WaveShaperNode::curveForBindings()
 {
-    return waveShaperProcessor()->curve();
+    ASSERT(isMainThread());
+    return waveShaperProcessor()->curveForBindings();
 }
 
 static inline WaveShaperProcessor::OverSampleType processorType(OverSampleType type)
@@ -108,7 +109,7 @@
     return WaveShaperProcessor::OverSampleNone;
 }
 
-void WaveShaperNode::setOversample(OverSampleType type)
+void WaveShaperNode::setOversampleForBindings(OverSampleType type)
 {
     ASSERT(isMainThread());
     INFO_LOG(LOGIDENTIFIER, type);
@@ -115,12 +116,13 @@
 
     // Synchronize with any graph changes or changes to channel configuration.
     Locker contextLocker { context().graphLock() };
-    waveShaperProcessor()->setOversample(processorType(type));
+    waveShaperProcessor()->setOversampleForBindings(processorType(type));
 }
 
-auto WaveShaperNode::oversample() const -> OverSampleType
+auto WaveShaperNode::oversampleForBindings() const -> OverSampleType
 {
-    switch (const_cast<WaveShaperNode*>(this)->waveShaperProcessor()->oversample()) {
+    ASSERT(isMainThread());
+    switch (waveShaperProcessor()->oversampleForBindings()) {
     case WaveShaperProcessor::OverSampleNone:
         return OverSampleType::None;
     case WaveShaperProcessor::OverSample2x:
@@ -134,7 +136,11 @@
 
 bool WaveShaperNode::propagatesSilence() const
 {
-    auto curve = const_cast<WaveShaperNode*>(this)->curve();
+    if (!waveShaperProcessor()->processLock().tryLock())
+        return false;
+
+    Locker locker { AdoptLock, waveShaperProcessor()->processLock() };
+    auto curve = waveShaperProcessor()->curve();
     return !curve || !curve->length();
 }
 

Modified: trunk/Source/WebCore/Modules/webaudio/WaveShaperNode.h (278306 => 278307)


--- trunk/Source/WebCore/Modules/webaudio/WaveShaperNode.h	2021-06-01 14:56:04 UTC (rev 278306)
+++ trunk/Source/WebCore/Modules/webaudio/WaveShaperNode.h	2021-06-01 15:09:47 UTC (rev 278307)
@@ -40,11 +40,11 @@
     static ExceptionOr<Ref<WaveShaperNode>> create(BaseAudioContext&, const WaveShaperOptions& = { });
 
     // setCurve() is called on the main thread.
-    ExceptionOr<void> setCurve(RefPtr<Float32Array>&&);
-    Float32Array* curve();
+    ExceptionOr<void> setCurveForBindings(RefPtr<Float32Array>&&);
+    Float32Array* curveForBindings();
 
-    void setOversample(OverSampleType);
-    OverSampleType oversample() const;
+    void setOversampleForBindings(OverSampleType);
+    OverSampleType oversampleForBindings() const;
 
     double latency() const { return latencyTime(); }
 
@@ -54,6 +54,7 @@
     bool propagatesSilence() const final;
 
     WaveShaperProcessor* waveShaperProcessor() { return static_cast<WaveShaperProcessor*>(processor()); }
+    const WaveShaperProcessor* waveShaperProcessor() const { return static_cast<const WaveShaperProcessor*>(processor()); }
 };
 
 String convertEnumerationToString(WebCore::OverSampleType); // in JSOverSampleType.cpp

Modified: trunk/Source/WebCore/Modules/webaudio/WaveShaperNode.idl (278306 => 278307)


--- trunk/Source/WebCore/Modules/webaudio/WaveShaperNode.idl	2021-06-01 14:56:04 UTC (rev 278306)
+++ trunk/Source/WebCore/Modules/webaudio/WaveShaperNode.idl	2021-06-01 15:09:47 UTC (rev 278307)
@@ -29,6 +29,6 @@
 ] interface WaveShaperNode : AudioNode {
     [EnabledBySetting=WebAudio] constructor(BaseAudioContext context, optional WaveShaperOptions options);
 
-    attribute Float32Array? curve;
-    attribute OverSampleType oversample;
+    [ImplementedAs=curveForBindings] attribute Float32Array? curve;
+    [ImplementedAs=oversampleForBindings] attribute OverSampleType oversample;
 };

Modified: trunk/Source/WebCore/Modules/webaudio/WaveShaperProcessor.cpp (278306 => 278307)


--- trunk/Source/WebCore/Modules/webaudio/WaveShaperProcessor.cpp	2021-06-01 14:56:04 UTC (rev 278306)
+++ trunk/Source/WebCore/Modules/webaudio/WaveShaperProcessor.cpp	2021-06-01 15:09:47 UTC (rev 278307)
@@ -48,8 +48,9 @@
     return makeUnique<WaveShaperDSPKernel>(this);
 }
 
-void WaveShaperProcessor::setCurve(Float32Array* curve)
+void WaveShaperProcessor::setCurveForBindings(Float32Array* curve)
 {
+    ASSERT(isMainThread());
     // This synchronizes with process().
     Locker locker { m_processLock };
 
@@ -56,19 +57,19 @@
     m_curve = curve;
 }
 
-void WaveShaperProcessor::setOversample(OverSampleType oversample)
+void WaveShaperProcessor::setOversampleForBindings(OverSampleType oversample)
 {
+    ASSERT(isMainThread());
     // This synchronizes with process().
     Locker locker { m_processLock };
 
     m_oversample = oversample;
 
-    if (oversample != OverSampleNone) {
-        for (auto& audioDSPKernel : m_kernels) {
-            WaveShaperDSPKernel& kernel = static_cast<WaveShaperDSPKernel&>(*audioDSPKernel);
-            kernel.lazyInitializeOversampling();
-        }
-    }
+    if (oversample == OverSampleNone)
+        return;
+
+    for (auto& audioDSPKernel : m_kernels)
+        static_cast<WaveShaperDSPKernel&>(*audioDSPKernel).lazyInitializeOversampling();
 }
 
 void WaveShaperProcessor::process(const AudioBus* source, AudioBus* destination, size_t framesToProcess)
@@ -92,8 +93,8 @@
     Locker locker { AdoptLock, m_processLock };
 
     // For each channel of our input, process using the corresponding WaveShaperDSPKernel into the output channel.
-    for (unsigned i = 0; i < m_kernels.size(); ++i)
-        m_kernels[i]->process(source->channel(i)->data(), destination->channel(i)->mutableData(), framesToProcess);
+    for (size_t i = 0; i < m_kernels.size(); ++i)
+        static_cast<WaveShaperDSPKernel&>(*m_kernels[i]).process(source->channel(i)->data(), destination->channel(i)->mutableData(), framesToProcess);
 }
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/Modules/webaudio/WaveShaperProcessor.h (278306 => 278307)


--- trunk/Source/WebCore/Modules/webaudio/WaveShaperProcessor.h	2021-06-01 14:56:04 UTC (rev 278306)
+++ trunk/Source/WebCore/Modules/webaudio/WaveShaperProcessor.h	2021-06-01 15:09:47 UTC (rev 278307)
@@ -48,21 +48,25 @@
 
     virtual ~WaveShaperProcessor();
 
-    std::unique_ptr<AudioDSPKernel> createKernel() override;
+    std::unique_ptr<AudioDSPKernel> createKernel() final;
 
-    void process(const AudioBus* source, AudioBus* destination, size_t framesToProcess) override;
+    void process(const AudioBus* source, AudioBus* destination, size_t framesToProcess) final;
 
-    void setCurve(Float32Array*);
-    Float32Array* curve() { return m_curve.get(); }
+    void setCurveForBindings(Float32Array*);
+    Float32Array* curveForBindings() WTF_IGNORES_THREAD_SAFETY_ANALYSIS { ASSERT(isMainThread()); return m_curve.get(); } // Doesn't grab the lock, only safe to call on the main thread.
+    Float32Array* curve() const WTF_REQUIRES_LOCK(m_processLock) { return m_curve.get(); }
 
-    void setOversample(OverSampleType);
-    OverSampleType oversample() const { return m_oversample; }
+    void setOversampleForBindings(OverSampleType);
+    OverSampleType oversampleForBindings() const WTF_IGNORES_THREAD_SAFETY_ANALYSIS { ASSERT(isMainThread()); return m_oversample; } // Doesn't grab the lock, only safe to call on the main thread.
+    OverSampleType oversample() const WTF_REQUIRES_LOCK(m_processLock) { return m_oversample; }
 
+    Lock& processLock() const WTF_RETURNS_LOCK(m_processLock) { return m_processLock; }
+
 private:
     // m_curve represents the non-linear shaping curve.
-    RefPtr<Float32Array> m_curve;
+    RefPtr<Float32Array> m_curve WTF_GUARDED_BY_LOCK(m_processLock);
 
-    OverSampleType m_oversample { OverSampleNone };
+    OverSampleType m_oversample WTF_GUARDED_BY_LOCK(m_processLock) { OverSampleNone };
 
     // This synchronizes process() with setCurve().
     mutable Lock m_processLock;
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to