RESOLVED FIXED 229661
[WebRTC] Leak or over-release of CFPixelBufferRef returned from webrtc::createPixelBufferFromFrame()
https://bugs.webkit.org/show_bug.cgi?id=229661
Summary [WebRTC] Leak or over-release of CFPixelBufferRef returned from webrtc::creat...
David Kilzer (:ddkilzer)
Reported 2021-08-29 19:57:52 PDT
Leak or over-release of CFPixelBufferRef returned from webrtc::createPixelBufferFromFrame(). Found by the clang static analyzer: OpenSource/Source/ThirdParty/libwebrtc/Source/webrtc/sdk/WebKit/WebKitDecoderReceiver.cpp:177:9: warning: Incorrect decrement of the reference count of an object that is not owned at this point by the caller CFRelease(pixelBuffer); ^~~~~~~~~~~~~~~~~~~~~~ OpenSource/Source/ThirdParty/libwebrtc/Source/webrtc/sdk/WebKit/WebKitDecoderReceiver.cpp:177:9: warning: Incorrect decrement of the reference count of an object that is not owned at this point by the caller CFRelease(pixelBuffer); ^~~~~~~~~~~~~~~~~~~~~~ OpenSource/Source/ThirdParty/libwebrtc/Source/webrtc/sdk/WebKit/WebKitDecoderReceiver.cpp:177:9: warning: Incorrect decrement of the reference count of an object that is not owned at this point by the caller CFRelease(pixelBuffer); ^~~~~~~~~~~~~~~~~~~~~~ 3 warnings generated.
Attachments
Patch v1 (13.20 KB, patch)
2021-08-29 20:04 PDT, David Kilzer (:ddkilzer)
no flags
Patch v2 (30.04 KB, patch)
2021-08-30 07:49 PDT, David Kilzer (:ddkilzer)
no flags
Patch v3 (30.05 KB, patch)
2021-08-30 09:17 PDT, David Kilzer (:ddkilzer)
ews-feeder: commit-queue-
Patch v4 (30.11 KB, patch)
2021-08-30 10:21 PDT, David Kilzer (:ddkilzer)
ews-feeder: commit-queue-
Alternative approach (2.04 KB, patch)
2021-08-31 01:10 PDT, youenn fablet
no flags
Patch v5 (30.12 KB, patch)
2021-08-31 19:27 PDT, David Kilzer (:ddkilzer)
no flags
Radar WebKit Bug Importer
Comment 1 2021-08-29 19:58:21 PDT
David Kilzer (:ddkilzer)
Comment 2 2021-08-29 20:04:13 PDT
Created attachment 436754 [details] Patch v1
David Kilzer (:ddkilzer)
Comment 3 2021-08-30 07:49:12 PDT
Created attachment 436771 [details] Patch v2
David Kilzer (:ddkilzer)
Comment 4 2021-08-30 09:17:27 PDT
Created attachment 436778 [details] Patch v3
David Kilzer (:ddkilzer)
Comment 5 2021-08-30 10:21:56 PDT
Created attachment 436790 [details] Patch v4
Eric Carlson
Comment 6 2021-08-30 11:26:39 PDT
Comment on attachment 436790 [details] Patch v4 r=me once the bots are happy
David Kilzer (:ddkilzer)
Comment 7 2021-08-30 13:03:17 PDT
(In reply to Eric Carlson from comment #6) > Comment on attachment 436790 [details] > Patch v4 > > r=me once the bots are happy Failures reproduce on macOS 12 Monterey as well.
David Kilzer (:ddkilzer)
Comment 8 2021-08-30 13:14:42 PDT
I am probably going to need help figuring out why these two tests are failing, and only on macOS. There are a lot of moving parts I'm not familiar with here.
David Kilzer (:ddkilzer)
Comment 9 2021-08-30 15:27:53 PDT
Comment on attachment 436790 [details] Patch v4 View in context: https://bugs.webkit.org/attachment.cgi?id=436790&action=review > Source/WebKit/WebProcess/GPU/webrtc/LibWebRTCCodecs.cpp:434 > - RetainPtr<CVPixelBufferRef> newPixelBuffer; > - auto pixelBuffer = webrtc::pixelBufferFromFrame(frame, [&newPixelBuffer](size_t width, size_t height, webrtc::BufferType bufferType) -> CVPixelBufferRef { > + auto pixelBuffer = adoptCF(webrtc::createPixelBufferFromFrame(frame, [](size_t width, size_t height, webrtc::BufferType bufferType) -> CVPixelBufferRef { I think I was assuming that `newPixelBuffer` and `pixelBuffer` would be the same here, but they may not be. This may be the cause of the regression. Need to check into this.
youenn fablet
Comment 10 2021-08-31 01:01:48 PDT
Comment on attachment 436790 [details] Patch v4 View in context: https://bugs.webkit.org/attachment.cgi?id=436790&action=review >> Source/WebKit/WebProcess/GPU/webrtc/LibWebRTCCodecs.cpp:434 >> + auto pixelBuffer = adoptCF(webrtc::createPixelBufferFromFrame(frame, [](size_t width, size_t height, webrtc::BufferType bufferType) -> CVPixelBufferRef { > > I think I was assuming that `newPixelBuffer` and `pixelBuffer` would be the same here, but they may not be. > > This may be the cause of the regression. Need to check into this. The idea of pixelBufferFromFrame is the following: - In most cases, there is no need to do any conversion/allocation, pixelBuffer is directly retrieved from frame which keeps ownership of the underlying pixel buffer. - In some cases, in particular for muted frames, the pixel buffer is not I420, in which case we do need a conversion. The idea is that pixelBuffer will not own ownership on the new pixel buffer but newPixelBuffer will actually own it since it is a RetainPtr. The easiest way to fix this would be to apply the same strategy in WebKitDecoderReceiver::Decoded as in LibWebRTCCodecs::encodeFrame or RealtimeIncomingVideoSourceCocoa.
youenn fablet
Comment 11 2021-08-31 01:10:52 PDT
Created attachment 436852 [details] Alternative approach
youenn fablet
Comment 12 2021-08-31 01:12:26 PDT
I uploaded the alternative approach. I am fine with your approach since that might be easier to maintain.
David Kilzer (:ddkilzer)
Comment 13 2021-08-31 11:07:54 PDT
(In reply to youenn fablet from comment #12) > I uploaded the alternative approach. > I am fine with your approach since that might be easier to maintain. Thanks! The tests clearly say that I made a logic error (logic change) in my patch, but I can't see what that is! I may need to step away from the patch for a few days if I can't find it.
David Kilzer (:ddkilzer)
Comment 14 2021-08-31 17:48:39 PDT
(In reply to David Kilzer (:ddkilzer) from comment #13) > (In reply to youenn fablet from comment #12) > > I uploaded the alternative approach. > > I am fine with your approach since that might be easier to maintain. > > Thanks! The tests clearly say that I made a logic error (logic change) in > my patch, but I can't see what that is! I may need to step away from the > patch for a few days if I can't find it. I think I see the inadvertent logic change that I made in LibWebRTCCodecs.cpp. Testing incremental changes locally now to verify.
David Kilzer (:ddkilzer)
Comment 15 2021-08-31 19:23:59 PDT
Comment on attachment 436790 [details] Patch v4 View in context: https://bugs.webkit.org/attachment.cgi?id=436790&action=review > Source/WebKit/WebProcess/GPU/webrtc/LibWebRTCCodecs.cpp:460 > - auto sample = RemoteVideoSample::create(pixelBuffer, MediaTime(frame.timestamp_us() * 1000, 1000000), toMediaSampleVideoRotation(frame.rotation())); > + auto sample = RemoteVideoSample::create(pixelBuffer.get(), MediaTime(frame.timestamp_us() * 1000, 1000000), toMediaSampleVideoRotation(frame.rotation())); > if (!sample) { > // FIXME: Optimize this code path, currently we have non BGRA for muted frames at least. > - newPixelBuffer = convertToBGRA(pixelBuffer); > - sample = RemoteVideoSample::create(newPixelBuffer.get(), MediaTime(frame.timestamp_us() * 1000, 1000000), toMediaSampleVideoRotation(frame.rotation())); > + auto convertedPixelBuffer = convertToBGRA(pixelBuffer.get()); > + sample = RemoteVideoSample::create(convertedPixelBuffer.get(), MediaTime(frame.timestamp_us() * 1000, 1000000), toMediaSampleVideoRotation(frame.rotation())); > } > > encoder.connection->send(Messages::LibWebRTCCodecsProxy::EncodeFrame { encoder.identifier, *sample, frame.timestamp(), shouldEncodeAsKeyFrame }, 0); The logic bug I was missing was that the CVPixelBufferRef needed to be retained until after send() is called at the end of the method. By creating a local `convertedPixelBuffer` variable, the CVPixelBufferRef is released before send() is called, causing the bug. The fix is to reuse `pixelBuffer` to store the converted CVPixelBufferRef since its scope is the same as the send() method.
David Kilzer (:ddkilzer)
Comment 16 2021-08-31 19:27:28 PDT
Created attachment 436986 [details] Patch v5
EWS
Comment 17 2021-09-01 13:02:12 PDT
Committed r281868 (241198@main): <https://commits.webkit.org/241198@main> All reviewed patches have been landed. Closing bug and clearing flags on attachment 436986 [details].
Darin Adler
Comment 18 2021-09-01 13:07:06 PDT
Comment on attachment 436986 [details] Patch v5 View in context: https://bugs.webkit.org/attachment.cgi?id=436986&action=review > Source/WebKit/WebProcess/GPU/webrtc/LibWebRTCCodecs.cpp:457 > + pixelBuffer = convertToBGRA(pixelBuffer.get()); > + sample = RemoteVideoSample::create(pixelBuffer.get(), MediaTime(frame.timestamp_us() * 1000, 1000000), toMediaSampleVideoRotation(frame.rotation())); For future reference, don’t need a local variable for this. Intermediates are kept alive until the full expression they are part of is done, in this case until the function returns, so this would be correct: sample = RemoteVideoSample::create(convertToBGRA(pixelBuffer.get()).get(), MediaTime(frame.timestamp_us() * 1000, 1000000), toMediaSampleVideoRotation(frame.rotation())); But also, is it important to reuse the variable? Could just use a new one: auto convertedBuffer = convertToBGRA(pixelBuffer.get());
David Kilzer (:ddkilzer)
Comment 19 2021-09-01 13:11:27 PDT
Comment on attachment 436986 [details] Patch v5 View in context: https://bugs.webkit.org/attachment.cgi?id=436986&action=review >> Source/WebKit/WebProcess/GPU/webrtc/LibWebRTCCodecs.cpp:457 >> + sample = RemoteVideoSample::create(pixelBuffer.get(), MediaTime(frame.timestamp_us() * 1000, 1000000), toMediaSampleVideoRotation(frame.rotation())); > > For future reference, don’t need a local variable for this. Intermediates are kept alive until the full expression they are part of is done, in this case until the function returns, so this would be correct: > > sample = RemoteVideoSample::create(convertToBGRA(pixelBuffer.get()).get(), MediaTime(frame.timestamp_us() * 1000, 1000000), toMediaSampleVideoRotation(frame.rotation())); > > But also, is it important to reuse the variable? Could just use a new one: > > auto convertedBuffer = convertToBGRA(pixelBuffer.get()); It is important to have a variable outside the scope of the `if` block to keep the CVPixelBufferRef alive until `send()` is called later in the method. If this isn't done, it causes two test failures. See Comment #15 for details.
Darin Adler
Comment 20 2021-09-01 13:49:40 PDT
Comment on attachment 436986 [details] Patch v5 View in context: https://bugs.webkit.org/attachment.cgi?id=436986&action=review >>> Source/WebKit/WebProcess/GPU/webrtc/LibWebRTCCodecs.cpp:457 >>> + sample = RemoteVideoSample::create(pixelBuffer.get(), MediaTime(frame.timestamp_us() * 1000, 1000000), toMediaSampleVideoRotation(frame.rotation())); >> >> For future reference, don’t need a local variable for this. Intermediates are kept alive until the full expression they are part of is done, in this case until the function returns, so this would be correct: >> >> sample = RemoteVideoSample::create(convertToBGRA(pixelBuffer.get()).get(), MediaTime(frame.timestamp_us() * 1000, 1000000), toMediaSampleVideoRotation(frame.rotation())); >> >> But also, is it important to reuse the variable? Could just use a new one: >> >> auto convertedBuffer = convertToBGRA(pixelBuffer.get()); > > It is important to have a variable outside the scope of the `if` block to keep the CVPixelBufferRef alive until `send()` is called later in the method. If this isn't done, it causes two test failures. > > See Comment #15 for details. Makes sense. Seems like we have a small design problem that we should return and fix. RemoteVideoSample has a dangerous interface if it takes a raw pointer and the caller needs to keep it alive.
David Kilzer (:ddkilzer)
Comment 21 2021-09-02 08:50:22 PDT
(In reply to Darin Adler from comment #20) > Makes sense. Seems like we have a small design problem that we should return > and fix. RemoteVideoSample has a dangerous interface if it takes a raw > pointer and the caller needs to keep it alive. Bug 229806: RemoteVideoSample needs CVPixelBufferRef to be kept alive, but relies on caller to retain it
Note You need to log in before you can comment on or make changes to this bug.