summaryrefslogtreecommitdiff
path: root/cpputil
diff options
context:
space:
mode:
authorTim Taubert <ttaubert@mozilla.com>2017-03-24 11:18:52 +0100
committerTim Taubert <ttaubert@mozilla.com>2017-03-24 11:18:52 +0100
commit14dce5c17c2b2993ffd4fa456524b781f46a2dc6 (patch)
tree31724255437e0c38c5aba50459f21ea0d797129f /cpputil
parent81311a26f78c579d457a1d0e04f7e7e382e93235 (diff)
downloadnss-hg-14dce5c17c2b2993ffd4fa456524b781f46a2dc6.tar.gz
Bug 1348775 - Add custom TLS mutators for libFuzzer fuzzing targets r=franziskus
Differential Revision: https://nss-review.dev.mozaws.net/D261
Diffstat (limited to 'cpputil')
-rw-r--r--cpputil/cpputil.gyp1
-rw-r--r--cpputil/databuffer.h191
-rw-r--r--cpputil/tls_parser.cc73
-rw-r--r--cpputil/tls_parser.h138
4 files changed, 403 insertions, 0 deletions
diff --git a/cpputil/cpputil.gyp b/cpputil/cpputil.gyp
index cf666fd3c..82183f241 100644
--- a/cpputil/cpputil.gyp
+++ b/cpputil/cpputil.gyp
@@ -12,6 +12,7 @@
'sources': [
'dummy_io.cc',
'dummy_io_fwd.cc',
+ 'tls_parser.cc',
],
'dependencies': [
'<(DEPTH)/exports.gyp:nss_exports',
diff --git a/cpputil/databuffer.h b/cpputil/databuffer.h
new file mode 100644
index 000000000..e7236d4e9
--- /dev/null
+++ b/cpputil/databuffer.h
@@ -0,0 +1,191 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef databuffer_h__
+#define databuffer_h__
+
+#include <algorithm>
+#include <cassert>
+#include <cstring>
+#include <iomanip>
+#include <iostream>
+#if defined(WIN32) || defined(WIN64)
+#include <winsock2.h>
+#else
+#include <arpa/inet.h>
+#endif
+
+extern bool g_ssl_gtest_verbose;
+
+namespace nss_test {
+
+class DataBuffer {
+ public:
+ DataBuffer() : data_(nullptr), len_(0) {}
+ DataBuffer(const uint8_t* data, size_t len) : data_(nullptr), len_(0) {
+ Assign(data, len);
+ }
+ DataBuffer(const DataBuffer& other) : data_(nullptr), len_(0) {
+ Assign(other);
+ }
+ ~DataBuffer() { delete[] data_; }
+
+ DataBuffer& operator=(const DataBuffer& other) {
+ if (&other != this) {
+ Assign(other);
+ }
+ return *this;
+ }
+
+ void Allocate(size_t len) {
+ delete[] data_;
+ data_ = new uint8_t[len ? len : 1]; // Don't depend on new [0].
+ len_ = len;
+ }
+
+ void Truncate(size_t len) { len_ = std::min(len_, len); }
+
+ void Assign(const DataBuffer& other) { Assign(other.data(), other.len()); }
+
+ void Assign(const uint8_t* data, size_t len) {
+ if (data) {
+ Allocate(len);
+ memcpy(static_cast<void*>(data_), static_cast<const void*>(data), len);
+ } else {
+ assert(len == 0);
+ data_ = nullptr;
+ len_ = 0;
+ }
+ }
+
+ // Write will do a new allocation and expand the size of the buffer if needed.
+ // Returns the offset of the end of the write.
+ size_t Write(size_t index, const uint8_t* val, size_t count) {
+ assert(val);
+ if (index + count > len_) {
+ size_t newlen = index + count;
+ uint8_t* tmp = new uint8_t[newlen]; // Always > 0.
+ if (data_) {
+ memcpy(static_cast<void*>(tmp), static_cast<const void*>(data_), len_);
+ }
+ if (index > len_) {
+ memset(static_cast<void*>(tmp + len_), 0, index - len_);
+ }
+ delete[] data_;
+ data_ = tmp;
+ len_ = newlen;
+ }
+ if (data_) {
+ memcpy(static_cast<void*>(data_ + index), static_cast<const void*>(val),
+ count);
+ }
+ return index + count;
+ }
+
+ size_t Write(size_t index, const DataBuffer& buf) {
+ return Write(index, buf.data(), buf.len());
+ }
+
+ // Write an integer, also performing host-to-network order conversion.
+ // Returns the offset of the end of the write.
+ size_t Write(size_t index, uint32_t val, size_t count) {
+ assert(count <= sizeof(uint32_t));
+ uint32_t nvalue = htonl(val);
+ auto* addr = reinterpret_cast<const uint8_t*>(&nvalue);
+ return Write(index, addr + sizeof(uint32_t) - count, count);
+ }
+
+ // This can't use the same trick as Write(), since we might be reading from a
+ // smaller data source.
+ bool Read(size_t index, size_t count, uint32_t* val) const {
+ assert(count < sizeof(uint32_t));
+ assert(val);
+ if ((index > len()) || (count > (len() - index))) {
+ return false;
+ }
+ *val = 0;
+ for (size_t i = 0; i < count; ++i) {
+ *val = (*val << 8) | data()[index + i];
+ }
+ return true;
+ }
+
+ // Starting at |index|, remove |remove| bytes and replace them with the
+ // contents of |buf|.
+ void Splice(const DataBuffer& buf, size_t index, size_t remove = 0) {
+ Splice(buf.data(), buf.len(), index, remove);
+ }
+
+ void Splice(const uint8_t* ins, size_t ins_len, size_t index,
+ size_t remove = 0) {
+ assert(ins);
+ uint8_t* old_value = data_;
+ size_t old_len = len_;
+
+ // The amount of stuff remaining from the tail of the old.
+ size_t tail_len = old_len - std::min(old_len, index + remove);
+ // The new length: the head of the old, the new, and the tail of the old.
+ len_ = index + ins_len + tail_len;
+ data_ = new uint8_t[len_ ? len_ : 1];
+
+ // The head of the old.
+ if (old_value) {
+ Write(0, old_value, std::min(old_len, index));
+ }
+ // Maybe a gap.
+ if (old_value && index > old_len) {
+ memset(old_value + index, 0, index - old_len);
+ }
+ // The new.
+ Write(index, ins, ins_len);
+ // The tail of the old.
+ if (tail_len > 0) {
+ Write(index + ins_len, old_value + index + remove, tail_len);
+ }
+
+ delete[] old_value;
+ }
+
+ void Append(const DataBuffer& buf) { Splice(buf, len_); }
+
+ const uint8_t* data() const { return data_; }
+ uint8_t* data() { return data_; }
+ size_t len() const { return len_; }
+ bool empty() const { return len_ == 0; }
+
+ private:
+ uint8_t* data_;
+ size_t len_;
+};
+
+static const size_t kMaxBufferPrint = 32;
+
+inline std::ostream& operator<<(std::ostream& stream, const DataBuffer& buf) {
+ stream << "[" << buf.len() << "] ";
+ for (size_t i = 0; i < buf.len(); ++i) {
+ if (!g_ssl_gtest_verbose && i >= kMaxBufferPrint) {
+ stream << "...";
+ break;
+ }
+ stream << std::hex << std::setfill('0') << std::setw(2)
+ << static_cast<unsigned>(buf.data()[i]);
+ }
+ stream << std::dec;
+ return stream;
+}
+
+inline bool operator==(const DataBuffer& a, const DataBuffer& b) {
+ return (a.empty() && b.empty()) ||
+ (a.len() == b.len() && 0 == memcmp(a.data(), b.data(), a.len()));
+}
+
+inline bool operator!=(const DataBuffer& a, const DataBuffer& b) {
+ return !(a == b);
+}
+
+} // namespace nss_test
+
+#endif
diff --git a/cpputil/tls_parser.cc b/cpputil/tls_parser.cc
new file mode 100644
index 000000000..e4c06aa91
--- /dev/null
+++ b/cpputil/tls_parser.cc
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "tls_parser.h"
+
+namespace nss_test {
+
+bool TlsParser::Read(uint8_t* val) {
+ if (remaining() < 1) {
+ return false;
+ }
+ *val = *ptr();
+ consume(1);
+ return true;
+}
+
+bool TlsParser::Read(uint32_t* val, size_t size) {
+ if (size > sizeof(uint32_t)) {
+ return false;
+ }
+
+ uint32_t v = 0;
+ for (size_t i = 0; i < size; ++i) {
+ uint8_t tmp;
+ if (!Read(&tmp)) {
+ return false;
+ }
+
+ v = (v << 8) | tmp;
+ }
+
+ *val = v;
+ return true;
+}
+
+bool TlsParser::Read(DataBuffer* val, size_t len) {
+ if (remaining() < len) {
+ return false;
+ }
+
+ val->Assign(ptr(), len);
+ consume(len);
+ return true;
+}
+
+bool TlsParser::ReadVariable(DataBuffer* val, size_t len_size) {
+ uint32_t len;
+ if (!Read(&len, len_size)) {
+ return false;
+ }
+ return Read(val, len);
+}
+
+bool TlsParser::Skip(size_t len) {
+ if (len > remaining()) {
+ return false;
+ }
+ consume(len);
+ return true;
+}
+
+bool TlsParser::SkipVariable(size_t len_size) {
+ uint32_t len;
+ if (!Read(&len, len_size)) {
+ return false;
+ }
+ return Skip(len);
+}
+
+} // namespace nss_test
diff --git a/cpputil/tls_parser.h b/cpputil/tls_parser.h
new file mode 100644
index 000000000..c9495b583
--- /dev/null
+++ b/cpputil/tls_parser.h
@@ -0,0 +1,138 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef tls_parser_h_
+#define tls_parser_h_
+
+#include <cstdint>
+#include <cstring>
+#include <memory>
+#if defined(WIN32) || defined(WIN64)
+#include <winsock2.h>
+#else
+#include <arpa/inet.h>
+#endif
+#include "databuffer.h"
+
+namespace nss_test {
+
+const uint8_t kTlsChangeCipherSpecType = 20;
+const uint8_t kTlsAlertType = 21;
+const uint8_t kTlsHandshakeType = 22;
+const uint8_t kTlsApplicationDataType = 23;
+
+const uint8_t kTlsHandshakeClientHello = 1;
+const uint8_t kTlsHandshakeServerHello = 2;
+const uint8_t kTlsHandshakeNewSessionTicket = 4;
+const uint8_t kTlsHandshakeHelloRetryRequest = 6;
+const uint8_t kTlsHandshakeEncryptedExtensions = 8;
+const uint8_t kTlsHandshakeCertificate = 11;
+const uint8_t kTlsHandshakeServerKeyExchange = 12;
+const uint8_t kTlsHandshakeCertificateRequest = 13;
+const uint8_t kTlsHandshakeCertificateVerify = 15;
+const uint8_t kTlsHandshakeClientKeyExchange = 16;
+const uint8_t kTlsHandshakeFinished = 20;
+
+const uint8_t kTlsAlertWarning = 1;
+const uint8_t kTlsAlertFatal = 2;
+
+const uint8_t kTlsAlertCloseNotify = 0;
+const uint8_t kTlsAlertEndOfEarlyData = 1;
+const uint8_t kTlsAlertUnexpectedMessage = 10;
+const uint8_t kTlsAlertBadRecordMac = 20;
+const uint8_t kTlsAlertRecordOverflow = 22;
+const uint8_t kTlsAlertHandshakeFailure = 40;
+const uint8_t kTlsAlertIllegalParameter = 47;
+const uint8_t kTlsAlertDecodeError = 50;
+const uint8_t kTlsAlertDecryptError = 51;
+const uint8_t kTlsAlertProtocolVersion = 70;
+const uint8_t kTlsAlertInappropriateFallback = 86;
+const uint8_t kTlsAlertMissingExtension = 109;
+const uint8_t kTlsAlertUnsupportedExtension = 110;
+const uint8_t kTlsAlertUnrecognizedName = 112;
+const uint8_t kTlsAlertNoApplicationProtocol = 120;
+
+const uint8_t kTlsFakeChangeCipherSpec[] = {
+ kTlsChangeCipherSpecType, // Type
+ 0xfe,
+ 0xff, // Version
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x10, // Fictitious sequence #
+ 0x00,
+ 0x01, // Length
+ 0x01 // Value
+};
+
+static const uint8_t kTls13PskKe = 0;
+static const uint8_t kTls13PskDhKe = 1;
+static const uint8_t kTls13PskAuth = 0;
+static const uint8_t kTls13PskSignAuth = 1;
+
+inline bool IsDtls(uint16_t version) { return (version & 0x8000) == 0x8000; }
+
+inline uint16_t NormalizeTlsVersion(uint16_t version) {
+ if (version == 0xfeff) {
+ return 0x0302; // special: DTLS 1.0 == TLS 1.1
+ }
+ if (IsDtls(version)) {
+ return (version ^ 0xffff) + 0x0201;
+ }
+ return version;
+}
+
+inline uint16_t TlsVersionToDtlsVersion(uint16_t version) {
+ if (version == 0x0302) {
+ return 0xfeff;
+ }
+ if (version == 0x0304) {
+ return version;
+ }
+ return 0xffff - version + 0x0201;
+}
+
+inline size_t WriteVariable(DataBuffer* target, size_t index,
+ const DataBuffer& buf, size_t len_size) {
+ index = target->Write(index, static_cast<uint32_t>(buf.len()), len_size);
+ return target->Write(index, buf.data(), buf.len());
+}
+
+class TlsParser {
+ public:
+ TlsParser(const uint8_t* data, size_t len) : buffer_(data, len), offset_(0) {}
+ explicit TlsParser(const DataBuffer& buf) : buffer_(buf), offset_(0) {}
+
+ bool Read(uint8_t* val);
+ // Read an integral type of specified width.
+ bool Read(uint32_t* val, size_t size);
+ // Reads len bytes into dest buffer, overwriting it.
+ bool Read(DataBuffer* dest, size_t len);
+ // Reads bytes into dest buffer, overwriting it. The number of bytes is
+ // determined by reading from len_size bytes from the stream first.
+ bool ReadVariable(DataBuffer* dest, size_t len_size);
+
+ bool Skip(size_t len);
+ bool SkipVariable(size_t len_size);
+
+ size_t consumed() const { return offset_; }
+ size_t remaining() const { return buffer_.len() - offset_; }
+
+ private:
+ void consume(size_t len) { offset_ += len; }
+ const uint8_t* ptr() const { return buffer_.data() + offset_; }
+
+ DataBuffer buffer_;
+ size_t offset_;
+};
+
+} // namespace nss_test
+
+#endif