Index: net/cert/ct_serialization.cc |
diff --git a/net/cert/ct_serialization.cc b/net/cert/ct_serialization.cc |
index 235d37e65ad1fc8106a05d602c3c9f04b4e61a48..2de83087260f8828a81a44e65f719eeb5bd8ebe9 100644 |
--- a/net/cert/ct_serialization.cc |
+++ b/net/cert/ct_serialization.cc |
@@ -6,9 +6,11 @@ |
#include <stdint.h> |
+#include <algorithm> |
#include <limits> |
#include "base/logging.h" |
+#include "base/numerics/safe_math.h" |
namespace net { |
@@ -54,11 +56,15 @@ template <typename T> |
bool ReadUint(size_t length, base::StringPiece* in, T* out) { |
if (in->size() < length) |
return false; |
+ DCHECK_NE(length, 0u); |
DCHECK_LE(length, sizeof(T)); |
- T result = 0; |
- for (size_t i = 0; i < length; ++i) { |
- result = (result << 8) | static_cast<unsigned char>((*in)[i]); |
+ T result = static_cast<uint8_t>((*in)[0]); |
+ // This loop only executes if sizeof(T) > 1, because the first operation is |
+ // to shift left by 1 byte, which is undefined behaviour if T is a 1 byte |
+ // integer. |
+ for (size_t i = 1; i < length; ++i) { |
+ result = (result << 8) | static_cast<uint8_t>((*in)[i]); |
} |
in->remove_prefix(length); |
*out = result; |
@@ -66,14 +72,18 @@ bool ReadUint(size_t length, base::StringPiece* in, T* out) { |
} |
// Reads a TLS-encoded field length from |in|. |
-// The bytes read from |in| are discarded (i.e. |in|'s prefix removed) |
-// |prefix_length| indicates the bytes needed to represent the length (e.g. 3) |
+// The bytes read from |in| are discarded (i.e. |in|'s prefix removed). |
+// |prefix_length| indicates the bytes needed to represent the length (e.g. 3). |
+// Max |prefix_length| is 8. |
// success, returns true and stores the result in |*out|. |
bool ReadLength(size_t prefix_length, base::StringPiece* in, size_t* out) { |
- size_t length; |
+ uint64_t length = 0; |
if (!ReadUint(prefix_length, in, &length)) |
return false; |
- *out = length; |
+ base::CheckedNumeric<size_t> checked_length = length; |
+ if (!checked_length.IsValid()) |
+ return false; |
+ *out = checked_length.ValueOrDie(); |
return true; |
} |
@@ -96,7 +106,7 @@ bool ReadFixedBytes(size_t length, |
bool ReadVariableBytes(size_t prefix_length, |
base::StringPiece* in, |
base::StringPiece* out) { |
- size_t length; |
+ size_t length = 0; |
if (!ReadLength(prefix_length, in, &length)) |
return false; |
return ReadFixedBytes(length, in, out); |
@@ -176,15 +186,15 @@ bool ConvertSignatureAlgorithm( |
} |
// Writes a TLS-encoded variable length unsigned integer to |output|. |
-// |length| indicates the size (in bytes) of the integer. |
+// |length| indicates the size (in bytes) of the integer. This must be able to |
+// accomodate |value|. |
// |value| the value itself to be written. |
-template <typename T> |
-void WriteUint(size_t length, T value, std::string* output) { |
- DCHECK_LE(length, sizeof(T)); |
- DCHECK(length == sizeof(T) || value >> (length * 8) == 0); |
+void WriteUint(size_t length, uint64_t value, std::string* output) { |
+ // Check that |value| fits into |length| bytes. |
+ DCHECK(length >= sizeof(value) || value >> (length * 8) == 0); |
for (; length > 0; --length) { |
- output->push_back((value >> ((length - 1)* 8)) & 0xFF); |
+ output->push_back((value >> ((length - 1) * 8)) & 0xFF); |
} |
} |
@@ -195,25 +205,33 @@ void WriteUint(size_t length, T value, std::string* output) { |
// length when reading. |
// If the length of |input| is dynamic and data is expected to follow it, |
// WriteVariableBytes must be used. |
-void WriteEncodedBytes(const base::StringPiece& input, std::string* output) { |
+// Returns the number of bytes written (the length of |input|). |
+size_t WriteEncodedBytes(const base::StringPiece& input, std::string* output) { |
input.AppendToString(output); |
+ return input.size(); |
} |
// Writes a variable-length array to |output|. |
-// |prefix_length| indicates the number of bytes needed to represnt the length. |
+// |prefix_length| indicates the number of bytes needed to represent the length. |
// |input| is the array itself. |
-// If the size of |input| is less than 2^|prefix_length| - 1, encode the |
-// length and data and return true. Otherwise, return false. |
+// If 1 <= |prefix_length| <= 8 and the size of |input| is less than |
+// 2^|prefix_length| - 1, encode the length and data and return true. |
+// Otherwise, return false. |
bool WriteVariableBytes(size_t prefix_length, |
const base::StringPiece& input, |
std::string* output) { |
- size_t input_size = input.size(); |
- size_t max_allowed_input_size = |
- static_cast<size_t>(((1 << (prefix_length * 8)) - 1)); |
- if (input_size > max_allowed_input_size) |
+ DCHECK_GE(prefix_length, 1u); |
+ DCHECK_LE(prefix_length, 8u); |
+ |
+ uint64_t input_size = input.size(); |
+ uint64_t max_input_size = (prefix_length == 8) |
+ ? UINT64_MAX |
+ : ((UINT64_C(1) << (prefix_length * 8)) - 1); |
+ |
+ if (input_size > max_input_size) |
return false; |
- WriteUint(prefix_length, input.size(), output); |
+ WriteUint(prefix_length, input_size, output); |
WriteEncodedBytes(input, output); |
return true; |
@@ -291,6 +309,26 @@ bool EncodeLogEntry(const LogEntry& input, std::string* output) { |
return false; |
} |
+static bool ReadTimeSinceEpoch(base::StringPiece* input, base::Time* output) { |
+ uint64_t time_since_epoch = 0; |
+ if (!ReadUint(kTimestampLength, input, &time_since_epoch)) |
+ return false; |
+ |
+ base::CheckedNumeric<int64_t> time_since_epoch_signed = time_since_epoch; |
+ |
+ if (!time_since_epoch_signed.IsValid()) { |
+ DVLOG(1) << "Timestamp value too big to cast to int64_t: " |
+ << time_since_epoch; |
+ return false; |
+ } |
+ |
+ *output = |
+ base::Time::UnixEpoch() + |
+ base::TimeDelta::FromMilliseconds(time_since_epoch_signed.ValueOrDie()); |
+ |
+ return true; |
+} |
+ |
static void WriteTimeSinceEpoch(const base::Time& timestamp, |
std::string* output) { |
base::TimeDelta time_since_epoch = timestamp - base::Time::UnixEpoch(); |
@@ -351,28 +389,17 @@ bool DecodeSignedCertificateTimestamp( |
} |
result->version = SignedCertificateTimestamp::SCT_VERSION_1; |
- uint64_t timestamp; |
base::StringPiece log_id; |
base::StringPiece extensions; |
if (!ReadFixedBytes(kLogIdLength, input, &log_id) || |
- !ReadUint(kTimestampLength, input, ×tamp) || |
- !ReadVariableBytes(kExtensionsLengthBytes, input, |
- &extensions) || |
+ !ReadTimeSinceEpoch(input, &result->timestamp) || |
+ !ReadVariableBytes(kExtensionsLengthBytes, input, &extensions) || |
!DecodeDigitallySigned(input, &result->signature)) { |
return false; |
} |
- if (timestamp > static_cast<uint64_t>(std::numeric_limits<int64_t>::max())) { |
- DVLOG(1) << "Timestamp value too big to cast to int64_t: " << timestamp; |
- return false; |
- } |
- |
log_id.CopyToString(&result->log_id); |
extensions.CopyToString(&result->extensions); |
- result->timestamp = |
- base::Time::UnixEpoch() + |
- base::TimeDelta::FromMilliseconds(static_cast<int64_t>(timestamp)); |
- |
output->swap(result); |
return true; |
} |
@@ -381,7 +408,7 @@ bool EncodeSCTListForTesting(const base::StringPiece& sct, |
std::string* output) { |
std::string encoded_sct; |
return WriteVariableBytes(kSerializedSCTLengthBytes, sct, &encoded_sct) && |
- WriteVariableBytes(kSCTListLengthBytes, encoded_sct, output); |
+ WriteVariableBytes(kSCTListLengthBytes, encoded_sct, output); |
} |
} // namespace ct |