Prevent segfaults when a session is deallocated

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

Race conditions arose when a session was closed while data was
still queued for decryption in MediaCodec buffers. If a session
is closed while data is still queued for decryption, subsequent
decryption requests will be rejected with a CryptoException
ERROR_SESSION_NOT_OPENED.

Test: Verified by wv unit/integration test and
      WvCdmExtendedDurationTest.DecryptionCloseSessionConcurrencyTest

b/36747801

Change-Id: I044d1d6b9fc886a1c353d20b9c6365319aa71e80
This commit is contained in:
Rahul Frias
2017-04-19 17:20:08 -07:00
parent c2969ef0f4
commit f2fccc20cd
2 changed files with 107 additions and 0 deletions

View File

@@ -22,6 +22,7 @@
#include "string_conversions.h"
#include "test_base.h"
#include "url_request.h"
#include <utils/Thread.h>
#include "wv_cdm_constants.h"
#include "wv_content_decryption_module.h"
@@ -678,6 +679,43 @@ class WvCdmExtendedDurationTest : public WvCdmTestBase {
return security_level;
}
class CloseSessionThread : public android::Thread {
public:
CloseSessionThread() :
Thread(false),
wv_content_decryption_module_(NULL) {}
~CloseSessionThread() {}
bool Start(WvContentDecryptionModule* decryptor,
const CdmSessionId& session_id,
uint32_t time_in_msecs) {
wv_content_decryption_module_ = decryptor;
sess_id_ = session_id;
delay_.tv_sec = time_in_msecs / 1000;
delay_.tv_nsec = (time_in_msecs % 1000) * 10000000ll;
return run("CloseSessionThread") == android::NO_ERROR;
}
void Stop() { requestExitAndWait(); }
private:
virtual bool threadLoop() {
struct timespec delay_remaining;
int result = nanosleep(&delay_, &delay_remaining);
while (result < 0 &&
(delay_remaining.tv_sec > 0 || delay_remaining.tv_nsec > 0)) {
result = nanosleep(&delay_remaining, &delay_remaining);
}
wv_content_decryption_module_->CloseSession(sess_id_);
return false;
}
WvContentDecryptionModule* wv_content_decryption_module_;
CdmSessionId sess_id_;
struct timespec delay_;
CORE_DISALLOW_COPY_AND_ASSIGN(CloseSessionThread);
};
WvContentDecryptionModule decryptor_;
CdmKeyMessage key_msg_;
CdmKeyResponse key_response_;
@@ -831,6 +869,74 @@ TEST_F(WvCdmExtendedDurationTest, VerifyLicenseRenewalTest) {
decryptor_.CloseSession(session_id_);
}
TEST_F(WvCdmExtendedDurationTest, DecryptionCloseSessionConcurrencyTest) {
Unprovision();
Provision();
// Leave session open to avoid CDM termination
CdmSessionId session_id;
decryptor_.OpenSession(g_key_system, NULL, kDefaultCdmIdentifier, NULL,
&session_id);
// Retrieve offline license
decryptor_.OpenSession(g_key_system, NULL, kDefaultCdmIdentifier, NULL,
&session_id_);
GenerateKeyRequest(kOfflineClip2PstInitData, kLicenseTypeOffline);
VerifyKeyRequestResponse(g_license_server, g_client_auth, false);
EXPECT_FALSE(key_set_id_.empty());
decryptor_.CloseSession(session_id_);
for (uint32_t j = 0; j < 500; ++j) {
decryptor_.OpenSession(g_key_system, NULL, kDefaultCdmIdentifier, NULL,
&session_id_);
EXPECT_EQ(KEY_ADDED, decryptor_.RestoreKey(session_id_, key_set_id_));
CdmResponseType status = NO_ERROR;
struct timespec decrypt_delay;
decrypt_delay.tv_sec = 0;
decrypt_delay.tv_nsec = 10000000ll; // 10 ms
CloseSessionThread* thread = new CloseSessionThread();
thread->Start(&decryptor_, session_id_, 500 /* 500 ms */);
thread = NULL;
while (status == NO_ERROR) {
struct timespec delay_remaining;
int result = nanosleep(&decrypt_delay, &delay_remaining);
while (result < 0 &&
(delay_remaining.tv_sec > 0 || delay_remaining.tv_nsec > 0)) {
result = nanosleep(&delay_remaining, &delay_remaining);
}
SubSampleInfo* data = &kEncryptedOfflineClip2SubSample;
for (size_t i = 0; i < data->num_of_subsamples; i++) {
std::vector<uint8_t> decrypt_buffer((data + i)->encrypt_data.size());
CdmDecryptionParameters decryption_parameters(
&(data + i)->key_id, &(data + i)->encrypt_data.front(),
(data + i)->encrypt_data.size(), &(data + i)->iv,
(data + i)->block_offset, &decrypt_buffer[0]);
decryption_parameters.is_encrypted = (data + i)->is_encrypted;
decryption_parameters.is_secure = (data + i)->is_secure;
decryption_parameters.subsample_flags = (data + i)->subsample_flags;
status = decryptor_.Decrypt(session_id_, (data + i)->validate_key_id,
decryption_parameters);
switch (status) {
case SESSION_NOT_FOUND_FOR_DECRYPT:
break;
case NO_ERROR:
EXPECT_EQ((data + i)->decrypt_data, decrypt_buffer);
break;
default:
EXPECT_TRUE(false);
}
}
}
}
decryptor_.CloseSession(session_id);
}
TEST_F(WvCdmExtendedDurationTest, UsageOverflowTest) {
Provision();
SubSampleInfo* data = &kEncryptedStreamingClip5SubSample;