Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  mutators.cc

  Sprache: C
 

/* 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 "mutators.h"

#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <random>
#include <vector>

#include "tls_parser.h"

// TLS record header: ContentType(1) + ProtocolVersion(2) + length(2) = 5 bytes.
// DTLS adds epoch(2) + sequence_number(6) before the length field.
// See RFC 8446 Section 5.1 (TLS) and RFC 9147 Section 4 (DTLS).
constexpr size_t kHeaderBytes = 5 + EXTRA_HEADER_BYTES;
constexpr size_t kLengthOffset = 3 + EXTRA_HEADER_BYTES;

// RFC 8446 Section 5.1: change_cipher_spec(20), alert(21),
// handshake(22), application_data(23). Also heartbeat(24, RFC 6520),
// tls12_cid(25, RFC 9146), and ACK(26, RFC 9147 DTLS 1.3).
constexpr uint8_t kContentTypes[] = {20212223242526};

struct Record {
  uint8_t* data;
  size_t size;
  size_t remaining;

  uint8_t contentType() { return data[0]; }
};

static std::vector<Record> ParseRecords(uint8_t* data, size_t size) {
  std::vector<Record> records;
  nss_test::TlsParser parser(data, size);

  while (parser.remaining()) {
    size_t offset = parser.consumed();

    if (!parser.Skip(kLengthOffset)) {
      break;
    }

    // Read the 2-byte length field, then skip that many payload bytes.
    if (!parser.SkipVariable(2)) {
      break;
    }

    records.push_back(
        {data + offset, parser.consumed() - offset, parser.remaining()});
  }

  return records;
}

static size_t DropRecord(uint8_t* data, size_t size, size_t maxSize,
                         std::mt19937& rng) {
  std::vector<Record> records = ParseRecords(data, size);
  if (records.empty()) {
    return 0;
  }

  std::uniform_int_distribution<size_t> dist(0, records.size() - 1);
  Record& record = records.at(dist(rng));

  memmove(record.data, record.data + record.size, record.remaining);
  return size - record.size;
}

static size_t SwapRecords(uint8_t* data, size_t size, size_t maxSize,
                          std::mt19937& rng) {
  std::vector<Record> records = ParseRecords(data, size);
  if (records.size() < 2) {
    return 0;
  }

  std::uniform_int_distribution<size_t> dist(0, records.size() - 1);
  size_t i = dist(rng);
  size_t j;
  do {
    j = dist(rng);
  } while (j == i);

  // Read from buf (snapshot), write to data. Do not remove the copy;
  // without it, in-place reordering of variable-size records aliases.
  std::vector<uint8_t> buf(data, data + size);
  std::swap(records.at(i), records.at(j));

  uint8_t* dest = data;
  for (Record& rec : records) {
    memcpy(dest, buf.data() + (rec.data - data), rec.size);
    dest += rec.size;
  }

  return size;
}

// Copy a random record and insert the duplicate before a random record.
static size_t DuplicateRecord(uint8_t* data, size_t size, size_t maxSize,
                              std::mt19937& rng) {
  std::vector<Record> records = ParseRecords(data, size);
  if (records.empty()) {
    return 0;
  }

  std::uniform_int_distribution<size_t> dist(0, records.size() - 1);
  Record& record = records.at(dist(rng));

  if (size + record.size > maxSize) {
    return 0;
  }

  Record& other = records.at(dist(rng));

  memmove(other.data + record.size, other.data, other.size + other.remaining);

  uint8_t* src =
      (record.data < other.data) ? record.data : record.data + record.size;
  memcpy(other.data, src, record.size);

  return size + record.size;
}

// Shorten a random record's payload to a random length.
static size_t TruncateRecord(uint8_t* data, size_t size, size_t maxSize,
                             std::mt19937& rng) {
  std::vector<Record> records = ParseRecords(data, size);
  if (records.empty()) {
    return 0;
  }

  std::uniform_int_distribution<size_t> dist(0, records.size() - 1);
  Record& record = records.at(dist(rng));

  if (record.size <= kHeaderBytes) {
    return 0;
  }

  std::uniform_int_distribution<size_t> dist2(kHeaderBytes, record.size - 1);
  size_t length = dist2(rng);

  size_t payloadLength = length - kHeaderBytes;
  record.data[kLengthOffset] = (payloadLength >> 8) & 0xff;
  record.data[kLengthOffset + 1] = payloadLength & 0xff;

  memmove(record.data + length, record.data + record.size, record.remaining);

  return size + length - record.size;
}

// Split a random record into two records of the same ContentType.
// RFC 8446 Section 5.1: "Multiple fragments of a Handshake message may be
// coalesced into a single TLSPlaintext record" -- the inverse is also valid.
// For DTLS the second record inherits the first's epoch/seqno; real DTLS
// fragments at the handshake layer, but duplicate seqnos exercise anti-replay.
static size_t FragmentRecord(uint8_t* data, size_t size, size_t maxSize,
                             std::mt19937& rng) {
  if (size + kHeaderBytes > maxSize) {
    return 0;
  }

  std::vector<Record> records = ParseRecords(data, size);
  if (records.empty()) {
    return 0;
  }

  std::uniform_int_distribution<size_t> dist(0, records.size() - 1);
  Record& record = records.at(dist(rng));
  size_t contentLength = record.size - kHeaderBytes;

  if (contentLength < 2) {
    return 0;
  }

  std::uniform_int_distribution<size_t> dist2(1, contentLength - 1);
  size_t firstLength = dist2(rng);
  size_t secondLength = contentLength - firstLength;

  // Update first record's length.
  record.data[kLengthOffset] = (firstLength >> 8) & 0xff;
  record.data[kLengthOffset + 1] = firstLength & 0xff;

  // Make room for the second record's header at the split point.
  uint8_t* second = record.data + kHeaderBytes + firstLength;
  memmove(second + kHeaderBytes, second, record.remaining + secondLength);

  // Write second header: copy type/version from first, set new length.
  memcpy(second, record.data, kLengthOffset);
  second[kLengthOffset] = (secondLength >> 8) & 0xff;
  second[kLengthOffset + 1] = secondLength & 0xff;

  return size + kHeaderBytes;
}

// Insert a zero-length record with a random content type before a random
// record. RFC 8446 Section 5.4: "Application Data records may contain a
// zero-length TLSInnerPlaintext.content".
static size_t InjectEmptyRecord(uint8_t* data, size_t size, size_t maxSize,
                                std::mt19937& rng) {
  if (size + kHeaderBytes > maxSize) {
    return 0;
  }

  std::vector<Record> records = ParseRecords(data, size);
  if (records.empty()) {
    return 0;
  }

  std::uniform_int_distribution<size_t> dist(0, records.size() - 1);
  Record& record = records.at(dist(rng));

  uint8_t* dest = record.data;
  memmove(dest + kHeaderBytes, dest, record.size + record.remaining);

  // Copy ProtocolVersion (+ epoch/seqno for DTLS) from the adjacent
  // record, pick a random ContentType, and set length to zero.
  memcpy(dest + 1, dest + kHeaderBytes + 1, kLengthOffset - 1);

  std::uniform_int_distribution<size_t> dist2(0sizeof(kContentTypes) - 1);
  dest[0] = kContentTypes[dist2(rng)];

  dest[kLengthOffset] = 0;
  dest[kLengthOffset + 1] = 0;

  return size + kHeaderBytes;
}

// Merge two adjacent records that share a ContentType into one.
// Inverse of FragmentRecord. RFC 8446 Section 5.1: "Handshake messages may
// be fragmented over several records".
static size_t MergeRecords(uint8_t* data, size_t size, size_t maxSize,
                           std::mt19937& rng) {
  std::vector<Record> records = ParseRecords(data, size);
  if (records.size() < 2) {
    return 0;
  }

  std::vector<size_t> candidates;
  for (size_t i = 0; i + 1 < records.size(); i++) {
    if (records[i].contentType() == records[i + 1].contentType()) {
      candidates.push_back(i);
    }
  }

  if (candidates.empty()) {
    return 0;
  }

  std::uniform_int_distribution<size_t> dist(0, candidates.size() - 1);
  size_t idx = candidates.at(dist(rng));

  Record& first = records.at(idx);
  Record& second = records.at(idx + 1);

  size_t combinedPayload =
      (first.size - kHeaderBytes) + (second.size - kHeaderBytes);

  // RFC 8446 Section 5.1: TLSPlaintext.length MUST NOT exceed 2^14.
  // We use the uint16 max here to also exercise the overflow path.
  if (combinedPayload > 0xffff) {
    return 0;
  }

  first.data[kLengthOffset] = (combinedPayload >> 8) & 0xff;
  first.data[kLengthOffset + 1] = combinedPayload & 0xff;

  uint8_t* secondHeader = second.data;
  memmove(secondHeader, secondHeader + kHeaderBytes,
          second.size - kHeaderBytes + second.remaining);

  return size - kHeaderBytes;
}

// Extend a random record's payload with random bytes.
// Inverse of TruncateRecord. Exercises overlong-message handling and
// "trailing data after message" code paths.
static size_t ExtendRecord(uint8_t* data, size_t size, size_t maxSize,
                           std::mt19937& rng) {
  std::vector<Record> records = ParseRecords(data, size);
  if (records.empty()) {
    return 0;
  }

  std::uniform_int_distribution<size_t> dist(0, records.size() - 1);
  Record& record = records.at(dist(rng));

  size_t available = maxSize - size;
  if (available == 0) {
    return 0;
  }

  size_t currentPayload = record.size - kHeaderBytes;
  if (currentPayload >= 0xffff) {
    return 0;
  }

  size_t maxExtend = std::min(available, (size_t)0xffff - currentPayload);
  std::uniform_int_distribution<size_t> dist2(1, maxExtend);
  size_t extendBy = dist2(rng);

  // Make room after the record's current payload.
  uint8_t* insertPoint = record.data + record.size;
  memmove(insertPoint + extendBy, insertPoint, record.remaining);

  // Fill extension with random bytes.
  std::uniform_int_distribution<uint8_t> dist3(0255);
  for (size_t i = 0; i < extendBy; ++i) {
    insertPoint[i] = dist3(rng);
  }

  // Update the length field.
  size_t newPayload = currentPayload + extendBy;
  record.data[kLengthOffset] = (newPayload >> 8) & 0xff;
  record.data[kLengthOffset + 1] = newPayload & 0xff;

  return size + extendBy;
}

using Mutator = size_t (*)(uint8_t*, size_t, size_t, std::mt19937&);

constexpr Mutator kMutators[] = {
    DropRecord,     SwapRecords,       DuplicateRecord, TruncateRecord,
    FragmentRecord, InjectEmptyRecord, MergeRecords,    ExtendRecord,
};

namespace TlsMutators {

extern "C" size_t LLVMFuzzerMutate(uint8_t* data, size_t size, size_t maxSize);

// Pick a random TLS-aware mutator or fall back to libFuzzer's default.
size_t CustomMutator(uint8_t* data, size_t size, size_t maxSize,
                     unsigned int seed) {
  std::mt19937 rng(seed);
  std::bernoulli_distribution coin;

  if (coin(rng)) {
    std::uniform_int_distribution<size_t> dist(
        0, (sizeof(kMutators) / sizeof(kMutators[0])) - 1);
    return kMutators[dist(rng)](data, size, maxSize, rng);
  }

  return LLVMFuzzerMutate(data, size, maxSize);
}

// Merge records from two transcripts, shuffle, and write to `out`.
// const_cast is safe: records are only read via memcpy to `out`.
size_t CustomCrossOver(const uint8_t* data1, size_t size1, const uint8_t* data2,
                       size_t size2, uint8_t* out, size_t maxOutSize,
                       unsigned int seed) {
  std::vector<Record> records1 =
      ParseRecords(const_cast<uint8_t*>(data1), size1);
  if (records1.empty()) {
    return 0;
  }

  std::vector<Record> records2 =
      ParseRecords(const_cast<uint8_t*>(data2), size2);
  if (records2.empty()) {
    return 0;
  }

  // Append `records2` to the back of `records1`.
  std::move(records2.begin(), records2.end(), std::back_inserter(records1));

  std::mt19937 rng(seed);
  std::shuffle(records1.begin(), records1.end(), rng);

  size_t total = 0;
  for (Record& record : records1) {
    size_t length = record.size;
    if (total + length > maxOutSize) {
      break;
    }

    memcpy(out + total, record.data, length);
    total += length;
  }

  return total;
}

}  // namespace TlsMutators

Messung V0.5 in Prozent
C=97 H=99 G=97

¤ Dauer der Verarbeitung: 0.6 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Statistik
#Sources=434850
#Domains=655579