From e83e4c7460972898dc06a5f5ab36eed7c6b101b5 Mon Sep 17 00:00:00 2001
From: Jer Noble <jer.noble@apple.com>
Date: Tue, 11 Jun 2024 11:54:06 -0700
Subject: [PATCH] CVE-2024-40780: Add check in AudioBufferSourceNode::renderFromBuffer() 
when detune is set to large negative value

Upstream-Status: Backport from https://github.com/WebKit/WebKit/commit/e83e4c7460972898dc06a5f5ab36eed7c6b101b5
CVE: CVE-2024-40780

Signed-off-by: Rohini Sangam <rsangam@mvista.com>
---
 ...buffersourcenode-detune-crash-expected.txt | 10 +++++++
 .../audiobuffersourcenode-detune-crash.html   | 30 +++++++++++++++++++
 .../webaudio/AudioBufferSourceNode.cpp        |  7 +++++
 3 files changed, 47 insertions(+)
 create mode 100644 LayoutTests/webaudio/audiobuffersourcenode-detune-crash-expected.txt
 create mode 100644 LayoutTests/webaudio/audiobuffersourcenode-detune-crash.html

diff --git a/LayoutTests/webaudio/audiobuffersourcenode-detune-crash-expected.txt b/LayoutTests/webaudio/audiobuffersourcenode-detune-crash-expected.txt
new file mode 100644
index 00000000..914ba0b1
--- /dev/null
+++ b/LayoutTests/webaudio/audiobuffersourcenode-detune-crash-expected.txt
@@ -0,0 +1,10 @@
+Attempting to create a AudioBufferSourceNode with a large negative detune value should not crash.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS Test passed because it did not crash.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/webaudio/audiobuffersourcenode-detune-crash.html b/LayoutTests/webaudio/audiobuffersourcenode-detune-crash.html
new file mode 100644
index 00000000..e8af579d
--- /dev/null
+++ b/LayoutTests/webaudio/audiobuffersourcenode-detune-crash.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+    <head>
+    <script src="../resources/js-test-pre.js"></script>
+    <script src="resources/audio-testing.js"></script>
+    </head>
+    <body>
+        <script>
+            description("Attempting to create a AudioBufferSourceNode with a large negative detune value should not crash.");
+
+            jsTestIsAsync = true;
+
+            var context = new AudioContext();
+            var src = context.createBufferSource();
+            var buffer = context.createBuffer(1, 256, 44100);
+            src.buffer = buffer;
+            src.start(undefined, 1);
+            src.connect(context.listener.positionX, 0);
+            var panner = context.createPanner();
+            src.detune.value = -0xffffff;
+            panner.connect(context.destination);
+            setTimeout(() => {
+                testPassed("Test passed because it did not crash.");
+                finishJSTest();
+            }, 100);
+        </script>
+
+        <script src="../resources/js-test-post.js"></script>
+    </body>
+</html>
diff --git a/Source/WebCore/Modules/webaudio/AudioBufferSourceNode.cpp b/Source/WebCore/Modules/webaudio/AudioBufferSourceNode.cpp
index 689d37a1..f68e7ff5 100644
--- a/Source/WebCore/Modules/webaudio/AudioBufferSourceNode.cpp
+++ b/Source/WebCore/Modules/webaudio/AudioBufferSourceNode.cpp
@@ -327,9 +327,16 @@ bool AudioBufferSourceNode::renderFromBuffer(AudioBus* bus, unsigned destination
         virtualReadIndex = readIndex;
     } else if (!pitchRate) {
         unsigned readIndex = static_cast<unsigned>(virtualReadIndex);
+        int deltaFrames = static_cast<int>(virtualDeltaFrames);
+        maxFrame = static_cast<unsigned>(virtualMaxFrame);
+
+        if (readIndex >= maxFrame)
+            readIndex -= deltaFrames;
 
         for (unsigned i = 0; i < numberOfChannels; ++i)
             std::fill_n(destinationChannels[i] + writeIndex, framesToProcess, sourceChannels[i][readIndex]);
+
+        virtualReadIndex = readIndex;
     } else if (reverse) {
         unsigned maxFrame = static_cast<unsigned>(virtualMaxFrame);
         unsigned minFrame = static_cast<unsigned>(floorf(virtualMinFrame));
-- 
2.35.7

