Fixed race condition in closeSession

Merged from http://go/wvgerrit/164799

Fix race that corrupts mCryptoSessions std::map,
and race that occurs when CryptoSessions are used after free.

Test: poc
Test: atest MediaDrmParameterizedTests
Test: atest GtsMediaTestCases

Bug: 258189255
Change-Id: I298d3e0770ace9cd590dfaacaa4c52a0732c2fe3
This commit is contained in:
Edwin Wong
2023-01-04 05:49:06 +00:00
parent d676bd9c6d
commit 94f86b717c
2 changed files with 85 additions and 49 deletions

View File

@@ -14,6 +14,7 @@
#include <list> #include <list>
#include <map> #include <map>
#include <mutex>
#include "OEMCryptoCENC.h" #include "OEMCryptoCENC.h"
#include "WVGenericCryptoInterface.h" #include "WVGenericCryptoInterface.h"
@@ -396,9 +397,46 @@ class WVDrmPlugin : public ::aidl::android::hardware::drm::BnDrmPlugin,
android::Mutex mLock; android::Mutex mLock;
}; };
class CryptoSessionMap {
public:
std::map<CdmSessionId, std::shared_ptr<CryptoSession>> clear() {
std::unique_lock<std::mutex> auto_lock(mLock);
auto copy = mMap;
mMap.clear();
return copy;
}
std::shared_ptr<CryptoSession> get(const CdmSessionId& sid) {
std::unique_lock<std::mutex> auto_lock(mLock);
if (mMap.count(sid)) {
return mMap[sid];
}
return nullptr;
}
bool empty() {
std::unique_lock<std::mutex> auto_lock(mLock);
return mMap.empty();
}
void erase(const CdmSessionId& sid) {
std::unique_lock<std::mutex> auto_lock(mLock);
mMap.erase(sid);
}
void insert(const CdmSessionId& sid, OEMCrypto_SESSION osid) {
std::unique_lock<std::mutex> auto_lock(mLock);
mMap[sid] = std::make_shared<CryptoSession>(osid);
}
private:
std::mutex mLock;
std::map<CdmSessionId, std::shared_ptr<CryptoSession>> mMap;
};
android::sp<wvcdm::WvContentDecryptionModule> const mCDM; android::sp<wvcdm::WvContentDecryptionModule> const mCDM;
WVGenericCryptoInterface* mCrypto; WVGenericCryptoInterface* mCrypto;
std::map<CdmSessionId, CryptoSession> mCryptoSessions; CryptoSessionMap mCryptoSessions;
std::shared_ptr<::aidl::android::hardware::drm::IDrmPluginListener> mListener; std::shared_ptr<::aidl::android::hardware::drm::IDrmPluginListener> mListener;
std::string mProvisioningServiceCertificate; std::string mProvisioningServiceCertificate;

View File

@@ -48,6 +48,7 @@ namespace hardware {
namespace drm { namespace drm {
namespace widevine { namespace widevine {
using std::shared_ptr;
using std::string; using std::string;
using std::vector; using std::vector;
@@ -171,15 +172,18 @@ WVDrmPlugin::~WVDrmPlugin() {
} }
void WVDrmPlugin::Close() { void WVDrmPlugin::Close() {
typedef std::map<CdmSessionId, CryptoSession>::iterator mapIterator; typedef std::map<CdmSessionId, std::shared_ptr<CryptoSession>>::iterator mapIterator;
for (mapIterator iter = mCryptoSessions.begin(); auto cryptoSessions = mCryptoSessions.clear();
iter != mCryptoSessions.end(); ++iter) { for (mapIterator iter = cryptoSessions.begin();
iter != cryptoSessions.end(); ++iter) {
CdmResponseType res = mCDM->CloseSession(iter->first); CdmResponseType res = mCDM->CloseSession(iter->first);
if (!isCdmResponseTypeSuccess(res)) { if (!isCdmResponseTypeSuccess(res)) {
ALOGE("Failed to close session while destroying WVDrmPlugin"); ALOGE("Failed to close session while destroying WVDrmPlugin");
} }
} }
mCryptoSessions.clear(); // clear local copy of cryptoSessions map
cryptoSessions.clear();
if (mCdmIdentifierBuilder.is_sealed()) { if (mCdmIdentifierBuilder.is_sealed()) {
CdmIdentifier identifier; CdmIdentifier identifier;
auto status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier); auto status = mCdmIdentifierBuilder.getCdmIdentifier(&identifier);
@@ -221,7 +225,7 @@ WvStatus WVDrmPlugin::openSessionCommon(vector<uint8_t>& sessionId) {
info.count(wvcdm::QUERY_KEY_OEMCRYPTO_SESSION_ID)) { info.count(wvcdm::QUERY_KEY_OEMCRYPTO_SESSION_ID)) {
OEMCrypto_SESSION oecSessionId = OEMCrypto_SESSION oecSessionId =
std::stoul(info[wvcdm::QUERY_KEY_OEMCRYPTO_SESSION_ID]); std::stoul(info[wvcdm::QUERY_KEY_OEMCRYPTO_SESSION_ID]);
mCryptoSessions[cdmSessionId] = CryptoSession(oecSessionId); mCryptoSessions.insert(cdmSessionId, oecSessionId);
success = true; success = true;
} else { } else {
ALOGE("Unable to query key control info."); ALOGE("Unable to query key control info.");
@@ -1223,7 +1227,7 @@ Status WVDrmPlugin::unprovisionDevice() {
std::string _value(in_value.c_str()); std::string _value(in_value.c_str());
if (name == "securityLevel") { if (name == "securityLevel") {
if (mCryptoSessions.size() == 0) { if (mCryptoSessions.empty()) {
if (_value == wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3.c_str()) { if (_value == wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3.c_str()) {
mPropertySet.set_security_level(wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3); mPropertySet.set_security_level(wvcdm::QUERY_VALUE_SECURITY_LEVEL_L3);
} else if (_value == wvcdm::QUERY_VALUE_SECURITY_LEVEL_L1.c_str()) { } else if (_value == wvcdm::QUERY_VALUE_SECURITY_LEVEL_L1.c_str()) {
@@ -1263,7 +1267,7 @@ Status WVDrmPlugin::unprovisionDevice() {
return toNdkScopedAStatus(Status::BAD_VALUE); return toNdkScopedAStatus(Status::BAD_VALUE);
} }
} else if (name == "sessionSharing") { } else if (name == "sessionSharing") {
if (mCryptoSessions.size() == 0) { if (mCryptoSessions.empty()) {
if (_value == kEnable) { if (_value == kEnable) {
mPropertySet.set_is_session_sharing_enabled(true); mPropertySet.set_is_session_sharing_enabled(true);
} else if (_value == kDisable) { } else if (_value == kDisable) {
@@ -1278,7 +1282,7 @@ Status WVDrmPlugin::unprovisionDevice() {
return toNdkScopedAStatus(Status::ERROR_DRM_UNKNOWN); return toNdkScopedAStatus(Status::ERROR_DRM_UNKNOWN);
} }
} else if (name == "appId") { } else if (name == "appId") {
if (mCryptoSessions.size() == 0) { if (mCryptoSessions.empty()) {
mPropertySet.set_app_id(_value.c_str()); mPropertySet.set_app_id(_value.c_str());
} else { } else {
ALOGE("App tried to set the application id while sessions are opened."); ALOGE("App tried to set the application id while sessions are opened.");
@@ -1286,7 +1290,7 @@ Status WVDrmPlugin::unprovisionDevice() {
return toNdkScopedAStatus(Status::ERROR_DRM_UNKNOWN); return toNdkScopedAStatus(Status::ERROR_DRM_UNKNOWN);
} }
} else if (name == "origin") { } else if (name == "origin") {
if (mCryptoSessions.size() != 0) { if (!mCryptoSessions.empty()) {
ALOGE("App tried to set the origin while sessions are opened."); ALOGE("App tried to set the origin while sessions are opened.");
ALOGW("Returns UNKNOWN error for legacy status kErrorSessionIsOpen"); ALOGW("Returns UNKNOWN error for legacy status kErrorSessionIsOpen");
return toNdkScopedAStatus(Status::ERROR_DRM_UNKNOWN); return toNdkScopedAStatus(Status::ERROR_DRM_UNKNOWN);
@@ -1414,14 +1418,13 @@ Status WVDrmPlugin::unprovisionDevice() {
std::string algo(in_algorithm.c_str()); std::string algo(in_algorithm.c_str());
CdmSessionId cdmSessionId(in_sessionId.begin(), in_sessionId.end()); CdmSessionId cdmSessionId(in_sessionId.begin(), in_sessionId.end());
if (!mCryptoSessions.count(cdmSessionId)) { shared_ptr<CryptoSession> cryptoSession = mCryptoSessions.get(cdmSessionId);
if (cryptoSession == nullptr) {
return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED); return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED);
} }
CryptoSession& cryptoSession = mCryptoSessions[cdmSessionId];
if (algo == "AES/CBC/NoPadding") { if (algo == "AES/CBC/NoPadding") {
cryptoSession.setCipherAlgorithm(OEMCrypto_AES_CBC_128_NO_PADDING); cryptoSession->setCipherAlgorithm(OEMCrypto_AES_CBC_128_NO_PADDING);
} else { } else {
return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE); return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE);
} }
@@ -1437,14 +1440,13 @@ Status WVDrmPlugin::unprovisionDevice() {
std::string algo(in_algorithm.c_str()); std::string algo(in_algorithm.c_str());
CdmSessionId cdmSessionId(in_sessionId.begin(), in_sessionId.end()); CdmSessionId cdmSessionId(in_sessionId.begin(), in_sessionId.end());
if (!mCryptoSessions.count(cdmSessionId)) { shared_ptr<CryptoSession> cryptoSession = mCryptoSessions.get(cdmSessionId);
if (cryptoSession == nullptr) {
return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED); return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED);
} }
CryptoSession& cryptoSession = mCryptoSessions[cdmSessionId];
if (algo == "HmacSHA256") { if (algo == "HmacSHA256") {
cryptoSession.setMacAlgorithm(OEMCrypto_HMAC_SHA256); cryptoSession->setMacAlgorithm(OEMCrypto_HMAC_SHA256);
} else { } else {
return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE); return toNdkScopedAStatus(Status::ERROR_DRM_CANNOT_HANDLE);
} }
@@ -1461,18 +1463,17 @@ Status WVDrmPlugin::unprovisionDevice() {
*_aidl_return = output; *_aidl_return = output;
CdmSessionId cdmSessionId(in_sessionId.begin(), in_sessionId.end()); CdmSessionId cdmSessionId(in_sessionId.begin(), in_sessionId.end());
if (!mCryptoSessions.count(cdmSessionId)) { const shared_ptr<CryptoSession> cryptoSession = mCryptoSessions.get(cdmSessionId);
if (cryptoSession == nullptr) {
return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED); return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED);
} }
const CryptoSession& cryptoSession = mCryptoSessions[cdmSessionId]; if (cryptoSession->cipherAlgorithm() == kInvalidCryptoAlgorithm) {
if (cryptoSession.cipherAlgorithm() == kInvalidCryptoAlgorithm) {
ALOGW("Returns UNKNOWN error for legacy status NO_INIT"); ALOGW("Returns UNKNOWN error for legacy status NO_INIT");
return toNdkScopedAStatus(Status::ERROR_DRM_UNKNOWN); return toNdkScopedAStatus(Status::ERROR_DRM_UNKNOWN);
} }
OEMCryptoResult res = mCrypto->selectKey(cryptoSession.oecSessionId(), OEMCryptoResult res = mCrypto->selectKey(cryptoSession->oecSessionId(),
in_keyId.data(), in_keyId.size()); in_keyId.data(), in_keyId.size());
if (res != OEMCrypto_SUCCESS) { if (res != OEMCrypto_SUCCESS) {
@@ -1483,9 +1484,9 @@ Status WVDrmPlugin::unprovisionDevice() {
output.resize(in_input.size()); output.resize(in_input.size());
Status status = Status::OK; Status status = Status::OK;
res = mCrypto->encrypt(cryptoSession.oecSessionId(), in_input.data(), res = mCrypto->encrypt(cryptoSession->oecSessionId(), in_input.data(),
in_input.size(), in_iv.data(), in_input.size(), in_iv.data(),
cryptoSession.cipherAlgorithm(), output.data()); cryptoSession->cipherAlgorithm(), output.data());
*_aidl_return = output; *_aidl_return = output;
if (res == OEMCrypto_SUCCESS) { if (res == OEMCrypto_SUCCESS) {
@@ -1506,18 +1507,17 @@ Status WVDrmPlugin::unprovisionDevice() {
*_aidl_return = output; *_aidl_return = output;
CdmSessionId cdmSessionId(in_sessionId.begin(), in_sessionId.end()); CdmSessionId cdmSessionId(in_sessionId.begin(), in_sessionId.end());
if (!mCryptoSessions.count(cdmSessionId)) { const shared_ptr<CryptoSession> cryptoSession = mCryptoSessions.get(cdmSessionId);
if (cryptoSession == nullptr) {
return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED); return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED);
} }
const CryptoSession& cryptoSession = mCryptoSessions[cdmSessionId]; if (cryptoSession->cipherAlgorithm() == kInvalidCryptoAlgorithm) {
if (cryptoSession.cipherAlgorithm() == kInvalidCryptoAlgorithm) {
ALOGW("Returns UNKNOWN error for legacy status NO_INIT"); ALOGW("Returns UNKNOWN error for legacy status NO_INIT");
return toNdkScopedAStatus(Status::ERROR_DRM_UNKNOWN); return toNdkScopedAStatus(Status::ERROR_DRM_UNKNOWN);
} }
OEMCryptoResult res = mCrypto->selectKey(cryptoSession.oecSessionId(), OEMCryptoResult res = mCrypto->selectKey(cryptoSession->oecSessionId(),
in_keyId.data(), in_keyId.size()); in_keyId.data(), in_keyId.size());
if (res != OEMCrypto_SUCCESS) { if (res != OEMCrypto_SUCCESS) {
@@ -1528,9 +1528,9 @@ Status WVDrmPlugin::unprovisionDevice() {
output.resize(in_input.size()); output.resize(in_input.size());
Status status = Status::OK; Status status = Status::OK;
res = mCrypto->decrypt(cryptoSession.oecSessionId(), in_input.data(), res = mCrypto->decrypt(cryptoSession->oecSessionId(), in_input.data(),
in_input.size(), in_iv.data(), in_input.size(), in_iv.data(),
cryptoSession.cipherAlgorithm(), output.data()); cryptoSession->cipherAlgorithm(), output.data());
*_aidl_return = output; *_aidl_return = output;
if (res == OEMCrypto_SUCCESS) { if (res == OEMCrypto_SUCCESS) {
@@ -1550,18 +1550,17 @@ Status WVDrmPlugin::unprovisionDevice() {
*_aidl_return = signature; *_aidl_return = signature;
CdmSessionId cdmSessionId(in_sessionId.begin(), in_sessionId.end()); CdmSessionId cdmSessionId(in_sessionId.begin(), in_sessionId.end());
if (!mCryptoSessions.count(cdmSessionId)) { const shared_ptr<CryptoSession> cryptoSession = mCryptoSessions.get(cdmSessionId);
if (cryptoSession == nullptr) {
return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED); return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED);
} }
const CryptoSession& cryptoSession = mCryptoSessions[cdmSessionId]; if (cryptoSession->macAlgorithm() == kInvalidCryptoAlgorithm) {
if (cryptoSession.macAlgorithm() == kInvalidCryptoAlgorithm) {
ALOGW("Returns UNKNOWN error for legacy status NO_INIT"); ALOGW("Returns UNKNOWN error for legacy status NO_INIT");
return toNdkScopedAStatus(Status::ERROR_DRM_UNKNOWN); return toNdkScopedAStatus(Status::ERROR_DRM_UNKNOWN);
} }
OEMCryptoResult res = mCrypto->selectKey(cryptoSession.oecSessionId(), OEMCryptoResult res = mCrypto->selectKey(cryptoSession->oecSessionId(),
in_keyId.data(), in_keyId.size()); in_keyId.data(), in_keyId.size());
if (res != OEMCrypto_SUCCESS) { if (res != OEMCrypto_SUCCESS) {
@@ -1571,8 +1570,8 @@ Status WVDrmPlugin::unprovisionDevice() {
size_t signatureSize = 0; size_t signatureSize = 0;
res = mCrypto->sign(cryptoSession.oecSessionId(), in_message.data(), res = mCrypto->sign(cryptoSession->oecSessionId(), in_message.data(),
in_message.size(), cryptoSession.macAlgorithm(), nullptr, in_message.size(), cryptoSession->macAlgorithm(), nullptr,
&signatureSize); &signatureSize);
Status status = Status::OK; Status status = Status::OK;
@@ -1591,8 +1590,8 @@ Status WVDrmPlugin::unprovisionDevice() {
signature.resize(signatureSize); signature.resize(signatureSize);
res = mCrypto->sign(cryptoSession.oecSessionId(), in_message.data(), res = mCrypto->sign(cryptoSession->oecSessionId(), in_message.data(),
in_message.size(), cryptoSession.macAlgorithm(), in_message.size(), cryptoSession->macAlgorithm(),
signature.data(), &signatureSize); signature.data(), &signatureSize);
*_aidl_return = signature; *_aidl_return = signature;
@@ -1614,18 +1613,17 @@ Status WVDrmPlugin::unprovisionDevice() {
*_aidl_return = match; *_aidl_return = match;
CdmSessionId cdmSessionId(in_sessionId.begin(), in_sessionId.end()); CdmSessionId cdmSessionId(in_sessionId.begin(), in_sessionId.end());
if (!mCryptoSessions.count(cdmSessionId)) { const shared_ptr<CryptoSession> cryptoSession = mCryptoSessions.get(cdmSessionId);
if (cryptoSession == nullptr) {
return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED); return toNdkScopedAStatus(Status::ERROR_DRM_SESSION_NOT_OPENED);
} }
const CryptoSession& cryptoSession = mCryptoSessions[cdmSessionId]; if (cryptoSession->macAlgorithm() == kInvalidCryptoAlgorithm) {
if (cryptoSession.macAlgorithm() == kInvalidCryptoAlgorithm) {
ALOGW("Returns UNKNOWN error for legacy status NO_INIT"); ALOGW("Returns UNKNOWN error for legacy status NO_INIT");
return toNdkScopedAStatus(Status::ERROR_DRM_UNKNOWN); return toNdkScopedAStatus(Status::ERROR_DRM_UNKNOWN);
} }
OEMCryptoResult res = mCrypto->selectKey(cryptoSession.oecSessionId(), OEMCryptoResult res = mCrypto->selectKey(cryptoSession->oecSessionId(),
in_keyId.data(), in_keyId.size()); in_keyId.data(), in_keyId.size());
if (res != OEMCrypto_SUCCESS) { if (res != OEMCrypto_SUCCESS) {
@@ -1633,8 +1631,8 @@ Status WVDrmPlugin::unprovisionDevice() {
return toNdkScopedAStatus(mapAndNotifyOfOEMCryptoResult(in_sessionId, res)); return toNdkScopedAStatus(mapAndNotifyOfOEMCryptoResult(in_sessionId, res));
} }
res = mCrypto->verify(cryptoSession.oecSessionId(), in_message.data(), res = mCrypto->verify(cryptoSession->oecSessionId(), in_message.data(),
in_message.size(), cryptoSession.macAlgorithm(), in_message.size(), cryptoSession->macAlgorithm(),
in_signature.data(), in_signature.size()); in_signature.data(), in_signature.size());
Status status = Status::OK; Status status = Status::OK;
@@ -1703,7 +1701,7 @@ Status WVDrmPlugin::unprovisionDevice() {
} }
::ndk::ScopedAStatus WVDrmPlugin::setListener( ::ndk::ScopedAStatus WVDrmPlugin::setListener(
const std::shared_ptr<::aidl::android::hardware::drm::IDrmPluginListener>& const shared_ptr<::aidl::android::hardware::drm::IDrmPluginListener>&
in_listener) { in_listener) {
mListener = in_listener; mListener = in_listener;
::ndk::ScopedAStatus _aidl_status; ::ndk::ScopedAStatus _aidl_status;