Reset crypto session pointers on RemoveKeys.

[ Merge of http://go/wvgerrit/189650 ]

The CDM session shares its CryptoSession instance with a few additional
member objects (CdmLicense and PolicyEngine).  When the CDM session's
crypto session is reset, it must also reset the CdmLicense and
PolicyEngine otherwise, a potential stale pointer reference may occur.

Test: request_license_test on Oriole
Test: WVTS on Oriole
Bug: 311239278
Change-Id: Ie175513ae652dcd96e12e5e1def574a8a56d5863
This commit is contained in:
Alex Dale
2024-01-02 17:12:11 -08:00
parent 7fd4541eab
commit bb71b1261e
11 changed files with 185 additions and 78 deletions

View File

@@ -81,9 +81,7 @@ CdmSession::CdmSession(wvutil::FileSystem* file_system,
security_level_(kSecurityLevelUninitialized),
requested_security_level_(kLevelDefault),
is_initial_usage_update_(true),
is_usage_update_needed_(false),
mock_license_parser_in_use_(false),
mock_policy_engine_in_use_(false) {
is_usage_update_needed_(false) {
assert(metrics_); // metrics_ must not be null.
crypto_metrics_ = metrics_->GetCryptoMetrics();
crypto_session_.reset(CryptoSession::MakeCryptoSession(crypto_metrics_));
@@ -866,18 +864,10 @@ CdmResponseType CdmSession::DeleteUsageEntry(uint32_t usage_entry_index) {
// The usage entry cannot be deleted if it has a crypto session handling
// it, so close and reopen session.
UpdateUsageEntryInformation();
CdmResponseType sts;
crypto_session_->Close();
crypto_session_.reset(CryptoSession::MakeCryptoSession(crypto_metrics_));
M_TIME(sts = crypto_session_->Open(requested_security_level_),
crypto_metrics_, crypto_session_open_, sts, requested_security_level_);
if (sts != NO_ERROR) return sts;
usage_table_ = nullptr;
bool has_support = false;
if (crypto_session_->HasUsageTableSupport(&has_support) && has_support) {
usage_table_ = crypto_session_->GetUsageTable();
}
crypto_session_->Close();
CdmResponseType sts = ResetCryptoSession();
if (sts != NO_ERROR) return sts;
if (usage_table_ == nullptr) {
LOGE("Usage table header unavailable");
@@ -1012,14 +1002,8 @@ bool CdmSession::StoreLicense(CdmOfflineLicenseState state, int* error_detail) {
}
CdmResponseType CdmSession::RemoveKeys() {
CdmResponseType sts;
crypto_session_.reset(CryptoSession::MakeCryptoSession(crypto_metrics_));
// Ignore errors
M_TIME(sts = crypto_session_->Open(requested_security_level_),
crypto_metrics_, crypto_session_open_, sts, requested_security_level_);
policy_engine_.reset(
new PolicyEngine(session_id_, nullptr, crypto_session_.get()));
return CdmResponseType(NO_ERROR);
crypto_session_->Close();
return ResetCryptoSession();
}
CdmResponseType CdmSession::RemoveLicense() {
@@ -1249,7 +1233,7 @@ CdmResponseType CdmSession::LoadPrivateKey(
DrmKeyTypeToMetricValue(private_key.type()));
drm_certificate_ = drm_certificate;
wrapped_private_key_ = std::move(private_key);
wrapped_private_key_ = private_key;
return CdmResponseType(NO_ERROR);
case SESSION_LOST_STATE_ERROR:
case SYSTEM_INVALIDATED_ERROR:
@@ -1313,6 +1297,77 @@ CdmResponseType CdmSession::GenerateRsaSignature(const std::string& message,
return crypto_session_->GenerateRsaSignature(message, signature, scheme);
}
CdmResponseType CdmSession::ResetCryptoSession() {
LOGD("Resetting crypto session: session_id = %s, ksid = %s",
IdToString(session_id_), IdToString(key_set_id_));
if (mock_crypto_session_in_use_) {
// If the crypto session is not reset, then there is nothing to do.
return CdmResponseType(NO_ERROR);
}
CdmResponseType sts;
crypto_session_.reset(CryptoSession::MakeCryptoSession(crypto_metrics_));
usage_table_ = nullptr;
M_TIME(sts = crypto_session_->Open(requested_security_level_),
crypto_metrics_, crypto_session_open_, sts, requested_security_level_);
CdmResponseType final_sts(NO_ERROR);
if (sts != NO_ERROR) {
// Challenging case, still need to reset other components.
LOGE("Failed to open crypto session: sts = %s", sts.ToString().c_str());
final_sts = sts;
} else {
// Reset all component dependent on the crypto session.
security_level_ = crypto_session_->GetSecurityLevel();
crypto_metrics_->crypto_session_security_level_.Record(security_level_);
if (!file_handle_->Init(security_level_)) {
LOGE("Unable to initialize file handle");
final_sts = CdmResponseType(SESSION_FILE_HANDLE_INIT_ERROR);
}
if (!file_handle_->HasCertificate(atsc_mode_enabled_)) {
LOGE("Missing certificate: atsc_mode_enabled = %s",
BoolToString(atsc_mode_enabled_));
final_sts = CdmResponseType(NEED_PROVISIONING);
}
bool has_support = false;
if (crypto_session_->HasUsageTableSupport(&has_support) && has_support) {
usage_table_ = crypto_session_->GetUsageTable();
}
}
// Even if the session is not open, other members need new session pointer.
if (mock_policy_engine_in_use_) {
// Simply pass the new pointer.
policy_engine_->set_crypto_session(crypto_session_.get());
} else {
// Attempt to maintain event listener.
WvCdmEventListener* event_listener =
policy_engine_ ? policy_engine_->event_listener() : nullptr;
policy_engine_.reset(
new PolicyEngine(session_id_, event_listener, crypto_session_.get()));
}
if (mock_license_parser_in_use_) {
license_parser_->set_crypto_session(crypto_session_.get());
license_parser_->set_policy_engine(policy_engine_.get());
} else {
license_parser_.reset(new CdmLicense(session_id_));
std::string service_certificate;
if (!Properties::GetServiceCertificate(session_id_, &service_certificate))
service_certificate.clear();
if (!license_parser_->Init(Properties::UsePrivacyMode(session_id_),
service_certificate, crypto_session_.get(),
policy_engine_.get())) {
LOGE("Failed to initialize license parser");
final_sts = CdmResponseType(LICENSE_PARSER_INIT_ERROR);
}
}
return final_sts;
}
// For testing only - takes ownership of pointers
void CdmSession::set_license_parser(CdmLicense* license_parser) {
@@ -1322,6 +1377,7 @@ void CdmSession::set_license_parser(CdmLicense* license_parser) {
void CdmSession::set_crypto_session(CryptoSession* crypto_session) {
crypto_session_.reset(crypto_session);
mock_crypto_session_in_use_ = true;
}
void CdmSession::set_policy_engine(PolicyEngine* policy_engine) {

View File

@@ -195,9 +195,7 @@ std::vector<CryptoKey> ExtractContentKeys(const License& license) {
} // namespace
CdmLicense::CdmLicense(const CdmSessionId& session_id)
: crypto_session_(nullptr),
policy_engine_(nullptr),
session_id_(session_id),
: session_id_(session_id),
initialized_(false),
renew_with_client_id_(false),
is_offline_(false),
@@ -206,15 +204,17 @@ CdmLicense::CdmLicense(const CdmSessionId& session_id)
license_key_type_(kLicenseKeyTypeContent) {}
CdmLicense::CdmLicense(const CdmSessionId& session_id, wvutil::Clock* clock)
: crypto_session_(nullptr),
policy_engine_(nullptr),
session_id_(session_id),
: session_id_(session_id),
initialized_(false),
renew_with_client_id_(false),
is_offline_(false),
use_privacy_mode_(false),
clock_(clock),
license_key_type_(kLicenseKeyTypeContent) {
clock_.reset(clock);
if (!clock_) {
LOGW("Input |clock| is null, using default");
clock_.reset(new wvutil::Clock());
}
}
CdmLicense::~CdmLicense() {}

View File

@@ -39,9 +39,12 @@ PolicyEngine::PolicyEngine(CdmSessionId session_id,
if (version >= kMinOemCryptoApiVersionSupportsRenewalDelayBase) {
policy_timers_.reset(new PolicyTimersV18());
}
} else {
LOGW("Failed to get API version: session_id = %s", IdToString(session_id));
}
if (policy_timers_ == nullptr) {
if (!policy_timers_) {
// Use V16 policy timers if getting version failed.
policy_timers_.reset(new PolicyTimersV16());
}
InitDevice(crypto_session);

View File

@@ -12,6 +12,8 @@ namespace wvcdm {
namespace {
const char kEmptyIdRep[] = "<empty>";
const char kNullIdRep[] = "<null>";
const char kFalseRep[] = "false";
const char kTrueRep[] = "true";
// Thread local buffer used by UnknownEnumValueToString() to represent
// unknown enum values.
@@ -892,6 +894,8 @@ const char* IdPtrToString(const std::string* id) {
return id->empty() ? kEmptyIdRep : id->c_str();
}
const char* BoolToString(bool value) { return value ? kTrueRep : kFalseRep; }
const char* OemCryptoResultToString(OEMCryptoResult result) {
switch (result) {
/* OEMCrypto return values */