From 0aa3accacedef8f79ff5a2485a8e066cd3a0b954 Mon Sep 17 00:00:00 2001 From: dp0000 <53818367+dp0000@users.noreply.github.com> Date: Thu, 9 Jul 2026 09:58:40 +0530 Subject: [PATCH 1/7] Update DrmSessionManager.cpp --- middleware/drm/DrmSessionManager.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/middleware/drm/DrmSessionManager.cpp b/middleware/drm/DrmSessionManager.cpp index 078ffe0afe..305626aa51 100755 --- a/middleware/drm/DrmSessionManager.cpp +++ b/middleware/drm/DrmSessionManager.cpp @@ -109,6 +109,10 @@ void DrmSessionManager::clearSessionData() { if (drmSessionContexts != NULL && drmSessionContexts[i].drmSession != NULL) { + /* DELIA-70726 fix: block until any in-flight decrypt() on this session + * (called from a GStreamer pipeline thread via a cached raw pointer) + * has completed, before freeing the object. */ + drmSessionContexts[i].drmSession->PrepareForDestruction(); MW_SAFE_DELETE(drmSessionContexts[i].drmSession); drmSessionContexts[i] = DrmSessionContext(); } @@ -201,6 +205,8 @@ void DrmSessionManager::clearDrmSession(bool forceClearSession) if (drmSessionContexts[i].drmSession != NULL) { MW_LOG_WARN("DrmSessionManager:: Clearing failed Session Data Slot : %d", i); + /* DELIA-70726 fix: see clearSessionData() for rationale. */ + drmSessionContexts[i].drmSession->PrepareForDestruction(); MW_SAFE_DELETE(drmSessionContexts[i].drmSession); } } @@ -874,6 +880,13 @@ KeyState DrmSessionManager::getDrmSession(int &err, std::shared_ptr d } } MW_LOG_WARN("deleting existing DRM session for %s ", drmSessionContexts[sessionSlot].drmSession->getKeySystem().c_str()); + /* DELIA-70726 fix: this slot may still be referenced by a GStreamer + * decryptor element of a previous, not-yet-fully-torn-down pipeline + * (rapid/back-to-back channel change). Block here until any decrypt() + * call already in flight against this session finishes, so the delete + * below cannot race with OCDMSessionAdapter::verifyOutputProtection()/ + * decrypt() running on the old pipeline's multiqueue thread. */ + drmSessionContexts[sessionSlot].drmSession->PrepareForDestruction(); MW_SAFE_DELETE(drmSessionContexts[sessionSlot].drmSession); } this->ProfileUpdateCb(); From a8bf586374bee54754c9cc0ffe66a5923f0bdb8f Mon Sep 17 00:00:00 2001 From: dp0000 <53818367+dp0000@users.noreply.github.com> Date: Thu, 9 Jul 2026 09:59:06 +0530 Subject: [PATCH 2/7] Update DrmSession.cpp --- middleware/drm/DrmSession.cpp | 54 +++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/middleware/drm/DrmSession.cpp b/middleware/drm/DrmSession.cpp index f3e48c987b..35d10c9525 100644 --- a/middleware/drm/DrmSession.cpp +++ b/middleware/drm/DrmSession.cpp @@ -24,12 +24,17 @@ #include "DrmSession.h" #include "PlayerLogManager.h" +#include /** * @brief Constructor for DrmSession. */ DrmSession::DrmSession(const string &keySystem) : m_keySystem(keySystem),m_OutputProtectionEnabled(false) , mContentSecurityManagerSession() + , mLifecycleMutex() + , mLifecycleCV() + , mActiveOperations(0) + , mMarkedForDestruction(false) { } @@ -40,6 +45,55 @@ DrmSession::~DrmSession() { } +/** + * @brief DELIA-70726 fix: Acquire lifecycle guard before use in decrypt(). + */ +bool DrmSession::AcquireForUse() +{ + std::lock_guard lock(mLifecycleMutex); + if (mMarkedForDestruction) + { + return false; + } + mActiveOperations++; + return true; +} + +/** + * @brief DELIA-70726 fix: Release lifecycle guard after use in decrypt(). + */ +void DrmSession::ReleaseAfterUse() +{ + std::lock_guard lock(mLifecycleMutex); + if (mActiveOperations > 0) + { + mActiveOperations--; + } + if (mActiveOperations == 0) + { + mLifecycleCV.notify_all(); + } +} + +/** + * @brief DELIA-70726 fix: Block deletion of this session until any decrypt() + * call already in progress (having acquired the guard) has finished. + */ +void DrmSession::PrepareForDestruction(uint32_t timeoutMs) +{ + std::unique_lock lock(mLifecycleMutex); + mMarkedForDestruction = true; + if (mActiveOperations > 0) + { + MW_LOG_WARN("DrmSession::PrepareForDestruction : waiting for %d in-flight decrypt operation(s) to complete before delete", mActiveOperations); + mLifecycleCV.wait_for(lock, std::chrono::milliseconds(timeoutMs), [this]() { return mActiveOperations == 0; }); + if (mActiveOperations > 0) + { + MW_LOG_ERR("DrmSession::PrepareForDestruction : timed out waiting for in-flight decrypt operation(s); proceeding with destruction"); + } + } +} + /** * @brief Get the DRM System, ie, UUID for PlayReady WideVine etc.. */ From 9d987f027f96a0397de37d1cbffd16f8c050cc4f Mon Sep 17 00:00:00 2001 From: dp0000 <53818367+dp0000@users.noreply.github.com> Date: Thu, 9 Jul 2026 09:59:26 +0530 Subject: [PATCH 3/7] Update DrmSession.h --- middleware/drm/DrmSession.h | 49 +++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/middleware/drm/DrmSession.h b/middleware/drm/DrmSession.h index 1766304c8d..0072fdcf7a 100755 --- a/middleware/drm/DrmSession.h +++ b/middleware/drm/DrmSession.h @@ -29,6 +29,9 @@ #include #include #include +#include +#include +#include #include "DrmUtils.h" #include "ContentSecurityManagerSession.h" @@ -66,7 +69,53 @@ class DrmSession std::string m_keySystem; bool m_OutputProtectionEnabled; ContentSecurityManagerSession mContentSecurityManagerSession; + + /* DELIA-70726 fix: + * Lifecycle guard used to prevent the DrmSession object from being + * deleted (e.g. by DrmSessionManager during DRM session slot + * reuse/eviction on back-to-back channel changes) while a GStreamer + * pipeline thread (multiqueue/decryptor) is concurrently inside + * decrypt()/verifyOutputProtection(). Without this guard, deletion of + * a session that is still referenced by an old, not-yet-fully-torn-down + * pipeline results in a use-after-free SIGSEGV inside + * OCDMSessionAdapter::verifyOutputProtection(). + */ + std::mutex mLifecycleMutex; + std::condition_variable mLifecycleCV; + int mActiveOperations; + bool mMarkedForDestruction; + public: + /** + * @fn AcquireForUse + * @brief Must be called by any external caller (e.g. the GStreamer + * decryptor element) before invoking decrypt() on a DrmSession + * obtained via a raw/cached pointer. Returns false if the + * session is already being torn down, in which case decrypt() + * MUST NOT be called on this object. + * @retval true if it is safe to call decrypt(), false otherwise. + */ + bool AcquireForUse(); + + /** + * @fn ReleaseAfterUse + * @brief Must be called exactly once for every successful AcquireForUse(), + * after the decrypt() call completes. + */ + void ReleaseAfterUse(); + + /** + * @fn PrepareForDestruction + * @brief Must be called by the owner (DrmSessionManager) before deleting + * this DrmSession. Marks the session so that any new + * AcquireForUse() calls fail fast, and blocks (bounded) until all + * in-flight decrypt() operations that already acquired the guard + * have completed, making it safe to free the object. + * @param timeoutMs maximum time to wait for in-flight operations to drain. + */ + void PrepareForDestruction(uint32_t timeoutMs = 3000); + + /** * @brief Create drm session with given init data * @param f_pbInitData : pointer to initdata From 2cf27de0397d6038ac6c0510095baf79967955fa Mon Sep 17 00:00:00 2001 From: dp0000 <53818367+dp0000@users.noreply.github.com> Date: Thu, 9 Jul 2026 10:00:24 +0530 Subject: [PATCH 4/7] Update gstcdmidecryptor.cpp --- .../gst-plugins/drm/gst/gstcdmidecryptor.cpp | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/middleware/gst-plugins/drm/gst/gstcdmidecryptor.cpp b/middleware/gst-plugins/drm/gst/gstcdmidecryptor.cpp index 10add97a62..829ba720fb 100755 --- a/middleware/gst-plugins/drm/gst/gstcdmidecryptor.cpp +++ b/middleware/gst-plugins/drm/gst/gstcdmidecryptor.cpp @@ -534,12 +534,19 @@ static GstFlowReturn gst_cdmidecryptor_transform_ip( { // call decrypt even for clear samples in order to copy it to a secure buffer. If secure buffers are not supported // decrypt() call will return without doing anything - if (cdmidecryptor->drmSession != NULL && cdmidecryptor->sinkCaps != NULL) - errorCode = cdmidecryptor->drmSession->decrypt(keyIDBuffer, ivBuffer, buffer, subSampleCount, subsamplesBuffer, cdmidecryptor->sinkCaps); + /* DELIA-70726 fix: guard against the DrmSession being concurrently torn down + * (e.g. DrmSessionManager reusing/evicting the slot during a back-to-back + * channel change) while this pipeline still has buffers in flight. */ + if (cdmidecryptor->drmSession != NULL && cdmidecryptor->sinkCaps != NULL + && cdmidecryptor->drmSession->AcquireForUse()) + { + errorCode = cdmidecryptor->drmSession->decrypt(keyIDBuffer, ivBuffer, buffer, subSampleCount, subsamplesBuffer, cdmidecryptor->sinkCaps); + cdmidecryptor->drmSession->ReleaseAfterUse(); + } else - { /* If drmSession creation failed, then the call will be aborted here */ + { /* If drmSession creation failed, or is being destroyed, the call will be aborted here */ result = GST_FLOW_NOT_SUPPORTED; - GST_ERROR_OBJECT(cdmidecryptor, "drmSession or sinkCaps is **** NULL ****, returning GST_FLOW_NOT_SUPPORTED"); + GST_ERROR_OBJECT(cdmidecryptor, "drmSession or sinkCaps is **** NULL **** (or session is being destroyed), returning GST_FLOW_NOT_SUPPORTED"); } } goto free_resources; @@ -656,7 +663,20 @@ static GstFlowReturn gst_cdmidecryptor_transform_ip( result = GST_FLOW_NOT_SUPPORTED; goto free_resources; } + /* DELIA-70726 fix: guard against the DrmSession being concurrently torn down + * (e.g. DrmSessionManager reusing/evicting the slot during a back-to-back + * channel change) while this pipeline still has buffers in flight. Without + * this guard, the multiqueue/decryptor thread can call decrypt() on a + * DrmSession that is being (or has already been) freed, causing a + * use-after-free SIGSEGV inside OCDMSessionAdapter::verifyOutputProtection(). */ + if (!cdmidecryptor->drmSession->AcquireForUse()) + { + GST_ERROR_OBJECT(cdmidecryptor, "drmSession is being destroyed, aborting decrypt"); + result = GST_FLOW_NOT_SUPPORTED; + goto free_resources; + } errorCode = cdmidecryptor->drmSession->decrypt(keyIDBuffer, ivBuffer, buffer, subSampleCount, subsamplesBuffer, cdmidecryptor->sinkCaps); + cdmidecryptor->drmSession->ReleaseAfterUse(); cdmidecryptor->streamEncrypted = true; if (errorCode != 0 || cdmidecryptor->hdcpOpProtectionFailCount) From a837074e4f52acac067f5bbef34fe4c9d749bd67 Mon Sep 17 00:00:00 2001 From: dp0000 <53818367+dp0000@users.noreply.github.com> Date: Thu, 9 Jul 2026 15:38:29 +0530 Subject: [PATCH 5/7] Update gstcdmidecryptor.cpp --- middleware/gst-plugins/drm/gst/gstcdmidecryptor.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/middleware/gst-plugins/drm/gst/gstcdmidecryptor.cpp b/middleware/gst-plugins/drm/gst/gstcdmidecryptor.cpp index 829ba720fb..2f029cec0a 100755 --- a/middleware/gst-plugins/drm/gst/gstcdmidecryptor.cpp +++ b/middleware/gst-plugins/drm/gst/gstcdmidecryptor.cpp @@ -467,9 +467,12 @@ gst_cdmidecryptor_transform_caps(GstBaseTransform * trans, cdmidecryptor->sinkCaps = NULL; } cdmidecryptor->sinkCaps = gst_caps_copy(transformedCaps); - g_cond_signal(&cdmidecryptor->sinkCapsCond); - g_mutex_unlock(&cdmidecryptor->mutex); - GST_DEBUG_OBJECT(trans, "Set sinkCaps to %" GST_PTR_FORMAT, cdmidecryptor->sinkCaps); + sinkCapsCopy = gst_caps_ref(cdmidecryptor->sinkCaps); // take an extra ref to keep it alive + g_cond_signal(&cdmidecryptor->sinkCapsCond); + g_mutex_unlock(&cdmidecryptor->mutex); + GST_DEBUG_OBJECT(trans, "Set sinkCaps to %" GST_PTR_FORMAT, sinkCapsCopy); + gst_caps_unref(sinkCapsCopy); // release the extra ref + } return transformedCaps; } From e025a0849e8037d768cb72b8602f02fc11d5e371 Mon Sep 17 00:00:00 2001 From: dp0000 <53818367+dp0000@users.noreply.github.com> Date: Fri, 10 Jul 2026 07:25:45 +0530 Subject: [PATCH 6/7] Update gstcdmidecryptor.cpp --- middleware/gst-plugins/drm/gst/gstcdmidecryptor.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/middleware/gst-plugins/drm/gst/gstcdmidecryptor.cpp b/middleware/gst-plugins/drm/gst/gstcdmidecryptor.cpp index 2f029cec0a..245ee8bfd0 100755 --- a/middleware/gst-plugins/drm/gst/gstcdmidecryptor.cpp +++ b/middleware/gst-plugins/drm/gst/gstcdmidecryptor.cpp @@ -459,6 +459,7 @@ gst_cdmidecryptor_transform_caps(GstBaseTransform * trans, GST_LOG_OBJECT(trans, "returning %" GST_PTR_FORMAT, transformedCaps); if (direction == GST_PAD_SINK && !gst_caps_is_empty(transformedCaps)) { + GstCaps* sinkCapsCopy = NULL; g_mutex_lock(&cdmidecryptor->mutex); // clean up previous caps if (cdmidecryptor->sinkCaps) From a81fc0465ea920057545a72e424efa27a447114d Mon Sep 17 00:00:00 2001 From: deepikasri Date: Mon, 13 Jul 2026 01:13:56 -0400 Subject: [PATCH 7/7] SERXIONE-8829: Rialto server crash results in "OTTPENDING" and Techfault during linear playback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changes: Add UUID (dashed string) → 16-byte binary conversion so cencData can be matched against binary Key IDs parsed from PSSH. Make getKey() use map-safe lookup (find/begin) instead of assuming contiguous integer slots (e.g., at(0)). Improve fallback behavior and logging when the requested key slot is not found. --- middleware/drm/helper/WidevineDrmHelper.cpp | 61 +++++++++++++++++---- 1 file changed, 49 insertions(+), 12 deletions(-) diff --git a/middleware/drm/helper/WidevineDrmHelper.cpp b/middleware/drm/helper/WidevineDrmHelper.cpp index 1305f0648a..02f4e30ee3 100755 --- a/middleware/drm/helper/WidevineDrmHelper.cpp +++ b/middleware/drm/helper/WidevineDrmHelper.cpp @@ -24,6 +24,7 @@ #include #include +#include #include "WidevineDrmHelper.h" #include "DrmUtils.h" @@ -184,18 +185,55 @@ void WidevineDrmHelper::setDrmMetaData(const std::string& metaData) void WidevineDrmHelper::setDefaultKeyID(const std::string& cencData) { + mDefaultKeySlot = -1; std::vector defaultKeyID(cencData.begin(), cencData.end()); + // Also convert UUID string (e.g. "f3dff538-b8c9-58e4-e8cd-96cf811d32dc") to 16-byte binary + // for comparison against binary keyIDs parsed from PSSH + std::vector defaultKeyIDBinary; + std::string uuidHex; + uuidHex.reserve(cencData.size()); + for (char c : cencData) + { + if (c != '-') + { + uuidHex += c; + } + } + if (uuidHex.size() == 32) + { + defaultKeyIDBinary.reserve(16); + for (size_t i = 0; i < uuidHex.size(); i += 2) + { + char hexPair[3] = {uuidHex[i], uuidHex[i + 1], '\0'}; + char* end = nullptr; + unsigned long v = std::strtoul(hexPair, &end, 16); + if (end != hexPair + 2 || v > 0xFF) + { + MW_LOG_WARN("setDefaultKeyID: invalid hex in cencData at offset %zu", i); + defaultKeyIDBinary.clear(); + break; + } + defaultKeyIDBinary.push_back(static_cast(v)); + } + } + if(!mKeyIDs.empty()) { for(auto& it : mKeyIDs) { - if(defaultKeyID == it.second) + if(defaultKeyID == it.second || defaultKeyIDBinary == it.second) { mDefaultKeySlot = it.first; - MW_LOG_WARN("setDefaultKeyID : %s slot : %d", PlayerLogManager::getHexDebugStr(defaultKeyID).c_str(), mDefaultKeySlot); + MW_LOG_WARN("setDefaultKeyID : %s slot : %d", PlayerLogManager::getHexDebugStr(it.second).c_str(), mDefaultKeySlot); + break; } } } + if (mDefaultKeySlot < 0 && !mKeyIDs.empty()) + { + mDefaultKeySlot = mKeyIDs.begin()->first; + MW_LOG_WARN("setDefaultKeyID: no match found for cencData, defaulting to first slot %d", mDefaultKeySlot); + } } @@ -212,22 +250,21 @@ void WidevineDrmHelper::createInitData(std::vector& initData) const void WidevineDrmHelper::getKey(std::vector& keyID) const { MW_LOG_WARN("WidevineDrmHelper::getKey defaultkey: %d mKeyIDs.size:%zu", mDefaultKeySlot, mKeyIDs.size()); - // Print all key IDs for debugging - for (const auto& keyPair : mKeyIDs) { - std::string keyStr = PlayerLogManager::getHexDebugStr(keyPair.second); - MW_LOG_DEBUG("Key ID [%d]: %s", keyPair.first, keyStr.c_str()); - } - if ((mDefaultKeySlot >= 0) && (mDefaultKeySlot < mKeyIDs.size())) + if ((mDefaultKeySlot >= 0) && (mKeyIDs.find(mDefaultKeySlot) != mKeyIDs.end())) { - keyID = this->mKeyIDs.at(mDefaultKeySlot); + keyID = mKeyIDs.at(mDefaultKeySlot); } - else if (mKeyIDs.size() > 0) + else if (!mKeyIDs.empty()) { - keyID = this->mKeyIDs.at(0); + if (mDefaultKeySlot >= 0) + { + MW_LOG_WARN("mDefaultKeySlot(%d) not found in mKeyIDs, falling back to first entry", mDefaultKeySlot); + } + keyID = mKeyIDs.begin()->second; } else { - MW_LOG_ERR("No key"); + MW_LOG_ERR("No key available - mKeyIDs is empty"); } }