Files
android/libwvdrmengine/cdm/include/wv_content_decryption_module.h
Alex Dale d7eab65c8c Fixed dangling pointer issue in CdmInfo.
[ Cherry-pick of http://ag/34331848 ]
[ Merge of http://go/wvgerrit/224951 ]

There was a potential dangling pointer issue that was enabled
by how CdmInfo is initilized.  The file system that was passed
into the CdmEngine instance was pointing to a location in memory
that was not stable between move operations in the CdmInfo.
See b/429054262 for memory diagram of issue.

The CdmInfo is a private class within the Android CDM class, which
restricts the potential operations on it.  The easiest solution
is wrap the file system in a unique pointer; ensuring the pointer
remains stable even if a particular data segment of CdmInfo is
moved.

The default constructor for CdmInfo is deleted; this will force the
compiler to fail if |cdms_| is used in ways that would result in
uninitialized pointers.

Bug: 429054262
Test: WvTs on Komodo
Change-Id: I76a49fc5181ebd1613e238aa49986083a9f397ec
(cherry picked from commit 4c105faa4923bd9bd6352f757dedf3eaf9ed88fd)
2025-07-08 15:53:39 -07:00

394 lines
17 KiB
C++

// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
#ifndef CDM_BASE_WV_CONTENT_DECRYPTION_MODULE_H_
#define CDM_BASE_WV_CONTENT_DECRYPTION_MODULE_H_
#include <time.h>
#include <map>
#include <memory>
#include <mutex>
#include "utils/RefBase.h"
#include "cdm_identifier.h"
#include "disallow_copy_and_assign.h"
#include "file_store.h"
#include "timer.h"
#include "wv_cdm_types.h"
#include "wv_metrics.pb.h"
namespace wvcdm {
class CdmClientPropertySet;
class CdmEngine;
class WvCdmEventListener;
class WvMetricsSnapshot {
public:
WvMetricsSnapshot(const CdmIdentifier& identifier,
drm_metrics::WvCdmMetrics&& metrics, ::time_t ts);
// Acts as a constructor, but uses the current time.
static WvMetricsSnapshot MakeSnapshot(const CdmIdentifier& identifier,
drm_metrics::WvCdmMetrics&& metrics);
WvMetricsSnapshot() = default;
WvMetricsSnapshot(const WvMetricsSnapshot&) = default;
WvMetricsSnapshot(WvMetricsSnapshot&&) = default;
WvMetricsSnapshot& operator=(const WvMetricsSnapshot&) = default;
WvMetricsSnapshot& operator=(WvMetricsSnapshot&&) = default;
const CdmIdentifier& cdm_id() const { return cdm_id_; }
const drm_metrics::WvCdmMetrics& metrics() const { return metrics_; }
::time_t timestamp() const { return timestamp_; }
std::string GetFormattedTimestamp() const;
private:
CdmIdentifier cdm_id_;
drm_metrics::WvCdmMetrics metrics_;
::time_t timestamp_ = 0;
};
class WvMetricsSnapshotQueue
: public android::LightRefBase<WvMetricsSnapshotQueue> {
public:
WvMetricsSnapshotQueue();
explicit WvMetricsSnapshotQueue(size_t capacity) : capacity_(capacity) {}
size_t capacity() const { return capacity_; }
size_t size() const { return snapshots_.size(); }
WvMetricsSnapshotQueue(const WvMetricsSnapshotQueue&) = delete;
WvMetricsSnapshotQueue(WvMetricsSnapshotQueue&&) = delete;
WvMetricsSnapshotQueue& operator=(const WvMetricsSnapshotQueue&) = delete;
WvMetricsSnapshotQueue& operator=(WvMetricsSnapshotQueue&&) = delete;
void PushMetrics(WvMetricsSnapshot&& snapshot);
void PushMetrics(const WvMetricsSnapshot& snapshot);
bool GetAll(std::vector<WvMetricsSnapshot>* snapshots) const;
void Clear();
private:
void ReFit();
const size_t capacity_;
mutable std::mutex mutex_;
std::deque<WvMetricsSnapshot> snapshots_;
};
class WvContentDecryptionModule : public android::RefBase, public TimerHandler {
public:
WvContentDecryptionModule();
explicit WvContentDecryptionModule(WvMetricsSnapshotQueue* metrics_queue);
virtual ~WvContentDecryptionModule();
// Static methods
static bool IsSupported(const std::string& init_data_type);
static bool IsCenc(const std::string& init_data_type);
static bool IsWebm(const std::string& init_data_type);
static bool IsHls(const std::string& init_data_type);
static bool IsAudio(const std::string& init_data_type);
// Session related methods
virtual CdmResponseType OpenSession(const CdmKeySystem& key_system,
CdmClientPropertySet* property_set,
const CdmIdentifier& identifier,
WvCdmEventListener* event_listener,
CdmSessionId* session_id);
virtual CdmResponseType CloseSession(const CdmSessionId& session_id);
virtual bool IsOpenSession(const CdmSessionId& session_id);
// Construct a valid license request.
virtual CdmResponseType GenerateKeyRequest(
const CdmSessionId& session_id, const CdmKeySetId& key_set_id,
const std::string& init_data_type, const CdmInitData& init_data,
const CdmLicenseType license_type, CdmAppParameterMap& app_parameters,
CdmClientPropertySet* property_set, const CdmIdentifier& identifier,
CdmKeyRequest* key_request);
// Accept license response and extract key info.
virtual CdmResponseType AddKey(const CdmSessionId& session_id,
const CdmKeyResponse& key_data,
CdmKeySetId* key_set_id);
// Setup keys for offline usage which were retrived in an earlier key request
virtual CdmResponseType RestoreKey(const CdmSessionId& session_id,
const CdmKeySetId& key_set_id);
// Cancel session
virtual CdmResponseType RemoveKeys(const CdmSessionId& session_id);
// Query system information
virtual CdmResponseType QueryStatus(RequestedSecurityLevel security_level,
const std::string& key,
std::string* value);
// Query session information
virtual CdmResponseType QuerySessionStatus(const CdmSessionId& session_id,
CdmQueryMap* key_info);
// Query license information
virtual CdmResponseType QueryKeyStatus(const CdmSessionId& session_id,
CdmQueryMap* key_info);
// Query device CSR information for Provisioning 4.0
virtual CdmResponseType QueryDeviceSignedCsrPayload(
const std::string& challenge, const std::string& device_info,
std::string* value);
// Query OEMCrypto session ID
virtual CdmResponseType QueryOemCryptoSessionId(
const CdmSessionId& session_id, CdmQueryMap* response);
// Query security level support
static bool IsSecurityLevelSupported(CdmSecurityLevel level);
// Provisioning related methods
virtual CdmResponseType GetProvisioningRequest(
CdmCertificateType cert_type, const std::string& cert_authority,
const CdmIdentifier& identifier, const std::string& service_certificate,
RequestedSecurityLevel requested_security_level,
CdmProvisioningRequest* request, std::string* default_url);
virtual CdmResponseType HandleProvisioningResponse(
const CdmIdentifier& identifier, const CdmProvisioningResponse& response,
RequestedSecurityLevel requested_security_level, std::string* cert,
std::string* wrapped_key);
virtual CdmResponseType Unprovision(CdmSecurityLevel level,
const CdmIdentifier& identifier);
virtual bool IsProvisioned(CdmSecurityLevel level, const std::string& origin,
const std::string& spoid, bool atsc_mode_enabled);
// Secure stop related methods
virtual CdmResponseType GetUsageInfo(const std::string& app_id,
const CdmIdentifier& identifier,
CdmUsageReportList* usage_reports);
virtual CdmResponseType GetUsageInfo(const std::string& app_id,
const CdmSecureStopId& ssid,
const CdmIdentifier& identifier,
CdmUsageReport* usage_report);
virtual CdmResponseType RemoveAllUsageInfo(const std::string& app_id,
const CdmIdentifier& identifier);
virtual CdmResponseType RemoveUsageInfo(
const std::string& app_id, const CdmIdentifier& identifier,
const CdmSecureStopId& secure_stop_id);
virtual CdmResponseType ReleaseUsageInfo(const CdmKeyResponse& message,
const CdmIdentifier& identifier);
virtual CdmResponseType GetSecureStopIds(const std::string& app_id,
const CdmIdentifier& identifier,
std::vector<CdmSecureStopId>* ssids);
// Accept encrypted buffer and decrypt data.
// Decryption parameters that need to be specified are
// is_encrypted, is_secure, key_id, encrypt_buffer, encrypt_length,
// iv, block_offset, decrypt_buffer, decrypt_buffer_length,
// decrypt_buffer_offset and subsample_flags
virtual CdmResponseType Decrypt(const CdmSessionId& session_id,
bool validate_key_id,
const CdmDecryptionParameters& parameters);
// Accept encrypted sample and decrypt data.
virtual CdmResponseType DecryptV16(
const CdmSessionId& session_id, bool validate_key_id,
const CdmDecryptionParametersV16& parameters);
virtual void NotifyResolution(const CdmSessionId& session_id, uint32_t width,
uint32_t height);
// Validate a passed-in service certificate
virtual bool IsValidServiceCertificate(const std::string& certificate);
// Fill the |metrics| parameter with the engine metrics data for the
// CdmEngine associated with the given CdmIdentifier.
// If the CdmEngine instance does not exist, this will return an error.
virtual CdmResponseType GetCurrentMetrics(const CdmIdentifier& identifier,
drm_metrics::WvCdmMetrics* metrics);
// Fill the |snapshots| parameter wrapped engine metrics for all CdmEngine
// instances that currently enabled (actively decrypting or performing
// other engine-level operations).
// Current metrics snapshots contain the engine identifier, metrics data
// and time (roughly current time).
// |full_list_returned| will indicate whether all existing enginers were
// able to report their metrics successfully.
virtual CdmResponseType GetAllCurrentMetricsSnapshots(
std::vector<WvMetricsSnapshot>* snapshots, bool* full_list_returned);
// Fill the |snapshots| parameter wrapped engine metrics for several of
// the most recently closed CdmEngine instances.
// Saved metrics snapshots contain the engine identifier, metrics data
// and time of closing.
// The same engine identifier may appear multiple times in the list (
// depending on how the app utilizes the MediaDrm plugin).
virtual CdmResponseType GetAllSavedMetricsSnapshots(
std::vector<WvMetricsSnapshot>* snapshots);
// Closes the CdmEngine and sessions associated with the given CdmIdentifier.
virtual CdmResponseType CloseCdm(const CdmIdentifier& identifier);
// When positive, the keybox will be ignored at initialization and force the
// device to request a keybox OTA reprovisioning.
virtual CdmResponseType SetDebugIgnoreKeyboxCount(uint32_t count);
virtual CdmResponseType SetDecryptHash(const std::string& hash_data,
CdmSessionId* session_id);
virtual CdmResponseType GetDecryptHashError(const CdmSessionId& session_id,
std::string* hash_error_string);
// Return the list of key_set_ids stored on the current (origin-specific)
// file system.
virtual CdmResponseType ListStoredLicenses(
CdmSecurityLevel security_level, const CdmIdentifier& identifier,
std::vector<CdmKeySetId>* key_set_ids);
// Retrieve offline license state using key_set_id.
virtual CdmResponseType GetOfflineLicenseState(
const CdmKeySetId& key_set_id, CdmSecurityLevel security_level,
const CdmIdentifier& identifier, CdmOfflineLicenseState* licenseState);
// Remove offline license using key_set_id.
virtual CdmResponseType RemoveOfflineLicense(const CdmKeySetId& key_set_id,
CdmSecurityLevel security_level,
const CdmIdentifier& identifier);
virtual CdmResponseType SetPlaybackId(const CdmSessionId& session_id,
const std::string& playback_id);
virtual CdmResponseType GetSessionUserId(const CdmSessionId& session_id,
uint32_t* user_id);
virtual CdmResponseType StoreAtscLicense(
const CdmIdentifier& identifier, RequestedSecurityLevel security_level,
const CdmKeySetId& key_set_id,
const std::string& serialized_license_data);
virtual bool SetDefaultOtaKeyboxFallbackDurationRules();
virtual bool SetFastOtaKeyboxFallbackDurationRules();
// Generic crypto API.
virtual CdmResponseType GenericEncrypt(const CdmSessionId& session_id,
const KeyId& key_id,
const std::string& input,
const std::string& iv,
CdmEncryptionAlgorithm algorithm,
std::string* output);
virtual CdmResponseType GenericDecrypt(const CdmSessionId& session_id,
const KeyId& key_id,
const std::string& input,
const std::string& iv,
CdmEncryptionAlgorithm algorithm,
std::string* output);
virtual CdmResponseType GenericSign(const CdmSessionId& session_id,
const KeyId& key_id,
const std::string& input,
CdmSigningAlgorithm algorithm,
std::string* signature);
virtual CdmResponseType GenericVerify(const CdmSessionId& session_id,
const KeyId& key_id,
const std::string& input,
CdmSigningAlgorithm algorithm,
const std::string& signature);
private:
class CdmInfo {
public:
// This should never be used.
CdmInfo() = delete;
// It is expected that the filesystem loaded into |cdm_engine|
// is the same instance as |file_system|.
CdmInfo(std::unique_ptr<wvutil::FileSystem>&& file_system,
std::unique_ptr<CdmEngine>&& cdm_engine);
// No copy operators.
CdmInfo(const CdmInfo&) = delete;
CdmInfo& operator=(const CdmInfo&) = delete;
// Move operators OK.
CdmInfo(CdmInfo&&) = default;
CdmInfo& operator==(CdmInfo&& other);
wvutil::FileSystem* file_system() { return file_system_.get(); }
CdmEngine* cdm_engine() { return cdm_engine_.get(); }
private:
// Order matters, |cdm_engine_| is expected to contain a pointer
// to |file_system_|.
std::unique_ptr<wvutil::FileSystem> file_system_;
std::unique_ptr<CdmEngine> cdm_engine_;
};
// Finds the CdmEngine instance for the given identifier, creating one if
// needed.
CdmEngine* EnsureCdmForIdentifier(const CdmIdentifier& identifier);
// Finds the CdmEngine instance for the given session id, returning nullptr
// if not found.
CdmEngine* GetCdmForSessionId(const std::string& session_id);
// Close all of the open CdmEngine instances. This is used when ready to close
// the WvContentDecryptionModule instance.
void CloseAllCdms();
uint32_t GenerateSessionSharingId();
// Timer related methods to drive policy decisions
void EnableTimer();
void DisableTimer();
// Call this method only if invoked from a thread other than the timer thread.
// Otherwise this might result in deadlock.
void DisableTimerAndWaitForExit();
void OnTimerEvent();
// Fill the |metrics| parameter with the metrics data for the CdmEngine
// associated with the given |identifier|. If the CdmEngine instance does
// not exist, this will return an error.
// This methods assumes that |metrics| is not null and that the |cdms_lock_|
// has already been acquired.
CdmResponseType GetCurrentMetricsInternal(const CdmIdentifier& identifier,
drm_metrics::WvCdmMetrics* metrics);
void SaveMetrics(const CdmIdentifier& identifier,
drm_metrics::WvCdmMetrics&& metrics);
static std::mutex session_sharing_id_generation_lock_;
std::mutex timer_lock_;
Timer timer_;
// instance variables
// This manages the lifetime of the CDM instances.
std::map<CdmIdentifier, CdmInfo> cdms_;
// This contains weak pointers to the CDM instances contained in |cdms_|.
std::map<std::string, CdmEngine*> cdm_by_session_id_;
// Lock for accessing either |cdms_| or |cdm_by_session_id_|.
// This lock should be acquired for any of the following:
// 1) Getting a CDM instance from |cdms_| or |cdm_by_session_id_|
// - Hold lock while searching, release lock once pointer is acquired
// 2) Iterating over |cdms_| or |cdm_by_session_id_|
// - Hold lock for the entire duration of iterating over CDMs
// - DO NOT release the lock until all operations are complete
// - This MUST be done regardless of whether |cdms_| or
// |cdm_by_session_id_| is being modified
// 3) Creating a new CDM instance with |cdms_|
// - Hold lock when creating AND initializing the CDM
// - Release once CDM is fully initialized
// 4) Linking a session to a CDM with |cdm_by_session_id_|
// - Hold lock when mapping a session ID to a CDM, release once set
// 5) Unlinking a session from a CDM with |cdm_by_session_id_|
// - Hold lock when erasing, release once erased.
std::mutex cdms_lock_;
// Contains a finite list of histories of different CDM engine instances.
// When a CDM engine is closed, its metrics will be saved.
// Only used if not null.
WvMetricsSnapshotQueue* saved_metrics_snapshots_ = nullptr;
CORE_DISALLOW_COPY_AND_ASSIGN(WvContentDecryptionModule);
};
} // namespace wvcdm
#endif // CDM_BASE_WV_CONTENT_DECRYPTION_MODULE_H_