Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/security/nss/fuzz/targets/lib/asn1/   (Firefox Browser Version 153.0.1©)  Datei vom 27.6.2026 mit Größe 16 kB image not shown  

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 <cassert>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <random>
#include <utility>
#include <vector>

const uint8_t kTerminator[2] = {0x00, 0x00};

struct Item {
  enum Form { Short, Indef, Long };

  uint8_t* base;
  uint8_t* parent;

  size_t contentSize;
  size_t remainingSize;

  uint8_t tag() const { return base[0]; }
  bool constructed() const { return base[0] & 0x20; }

  Form form() const {
    if (base[1] < 0x80) return Short;
    if (base[1] == 0x80) return Indef;
    return Long;
  }

  uint8_t* lengthData() const { return base + 1; }
  uint8_t* contentData() const { return base + 1 + lengthSize(); }

  size_t lengthSize() const {
    if (base[1] > 0x80) return 1 + (base[1] & 0x7f);
    return 1;
  }
  size_t size() const { return 1 + lengthSize() + contentSize; }
};

static Item ParseItem(uint8_t* data, size_t maxSize, const Item* parent) {
  Item item = {
      .base = data,
      .parent = parent ? parent->base : nullptr,
  };

  if (maxSize < 2) {
    item.base = nullptr;
    item.parent = nullptr;
    return item;
  }

  // Short form.
  if (data[1] < 0x80) {
    item.contentSize = std::min(static_cast<size_t>(data[1]), maxSize - 2);
    item.remainingSize =
        (maxSize - item.size()) + (parent ? parent->remainingSize : 0);
    return item;
  }

  // Indefinite length.
  if (data[1] == 0x80) {
    uint8_t* term = static_cast<uint8_t*>(
        memmem(data + 2, maxSize - 2, kTerminator, sizeof(kTerminator)));

    item.contentSize = term ? static_cast<size_t>(term - data) : maxSize - 2;
    item.remainingSize =
        (maxSize - item.size()) + (parent ? parent->remainingSize : 0);
    return item;
  }

  // Long form.
  size_t octets = data[1] & 0x7f;

  if (octets > maxSize - 2) {
    item.base = nullptr;
    item.parent = nullptr;
    return item;
  }

  // Parse the octets.
  size_t contentSize = 0;
  for (size_t i = 0; i < octets; ++i) {
    contentSize = (contentSize << 8) | data[2 + i];
  }

  item.contentSize = std::min(contentSize, maxSize - (2 + octets));
  item.remainingSize =
      (maxSize - item.size()) + (parent ? parent->remainingSize : 0);
  return item;
}

static std::vector<Item> ParseItems(uint8_t* data, size_t size) {
  std::vector<Item> items;

  // Parse top-level concatenated items.
  size_t offset = 0;
  while (offset + 2 <= size) {
    Item item = ParseItem(data + offset, size - offset, nullptr);
    if (!item.base) {
      break;
    }

    items.push_back(item);
    offset += item.size();
  }

  // Descend into constructed items.
  for (size_t i = 0; i < items.size(); ++i) {
    Item parent = items[i];
    if (!parent.constructed()) {
      continue;
    }

    offset = 0;
    while (offset + 2 <= parent.contentSize) {
      Item child = ParseItem(parent.contentData() + offset,
                             parent.contentSize - offset, &parent);
      if (!child.base) {
        break;
      }

      items.push_back(child);
      offset += child.size();
    }
  }

  return items;
}
static size_t DropItem(uint8_t* data, size_t size, size_t maxSize,
                       std::mt19937& rng) {
  std::vector<Item> items = ParseItems(data, size);
  if (items.empty()) {
    return 0;
  }

  std::uniform_int_distribution<size_t> dist(0, items.size() - 1);
  Item& item = items.at(dist(rng));

  // Store the size since the memmove below overwrites `item`'s header,
  // changing what size() would return afterwards.
  size_t itemSize = item.size();

  memmove(item.base, item.base + itemSize, item.remainingSize);
  return size - itemSize;
}

static size_t SwapItems(uint8_t* data, size_t size, size_t maxSize,
                        std::mt19937& rng) {
  std::vector<Item> items = ParseItems(data, size);
  if (items.size() < 2) {
    return 0;
  }

  std::uniform_int_distribution<size_t> dist(0, items.size() - 1);
  Item item = items.at(dist(rng));

  // Filter for sibling items.
  auto isInvalidItem = [&item](const Item& it) {
    return it.base == item.base || it.parent != item.parent;
  };
  items.erase(std::remove_if(items.begin(), items.end(), isInvalidItem),
              items.end());

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

  std::uniform_int_distribution<size_t> dist2(0, items.size() - 1);
  Item other = items.at(dist2(rng));

  if (item.base > other.base) {
    std::swap(item, other);
  }

  // Store sizes since memmove may overwrite them.
  size_t itemSize = item.size();
  size_t otherSize = other.size();

  std::vector<uint8_t> scratch(itemSize + otherSize);
  memcpy(scratch.data(), item.base, itemSize);
  memcpy(scratch.data() + itemSize, other.base, otherSize);

  // Move the middle part first.
  size_t middleSize = other.base - (item.base + itemSize);
  memmove(item.base + otherSize, item.base + itemSize, middleSize);

  memcpy(item.base, scratch.data() + itemSize, otherSize);
  memcpy(item.base + otherSize + middleSize, scratch.data(), itemSize);

  return size;
}

static size_t DuplicateItem(uint8_t* data, size_t size, size_t maxSize,
                            std::mt19937& rng) {
  std::vector<Item> items = ParseItems(data, size);
  if (items.empty()) {
    return 0;
  }

  std::uniform_int_distribution<size_t> dist(0, items.size() - 1);
  Item& item = items.at(dist(rng));
  Item& other = items.at(dist(rng));

  // Store sizes since the memmove below may overwrite the tag/length octets
  // of `item` or `other`, changing what size() would return afterwards.
  size_t itemSize = item.size();
  size_t otherSize = other.size();

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

  // Make place to put a copy of `other` before `item`.
  memmove(item.base + otherSize, item.base, itemSize + item.remainingSize);

  // In case `other` was in the memmove range.
  uint8_t* src = (other.base > item.base) ? other.base + otherSize : other.base;
  memmove(item.base, src, otherSize);

  return size + otherSize;
}

static size_t TruncateItem(uint8_t* data, size_t size, size_t maxSize,
                           std::mt19937& rng) {
  std::vector<Item> items = ParseItems(data, size);
  if (items.empty()) {
    return 0;
  }

  std::uniform_int_distribution<size_t> dist(0, items.size() - 1);
  Item& item = items.at(dist(rng));

  if (item.contentSize == 0) {
    return 0;
  }

  std::uniform_int_distribution<size_t> dist2(0, item.contentSize - 1);
  size_t contentSize = dist2(rng);
  size_t removedSize = item.contentSize - contentSize;

  // For Item::Indef, the memmove is sufficient as the EOC is moved
  // to the left.
  memmove(item.contentData() + contentSize,
          item.contentData() + item.contentSize, item.remainingSize);

  if (item.form() == Item::Short) {
    item.lengthData()[0] = static_cast<uint8_t>(contentSize);
  }

  if (item.form() == Item::Long) {
    size_t lengthSize = item.lengthSize();
    for (size_t i = 1; i < lengthSize; ++i) {
      item.lengthData()[lengthSize - i] =
          static_cast<uint8_t>(contentSize & 0xff);
      contentSize >>= 8;
    }
  }

  return size - removedSize;
}

static size_t CorruptLength(uint8_t* data, size_t size, size_t maxSize,
                            std::mt19937& rng) {
  std::vector<Item> items = ParseItems(data, size);
  if (items.empty()) {
    return 0;
  }

  std::uniform_int_distribution<size_t> dist(0, items.size() - 1);
  Item& item = items.at(dist(rng));

  // For Item::Indef, there is nothing we can corrupt.
  if (item.form() == Item::Indef) {
    return 0;
  }

  if (item.form() == Item::Short) {
    std::uniform_int_distribution<uint8_t> dist2(00x7f);
    item.lengthData()[0] = dist2(rng);

    return size;
  }

  size_t lengthSize = item.lengthSize();
  size_t numOctets = lengthSize - 1;

  size_t formCap =
      (size_t{1} << std::min<size_t>(8 * numOctets, 8 * sizeof(size_t) - 1)) -
      1;

  std::uniform_int_distribution<size_t> dist2(0, formCap);
  size_t contentSize = dist2(rng);

  for (size_t i = 1; i < lengthSize; ++i) {
    item.lengthData()[lengthSize - i] =
        static_cast<uint8_t>(contentSize & 0xff);
    contentSize >>= 8;
  }

  return size;
}

static size_t ChangeForm(uint8_t* data, size_t size, size_t maxSize,
                         std::mt19937& rng) {
  std::vector<Item> items = ParseItems(data, size);
  if (items.empty()) {
    return 0;
  }

  std::uniform_int_distribution<size_t> dist(0, items.size() - 1);
  Item& item = items.at(dist(rng));

  // Recover the item's actual content, independent of its current form.
  // For Item::Indef, `contentSize` includes the 2-byte EOC terminator
  // (see ParseItem), so strip it to get the real content length.
  uint8_t* content = item.contentData();
  size_t contentLen = item.contentSize;
  if (item.form() == Item::Indef) {
    if (contentLen < sizeof(kTerminator)) {
      return 0;
    }
    contentLen -= sizeof(kTerminator);
  }

  // Collect the forms we can switch to, excluding the current one.
  // Short form can only hold lengths up to 0x7f; indefinite length is
  // only valid for constructed items.
  std::vector<Item::Form> candidates;
  if (item.form() != Item::Short && contentLen <= 0x7f) {
    candidates.push_back(Item::Short);
  }
  if (item.form() != Item::Long) {
    candidates.push_back(Item::Long);
  }
  if (item.form() != Item::Indef && item.constructed()) {
    candidates.push_back(Item::Indef);
  }
  if (candidates.empty()) {
    return 0;
  }

  std::uniform_int_distribution<size_t> formDist(0, candidates.size() - 1);
  Item::Form target = candidates.at(formDist(rng));

  // Re-encode the item with the new length form. Build it standalone first so
  // `content` (which still points into `data`) is copied before we shift the
  // buffer below.
  std::vector<uint8_t> encoded;
  encoded.push_back(item.tag());

  if (target == Item::Short) {
    encoded.push_back(static_cast<uint8_t>(contentLen));
  } else if (target == Item::Long) {
    // Minimal big-endian octets for `contentLen` (at least one).
    uint8_t octets[sizeof(size_t)];
    size_t numOctets = 0;
    size_t len = contentLen;
    do {
      octets[numOctets++] = static_cast<uint8_t>(len & 0xff);
      len >>= 8;
    } while (len);

    encoded.push_back(static_cast<uint8_t>(0x80 | numOctets));
    for (size_t i = 0; i < numOctets; ++i) {
      encoded.push_back(octets[numOctets - 1 - i]);
    }
  } else {  // Item::Indef
    encoded.push_back(0x80);
  }

  encoded.insert(encoded.end(), content, content + contentLen);

  if (target == Item::Indef) {
    encoded.insert(encoded.end(), kTerminator,
                   kTerminator + sizeof(kTerminator));
  }

  size_t oldSize = item.size();
  size_t remainingSize = item.remainingSize;

  if (encoded.size() > oldSize && size + (encoded.size() - oldSize) > maxSize) {
    return 0;
  }

  memmove(item.base + encoded.size(), item.base + oldSize, remainingSize);
  memcpy(item.base, encoded.data(), encoded.size());

  return size - oldSize + encoded.size();
}

// Split a primitive OCTET STRING or BIT STRING into a constructed
// indefinite-length wrapper with multiple primitive fragments.
// Original:    04 05 [A B C D E]
// Fragmented:  24 80  04 02 [A B]  04 02 [C D]  04 01 [E]  00 00
static size_t FragmentString(uint8_t* data, size_t size, size_t maxSize,
                             std::mt19937& rng) {
  auto isInvalidItem = [](const Item& it) {
    if (it.tag() == 0x04) return it.contentSize < 2;
    if (it.tag() == 0x03) return it.contentSize < 3;
    return true;
  };

  std::vector<Item> items = ParseItems(data, size);
  items.erase(std::remove_if(items.begin(), items.end(), isInvalidItem),
              items.end());
  if (items.empty()) {
    return 0;
  }

  std::uniform_int_distribution<size_t> dist(0, items.size() - 1);
  Item& item = items.at(dist(rng));

  std::vector<uint8_t> fragmentedItem;
  fragmentedItem.push_back(item.tag() | 0x20);
  fragmentedItem.push_back(0x80);

  // BIT STRING: Skip the unusued byte for `remainingSize`.
  bool isBitString = item.tag() == 0x03;
  size_t remainingSize = item.contentSize - (isBitString ? 1 : 0);

  do {
    // BIT STRING: We need to make extra room for the unused byte.
    std::uniform_int_distribution<size_t> dist2(
        1, std::min<size_t>(remainingSize, 0x7f - (isBitString ? 1 : 0)));
    size_t fragmentLen = dist2(rng);

    fragmentedItem.push_back(item.tag());
    fragmentedItem.push_back(
        static_cast<uint8_t>((isBitString ? 1 : 0) + fragmentLen));

    // BIT STRING: The actual unused byte is prepended only to the last
    // fragment data, otherwise zero.
    if (isBitString) {
      uint8_t unused =
          (remainingSize - fragmentLen) == 0 ? item.contentData()[0] : 0;
      fragmentedItem.push_back(unused);
    }

    uint8_t* src = item.contentData() + (item.contentSize - remainingSize);
    fragmentedItem.insert(fragmentedItem.end(), src, src + fragmentLen);

    remainingSize -= fragmentLen;
  } while (remainingSize);

  fragmentedItem.push_back(0x00);
  fragmentedItem.push_back(0x00);

  size_t growth = fragmentedItem.size() - item.size();

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

  memmove(item.base + fragmentedItem.size(), item.base + item.size(),
          item.remainingSize);
  memcpy(item.base, fragmentedItem.data(), fragmentedItem.size());

  return size + growth;
}

// Re-encode a low-tag-number tag (0..30) in high-tag-number form
// (0x1f + base-128). Non-canonical per DER; X.690 §8.1.2.4.
static size_t RecodeTagLong(uint8_t* data, size_t size, size_t maxSize,
                            std::mt19937& rng) {
  std::vector<Item> items = ParseItems(data, size);
  if (items.empty()) {
    return 0;
  }

  std::uniform_int_distribution<size_t> dist(0, items.size() - 1);
  Item& item = items.at(dist(rng));

  // Already in high-tag-number form, or no room to insert one byte.
  if ((item.tag() & 0x1f) == 0x1f || size + 1 > maxSize) {
    return 0;
  }

  memmove(item.base + 1, item.base, item.size() + item.remainingSize);
  item.base[0] |= 0x1f;
  item.base[1] &= 0x1f;

  return size + 1;
}

static size_t WrapInConstructed(uint8_t* data, size_t size, size_t maxSize,
                                std::mt19937& rng) {
  std::vector<Item> items = ParseItems(data, size);
  if (items.empty()) {
    return 0;
  }

  std::uniform_int_distribution<size_t> dist(0, items.size() - 1);
  Item& item = items.at(dist(rng));

  if (item.size() > 127 || size + 2 > maxSize) {
    return 0;
  }

  // 0x30/0x31 are SEQUENCE/SET; 0xa0..0xa3 are context [0]..[3] constructed,
  // the EXPLICIT-tag shape used for OPTIONAL fields in X.509/CMS/OCSP.
  const uint8_t kWrappers[] = {0x30, 0x31, 0xa0, 0xa1, 0xa2, 0xa3};
  std::uniform_int_distribution<size_t> dist2(
      0sizeof(kWrappers) / sizeof(kWrappers[0]) - 1);

  memmove(item.base + 2, item.base, item.size() + item.remainingSize);
  item.base[0] = kWrappers[dist2(rng)];
  item.base[1] = static_cast<uint8_t>(item.size());

  return size + 2;
}

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

constexpr Mutator kMutators[] = {
    DropItem,   SwapItems,      DuplicateItem, TruncateItem,      CorruptLength,
    ChangeForm, FragmentString, RecodeTagLong, WrapInConstructed,
};

namespace ASN1Mutators {

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

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);
}

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::mt19937 rng(seed);

  std::vector<Item> items1 = ParseItems(const_cast<uint8_t*>(data1), size1);
  std::vector<Item> items2 = ParseItems(const_cast<uint8_t*>(data2), size2);

  auto isChild = [](const Item& it) { return it.parent != nullptr; };
  items1.erase(std::remove_if(items1.begin(), items1.end(), isChild),
               items1.end());
  items2.erase(std::remove_if(items2.begin(), items2.end(), isChild),
               items2.end());

  std::vector<Item> pool;
  pool.insert(pool.end(), items1.begin(), items1.end());
  pool.insert(pool.end(), items2.begin(), items2.end());

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

  std::shuffle(pool.begin(), pool.end(), rng);

  size_t outSize = 0;
  for (const Item& item : pool) {
    if (outSize + item.size() > maxOutSize) {
      continue;
    }

    memcpy(out + outSize, item.base, item.size());
    outSize += item.size();
  }

  return outSize;
}

}  // namespace ASN1Mutators

Messung V0.5 in Prozent
C=93 H=91 G=91

¤ Dauer der Verarbeitung: 0.14 Sekunden  (vorverarbeitet am  2026-08-26) ¤

*© 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.