Merge "Added numeric value guards to ValueMetric."

This commit is contained in:
TreeHugger Robot
2021-11-12 05:42:43 +00:00
committed by Android (Google) Code Review
3 changed files with 106 additions and 63 deletions

View File

@@ -15,109 +15,103 @@
namespace wvcdm { namespace wvcdm {
namespace metrics { namespace metrics {
// Internal namespace for helper methods. namespace internal {
namespace impl {
// Helper function for setting a value in the proto. // Helper function for setting a value in the proto.
template <typename T> template <typename T>
void SetValue(drm_metrics::ValueMetric* value_proto, const T& value); void SetValue(drm_metrics::ValueMetric* value_proto, const T& value);
} // namespace impl } // namespace internal
// The ValueMetric class supports storing a single, overwritable value or an // The ValueMetric class supports storing a single, overwritable value or an
// error code. This class can be serialized to a drm_metrics::ValueMetric proto. // error code. This class can be serialized to a drm_metrics::ValueMetric proto.
// If an error or value was not provided, this metric will serialize to nullptr. // If an error or value was not provided, this metric will serialize to nullptr.
// //
// Example Usage: // Example Usage:
// Metric<string> cdm_version().Record("a.b.c.d"); // ValueMetric<string> cdm_version().Record("a.b.c.d");
// std::unique_ptr<drm_metrics::ValueMetric> mymetric( // std::unique_ptr<drm_metrics::ValueMetric> mymetric(
// cdm_version.ToProto()); // cdm_version.ToProto());
// //
// Example Error Usage: // Example Error Usage:
// //
// Metric<string> cdm_version().SetError(error_code); // ValueMetric<string> cdm_version().SetError(error_code);
// std::unique_ptr<drm_metrics::ValueMetric> mymetric( // std::unique_ptr<drm_metrics::ValueMetric> mymetric(
// cdm_version.ToProto()); // cdm_version.ToProto());
// //
template <typename T> template <typename T>
class ValueMetric { class ValueMetric {
public: public:
// Constructs a metric with the given metric name. ValueMetric() {}
explicit ValueMetric()
: error_code_(0), has_error_(false), has_value_(false) {}
// Record the value of the metric. // Record the value of the metric.
void Record(const T& value) { void Record(const T& value) {
std::unique_lock<std::mutex> lock(internal_lock_); std::unique_lock<std::mutex> lock(mutex_);
value_ = value; value_ = value;
has_value_ = true; state_ = kHasValue;
has_error_ = false;
} }
// Set the error code if an error was encountered. // Set the error code if an error was encountered.
void SetError(int error_code) { void SetError(int error_code) {
std::unique_lock<std::mutex> lock(internal_lock_); std::unique_lock<std::mutex> lock(mutex_);
error_code_ = error_code; error_code_ = error_code;
has_value_ = false; state_ = kHasError;
has_error_ = true;
} }
bool HasValue() const { bool HasValue() const {
std::unique_lock<std::mutex> lock(internal_lock_); std::unique_lock<std::mutex> lock(mutex_);
return has_value_; return state_ == kHasValue;
} }
const T& GetValue() const { const T& GetValue() const {
std::unique_lock<std::mutex> lock(internal_lock_); std::unique_lock<std::mutex> lock(mutex_);
return value_; return value_;
} }
bool HasError() const { bool HasError() const {
std::unique_lock<std::mutex> lock(internal_lock_); std::unique_lock<std::mutex> lock(mutex_);
return has_error_; return state_ == kHasError;
} }
int GetError() const { int GetError() const {
std::unique_lock<std::mutex> lock(internal_lock_); std::unique_lock<std::mutex> lock(mutex_);
return error_code_; return error_code_;
} }
// Clears the indicators that the metric or error was set. // Clears the indicators that the metric or error was set.
void Clear() { void Clear() {
std::unique_lock<std::mutex> lock(internal_lock_); std::unique_lock<std::mutex> lock(mutex_);
has_value_ = false; state_ = kNone;
has_error_ = false;
} }
// Returns a new ValueMetric proto containing the metric value or the // Returns a new ValueMetric proto containing the metric value or the
// error code. If neither the error or value are set, it returns nullptr. // error code. If neither the error or value are set, it returns nullptr.
drm_metrics::ValueMetric* ToProto() const { drm_metrics::ValueMetric* ToProto() const {
std::unique_lock<std::mutex> lock(internal_lock_); std::unique_lock<std::mutex> lock(mutex_);
if (has_error_) { switch (state_) {
drm_metrics::ValueMetric* value_proto = new drm_metrics::ValueMetric; case kHasValue: {
value_proto->set_error_code(error_code_); drm_metrics::ValueMetric* value_proto = new drm_metrics::ValueMetric;
return value_proto; internal::SetValue(value_proto, value_);
} else if (has_value_) { return value_proto;
drm_metrics::ValueMetric* value_proto = new drm_metrics::ValueMetric; }
impl::SetValue(value_proto, value_); case kHasError: {
return value_proto; drm_metrics::ValueMetric* value_proto = new drm_metrics::ValueMetric;
value_proto->set_error_code(error_code_);
return value_proto;
}
case kNone:
default:
return nullptr;
} }
return nullptr;
} }
private: private:
T value_; enum ValueState { kNone, kHasValue, kHasError };
int error_code_;
bool has_error_;
bool has_value_;
/* T value_;
* This locks the internal state of the value metric to ensure safety int error_code_ = 0;
* across multiple threads preventing the caller from worrying about ValueState state_ = kNone;
* locking.
* // This locks the internal state of the value metric to ensure safety
* This field must be declared mutable because the lock must be takeable even // across multiple threads preventing the caller from worrying about
* in const methods. // locking.
*/ mutable std::mutex mutex_;
mutable std::mutex internal_lock_; };
}; // class ValueMetric
} // namespace metrics } // namespace metrics
} // namespace wvcdm } // namespace wvcdm
#endif // WVCDM_METRICS_VALUE_METRIC_H_ #endif // WVCDM_METRICS_VALUE_METRIC_H_

View File

@@ -6,89 +6,111 @@
// ValueMetric class. // ValueMetric class.
#include "value_metric.h" #include "value_metric.h"
#include <limits>
#include "OEMCryptoCENC.h" #include "OEMCryptoCENC.h"
#include "metrics_collections.h" #include "metrics_collections.h"
#include "wv_cdm_types.h" #include "wv_cdm_types.h"
namespace wvcdm { namespace wvcdm {
namespace metrics { namespace metrics {
namespace impl { namespace internal {
namespace {
// Max value of protobuf's int64 type.
constexpr uint64_t kMaxValueCmp =
static_cast<uint64_t>(std::numeric_limits<int64_t>::max());
constexpr int64_t kMaxValue = std::numeric_limits<int64_t>::max();
template <typename T>
constexpr bool WillOverflow(T value) {
return static_cast<uint64_t>(value) > kMaxValueCmp;
}
// Prevents an overflow of very large numbers. If the value attempted to
// be assigned is greater than the maximum value supported by protobuf's
// int64 type, then the value is capped at the max.
template <typename T>
constexpr int64_t SafeAssign(T value) {
return WillOverflow(value) ? kMaxValue : static_cast<int64_t>(value);
}
} // namespace
template <> template <>
void SetValue<int>(drm_metrics::ValueMetric* value_proto, const int& value) { void SetValue<int>(drm_metrics::ValueMetric* value_proto, const int& value) {
value_proto->set_int_value(value); value_proto->set_int_value(static_cast<int64_t>(value));
} }
template <> template <>
void SetValue<long>(drm_metrics::ValueMetric* value_proto, const long& value) { void SetValue<long>(drm_metrics::ValueMetric* value_proto, const long& value) {
value_proto->set_int_value(value); value_proto->set_int_value(static_cast<int64_t>(value));
} }
template <> template <>
void SetValue<long long>(drm_metrics::ValueMetric* value_proto, void SetValue<long long>(drm_metrics::ValueMetric* value_proto,
const long long& value) { const long long& value) {
value_proto->set_int_value(value); value_proto->set_int_value(SafeAssign(value));
} }
template <> template <>
void SetValue<unsigned int>(drm_metrics::ValueMetric* value_proto, void SetValue<unsigned int>(drm_metrics::ValueMetric* value_proto,
const unsigned int& value) { const unsigned int& value) {
value_proto->set_int_value((int64_t)value); value_proto->set_int_value(SafeAssign(value));
} }
template <> template <>
void SetValue<unsigned short>(drm_metrics::ValueMetric* value_proto, void SetValue<unsigned short>(drm_metrics::ValueMetric* value_proto,
const unsigned short& value) { const unsigned short& value) {
value_proto->set_int_value((int64_t)value); value_proto->set_int_value(SafeAssign(value));
} }
template <> template <>
void SetValue<unsigned long>(drm_metrics::ValueMetric* value_proto, void SetValue<unsigned long>(drm_metrics::ValueMetric* value_proto,
const unsigned long& value) { const unsigned long& value) {
value_proto->set_int_value((int64_t)value); value_proto->set_int_value(SafeAssign(value));
} }
template <> template <>
void SetValue<unsigned long long>(drm_metrics::ValueMetric* value_proto, void SetValue<unsigned long long>(drm_metrics::ValueMetric* value_proto,
const unsigned long long& value) { const unsigned long long& value) {
value_proto->set_int_value((int64_t)value); value_proto->set_int_value(SafeAssign(value));
} }
template <> template <>
void SetValue<bool>(drm_metrics::ValueMetric* value_proto, const bool& value) { void SetValue<bool>(drm_metrics::ValueMetric* value_proto, const bool& value) {
value_proto->set_int_value(value); value_proto->set_int_value(value ? 1 : 0);
} }
template <> template <>
void SetValue<OEMCrypto_HDCP_Capability>( void SetValue<OEMCrypto_HDCP_Capability>(
drm_metrics::ValueMetric* value_proto, drm_metrics::ValueMetric* value_proto,
const OEMCrypto_HDCP_Capability& value) { const OEMCrypto_HDCP_Capability& value) {
value_proto->set_int_value(value); value_proto->set_int_value(static_cast<int64_t>(value));
} }
template <> template <>
void SetValue<OEMCrypto_ProvisioningMethod>( void SetValue<OEMCrypto_ProvisioningMethod>(
drm_metrics::ValueMetric* value_proto, drm_metrics::ValueMetric* value_proto,
const OEMCrypto_ProvisioningMethod& value) { const OEMCrypto_ProvisioningMethod& value) {
value_proto->set_int_value(value); value_proto->set_int_value(static_cast<int64_t>(value));
} }
template <> template <>
void SetValue<OEMCryptoInitializationMode>( void SetValue<OEMCryptoInitializationMode>(
drm_metrics::ValueMetric* value_proto, drm_metrics::ValueMetric* value_proto,
const OEMCryptoInitializationMode& value) { const OEMCryptoInitializationMode& value) {
value_proto->set_int_value(value); value_proto->set_int_value(static_cast<int64_t>(value));
} }
template <> template <>
void SetValue<CdmSecurityLevel>(drm_metrics::ValueMetric* value_proto, void SetValue<CdmSecurityLevel>(drm_metrics::ValueMetric* value_proto,
const CdmSecurityLevel& value) { const CdmSecurityLevel& value) {
value_proto->set_int_value(value); value_proto->set_int_value(static_cast<int64_t>(value));
} }
template <> template <>
void SetValue<CdmUsageSupportType>(drm_metrics::ValueMetric* value_proto, void SetValue<CdmUsageSupportType>(drm_metrics::ValueMetric* value_proto,
const CdmUsageSupportType& value) { const CdmUsageSupportType& value) {
value_proto->set_int_value(value); value_proto->set_int_value(static_cast<int64_t>(value));
} }
template <> template <>
@@ -102,6 +124,6 @@ void SetValue<std::string>(drm_metrics::ValueMetric* value_proto,
const std::string& value) { const std::string& value) {
value_proto->set_string_value(value); value_proto->set_string_value(value);
} }
} // namespace impl } // namespace internal
} // namespace metrics } // namespace metrics
} // namespace wvcdm } // namespace wvcdm

View File

@@ -57,5 +57,32 @@ TEST(ValueMetricTest, SetError) {
ASSERT_EQ(7, metric_proto->error_code()); ASSERT_EQ(7, metric_proto->error_code());
ASSERT_FALSE(metric_proto->has_int_value()); ASSERT_FALSE(metric_proto->has_int_value());
} }
TEST(ValueMetricTest, ValueOverflow) {
constexpr int64_t kMaxValue = std::numeric_limits<int64_t>::max();
constexpr uint64_t kMaxU64 = std::numeric_limits<uint64_t>::max();
constexpr size_t kMaxSizeT = std::numeric_limits<size_t>::max();
ValueMetric<uint64_t> metric_u64;
metric_u64.Record(kMaxU64);
// Runtime value should not be capped.
ASSERT_EQ(kMaxU64, metric_u64.GetValue());
std::unique_ptr<drm_metrics::ValueMetric> metric_proto(metric_u64.ToProto());
// Proto value should be capped.
ASSERT_EQ(kMaxValue, metric_proto->int_value());
ASSERT_FALSE(metric_proto->has_error_code());
ValueMetric<uint64_t> metric_z;
metric_z.Record(kMaxSizeT);
ASSERT_EQ(kMaxSizeT, metric_z.GetValue());
metric_proto.reset(metric_z.ToProto());
if (sizeof(int64_t) <= sizeof(size_t) &&
static_cast<size_t>(kMaxValue) <= kMaxSizeT) {
ASSERT_EQ(kMaxValue, metric_proto->int_value());
} else {
ASSERT_GT(kMaxValue, metric_proto->int_value());
}
}
} // namespace metrics } // namespace metrics
} // namespace wvcdm } // namespace wvcdm