_ZN5Botan17ct_expand_top_bitITkNSt3__117unsigned_integralEmEET_S2_:
   28|     88|BOTAN_FORCE_INLINE constexpr T ct_expand_top_bit(T a) {
   29|     88|   const T top = CT::value_barrier<T>(a >> (sizeof(T) * 8 - 1));
   30|     88|   return static_cast<T>(0) - top;
   31|     88|}
_ZN5Botan10ct_is_zeroITkNSt3__117unsigned_integralEmEET_S2_:
   37|     88|BOTAN_FORCE_INLINE constexpr T ct_is_zero(T x) {
   38|     88|   return ct_expand_top_bit<T>(~x & (x - 1));
   39|     88|}
_ZN5Botan10ct_is_zeroITkNSt3__117unsigned_integralEhEET_S2_:
   37|   246k|BOTAN_FORCE_INLINE constexpr T ct_is_zero(T x) {
   38|   246k|   return ct_expand_top_bit<T>(~x & (x - 1));
   39|   246k|}
_ZN5Botan17ct_expand_top_bitITkNSt3__117unsigned_integralEhEET_S2_:
   28|   246k|BOTAN_FORCE_INLINE constexpr T ct_expand_top_bit(T a) {
   29|   246k|   const T top = CT::value_barrier<T>(a >> (sizeof(T) * 8 - 1));
   30|   246k|   return static_cast<T>(0) - top;
   31|   246k|}

_ZN5Botan2CT6OptionImEC2EvQsr3stdE21default_initializableIT_E:
  661|     98|            : Option(T(), Choice::no()) {}
_ZN5Botan2CT6Choice2noEv:
  307|    102|      constexpr static Choice no() { return Choice(0); }
_ZN5Botan2CT6ChoiceC2Em:
  341|    370|      constexpr explicit Choice(underlying_type v) : m_value(CT::value_barrier<underlying_type>(v)) {}
_ZN5Botan2CT6OptionImEC2EmNS0_6ChoiceE:
  653|    190|      constexpr Option(T v, Choice valid) : m_has_value(valid), m_value(std::move(v)) {}
_ZN5Botan2CT6OptionImEC2Em:
  656|      4|      constexpr explicit Option(T v) : Option(std::move(v), Choice::yes()) {}
_ZN5Botan2CT6Choice3yesEv:
  305|      4|      constexpr static Choice yes() { return !no(); }
_ZNK5Botan2CT6ChoicentEv:
  309|    180|      constexpr Choice operator!() const { return Choice(~value()); }
_ZNK5Botan2CT6Choice5valueEv:
  332|    180|      constexpr underlying_type value() const { return value_barrier(m_value); }
_ZNK5Botan2CT6OptionImE9has_valueEv:
  664|    372|      constexpr Choice has_value() const { return m_has_value; }
_ZNK5Botan2CT6Choice7as_boolEv:
  329|    380|      constexpr bool as_bool() const { return m_value != 0; }
_ZNK5Botan2CT6OptionImE5valueEv:
  679|      8|      constexpr const T& value() const {
  680|      8|         BOTAN_STATE_CHECK(m_has_value.as_bool());
  ------------------
  |  |   51|      8|   do {                                                         \
  |  |   52|      8|      /* NOLINTNEXTLINE(*-simplify-boolean-expr) */             \
  |  |   53|      8|      if(!(expr)) {                                             \
  |  |  ------------------
  |  |  |  Branch (53:10): [True: 0, False: 8]
  |  |  ------------------
  |  |   54|      0|         /* NOLINTNEXTLINE(bugprone-lambda-function-name) */    \
  |  |   55|      0|         Botan::throw_invalid_state(#expr, __func__, __FILE__); \
  |  |   56|      0|      }                                                         \
  |  |   57|      8|   } while(0)
  |  |  ------------------
  |  |  |  Branch (57:12): [Folded, False: 8]
  |  |  ------------------
  ------------------
  681|      8|         return m_value;
  682|      8|      }
_ZN5Botan2CT4MaskIhE7is_zeroEh:
  437|   246k|      static constexpr Mask<T> is_zero(T x) { return Mask<T>(ct_is_zero<T>(value_barrier<T>(x))); }
_ZN5Botan2CT4MaskIhEC2Eh:
  637|   739k|      constexpr explicit Mask(T m) : m_mask(m) {}
_ZNK5Botan2CT4MaskIhE9as_choiceEv:
  619|     88|      constexpr CT::Choice as_choice() const {
  620|       |         if constexpr(sizeof(T) >= sizeof(Choice::underlying_type)) {
  621|       |            return CT::Choice::from_mask(static_cast<Choice::underlying_type>(unpoisoned_value()));
  622|     88|         } else {
  623|     88|            return CT::Choice::from_int(unpoisoned_value());
  624|     88|         }
  625|     88|      }
_ZN5Botan2CT6Choice8from_intIhQaasr3stdE17unsigned_integralIT_Entsr3stdE7same_asIbS3_EEES1_S3_:
  268|     88|      constexpr static Choice from_int(T v) {
  269|     88|         if constexpr(sizeof(T) <= sizeof(underlying_type)) {
  270|     88|            return !Choice(ct_is_zero<underlying_type>(v));
  271|       |         } else {
  272|       |            // Mask of T that is either |0| or |1|
  273|       |            const T v_is_0 = ct_is_zero<T>(value_barrier<T>(v));
  274|       |
  275|       |            // We want the mask to be set if v != 0 so we must check that
  276|       |            // v_is_0 is itself zero.
  277|       |            //
  278|       |            // Also sizeof(T) may not equal sizeof(underlying_type) so we must
  279|       |            // use ct_is_zero<underlying_type>. It's ok to either truncate or
  280|       |            // zero extend v_is_0 to 32 bits since we know it is |0| or |1|
  281|       |            // so even just the low bit is sufficient.
  282|       |            return Choice(ct_is_zero<underlying_type>(static_cast<underlying_type>(v_is_0)));
  283|       |         }
  284|     88|      }
_ZNK5Botan2CT4MaskIhE16unpoisoned_valueEv:
  598|     88|      constexpr T unpoisoned_value() const {
  599|     88|         T r = value();
  600|     88|         CT::unpoison(r);
  601|     88|         return r;
  602|     88|      }
_ZNK5Botan2CT4MaskIhE5valueEv:
  630|  1.23M|      constexpr T value() const { return value_barrier<T>(m_mask); }
_ZN5Botan2CT8unpoisonITkNSt3__18integralEhEEvRKT_:
  112|     88|constexpr void unpoison(const T& p) {
  113|     88|   unpoison(&p, 1);
  114|     88|}
_ZN5Botan2CT8unpoisonIhEEvPKT_m:
   67|     88|constexpr inline void unpoison(const T* p, size_t n) {
   68|       |#if defined(BOTAN_HAS_VALGRIND)
   69|       |   if(!std::is_constant_evaluated()) {
   70|       |      VALGRIND_MAKE_MEM_DEFINED(p, n * sizeof(T));
   71|       |   }
   72|       |#endif
   73|       |
   74|     88|   BOTAN_UNUSED(p, n);
  ------------------
  |  |  144|     88|#define BOTAN_UNUSED Botan::ignore_params
  ------------------
   75|     88|}
_ZN5Botan2CT4MaskIhE3setEv:
  382|     88|      static constexpr Mask<T> set() { return Mask<T>(static_cast<T>(~0)); }
_ZN5Botan2CT4MaskIhE7clearedEv:
  387|     88|      static constexpr Mask<T> cleared() { return Mask<T>(0); }
_ZN5Botan2CT4MaskIhE8is_equalEhh:
  442|   123k|      static constexpr Mask<T> is_equal(T x, T y) {
  443|   123k|         const T diff = value_barrier(x) ^ value_barrier(y);
  444|   123k|         return Mask<T>::is_zero(diff);
  445|   123k|      }
_ZNK5Botan2CT4MaskIhEcoEv:
  533|   123k|      constexpr Mask<T> operator~() const { return Mask<T>(~value()); }
_ZNK5Botan2CT4MaskIhE13if_set_returnEh:
  538|   123k|      constexpr T if_set_return(T x) const { return value() & x; }
_ZN5Botan2CT12is_not_equalIhEENS0_4MaskIT_EEPKS3_S6_m:
  838|     88|constexpr inline CT::Mask<T> is_not_equal(const T x[], const T y[], size_t len) {
  839|     88|   return ~CT::is_equal(x, y, len);
  840|     88|}
_ZN5Botan2CT8is_equalIhEENS0_4MaskIT_EEPKS3_S6_m:
  798|     88|constexpr inline CT::Mask<T> is_equal(const T x[], const T y[], size_t len) {
  799|     88|   if(std::is_constant_evaluated()) {
  ------------------
  |  Branch (799:7): [Folded, False: 88]
  ------------------
  800|      0|      T difference = 0;
  801|       |
  802|      0|      for(size_t i = 0; i != len; ++i) {
  ------------------
  |  Branch (802:25): [True: 0, False: 0]
  ------------------
  803|      0|         difference = difference | (x[i] ^ y[i]);
  804|      0|      }
  805|       |
  806|      0|      return CT::Mask<T>::is_zero(difference);
  807|     88|   } else {
  808|     88|      volatile T difference = 0;
  809|       |
  810|    440|      for(size_t i = 0; i != len; ++i) {
  ------------------
  |  Branch (810:25): [True: 352, False: 88]
  ------------------
  811|    352|         difference = difference | (x[i] ^ y[i]);
  812|    352|      }
  813|       |
  814|     88|      return CT::Mask<T>::is_zero(difference);
  815|     88|   }
  816|     88|}
_ZN5Botan2CTanENS0_4MaskIhEES2_:
  518|   246k|      friend Mask<T> operator&(Mask<T> x, Mask<T> y) { return Mask<T>(x.value() & y.value()); }
_ZN5Botan2CTorENS0_4MaskIhEES2_:
  528|   123k|      friend Mask<T> operator|(Mask<T> x, Mask<T> y) { return Mask<T>(x.value() | y.value()); }
_ZN5Botan2CT4MaskIhEoRES2_:
  510|   123k|      Mask<T>& operator|=(Mask<T> o) {
  511|   123k|         m_mask |= o.value();
  512|   123k|         return (*this);
  513|   123k|      }
_ZN5Botan2CT4MaskIhEaNES2_:
  494|   123k|      Mask<T>& operator&=(Mask<T> o) {
  495|   123k|         m_mask &= o.value();
  496|   123k|         return (*this);
  497|   123k|      }

_ZN5Botan2CT13value_barrierITkNSt3__117unsigned_integralEmQntsr3stdE7same_asIbT_EEES3_S3_:
   43|    638|constexpr inline T value_barrier(T x) {
   44|    638|   if(std::is_constant_evaluated()) {
  ------------------
  |  Branch (44:7): [Folded, False: 638]
  ------------------
   45|      0|      return x;
   46|    638|   } else {
   47|    638|#if defined(BOTAN_CT_VALUE_BARRIER_USE_ASM)
   48|       |      /*
   49|       |      * We may want a "stronger" statement such as
   50|       |      *     asm volatile("" : "+r,m"(x) : : "memory);
   51|       |      * (see https://theunixzoo.co.uk/blog/2021-10-14-preventing-optimisations.html)
   52|       |      * however the current approach seems sufficient with current compilers,
   53|       |      * and is minimally damaging with regards to degrading code generation.
   54|       |      */
   55|    638|      asm("" : "+r"(x) : /* no input */);  // NOLINT(*-no-assembler)
   56|    638|      return x;
   57|       |#elif defined(BOTAN_CT_VALUE_BARRIER_USE_VOLATILE)
   58|       |      volatile T vx = x;
   59|       |      return vx;
   60|       |#else
   61|       |      return x;
   62|       |#endif
   63|    638|   }
   64|    638|}
_ZN5Botan2CT13value_barrierITkNSt3__117unsigned_integralEhQntsr3stdE7same_asIbT_EEES3_S3_:
   43|  1.97M|constexpr inline T value_barrier(T x) {
   44|  1.97M|   if(std::is_constant_evaluated()) {
  ------------------
  |  Branch (44:7): [Folded, False: 1.97M]
  ------------------
   45|      0|      return x;
   46|  1.97M|   } else {
   47|  1.97M|#if defined(BOTAN_CT_VALUE_BARRIER_USE_ASM)
   48|       |      /*
   49|       |      * We may want a "stronger" statement such as
   50|       |      *     asm volatile("" : "+r,m"(x) : : "memory);
   51|       |      * (see https://theunixzoo.co.uk/blog/2021-10-14-preventing-optimisations.html)
   52|       |      * however the current approach seems sufficient with current compilers,
   53|       |      * and is minimally damaging with regards to degrading code generation.
   54|       |      */
   55|  1.97M|      asm("" : "+r"(x) : /* no input */);  // NOLINT(*-no-assembler)
   56|  1.97M|      return x;
   57|       |#elif defined(BOTAN_CT_VALUE_BARRIER_USE_VOLATILE)
   58|       |      volatile T vx = x;
   59|       |      return vx;
   60|       |#else
   61|       |      return x;
   62|       |#endif
   63|  1.97M|   }
   64|  1.97M|}

_ZN5Botan13ignore_paramsIJPKhmEEEvDpRKT_:
  142|     88|constexpr void ignore_params([[maybe_unused]] const T&... args) {}

LLVMFuzzerInitialize:
   28|      2|extern "C" int LLVMFuzzerInitialize(int* /*argc*/, char*** /*argv*/) {
   29|       |   /*
   30|       |   * This disables the mlock pool, as overwrites within the pool are
   31|       |   * opaque to ASan or other instrumentation.
   32|       |   */
   33|      2|   ::setenv("BOTAN_MLOCK_POOL_SIZE", "0", 1);
   34|      2|   return 0;
   35|      2|}
LLVMFuzzerTestOneInput:
   39|    105|extern "C" int LLVMFuzzerTestOneInput(const uint8_t in[], size_t len) {
   40|    105|   if(len <= max_fuzzer_input_size) {
  ------------------
  |  Branch (40:7): [True: 95, False: 10]
  ------------------
   41|     95|      try {
   42|     95|         fuzz(std::span<const uint8_t>(in, len));
   43|     95|      } catch(const std::exception& e) {
   44|      0|         std::cerr << "Uncaught exception from fuzzer driver " << e.what() << "\n";
   45|      0|         abort();
   46|      0|      } catch(...) {
   47|      0|         std::cerr << "Uncaught exception from fuzzer driver (unknown type)\n";
   48|      0|         abort();
   49|      0|      }
   50|     95|   }
   51|    105|   return 0;
   52|    105|}

_Z4fuzzNSt3__14spanIKhLm18446744073709551615EEE:
   42|     95|void fuzz(std::span<const uint8_t> in) {
   43|     95|   static const std::vector<uint8_t> Phash = {1, 2, 3, 4};
   44|       |
   45|     95|   auto lib_idx = Botan::oaep_find_delim(in, std::span{Phash});
   46|       |
   47|     95|   auto ref_idx = ref_oaep_unpad(in, Phash);
   48|       |
   49|     95|   if(lib_idx.has_value().as_bool() && ref_idx.has_value().as_bool()) {
  ------------------
  |  Branch (49:7): [True: 4, False: 91]
  |  Branch (49:7): [True: 4, False: 91]
  |  Branch (49:40): [True: 4, False: 0]
  ------------------
   50|      4|      FUZZER_ASSERT_EQUAL(lib_idx.value(), ref_idx.value());
  ------------------
  |  |   79|      4|   do {                                                                                      \
  |  |   80|      4|      if((x) != (y)) {                                                                       \
  |  |  ------------------
  |  |  |  Branch (80:10): [True: 0, False: 4]
  |  |  ------------------
  |  |   81|      0|         FUZZER_WRITE_AND_CRASH(#x << " = " << (x) << " != " << #y << " = " << (y) << "\n"); \
  |  |  ------------------
  |  |  |  |   70|      0|   do {                                                                                                       \
  |  |  |  |   71|      0|      std::cerr << expr << " @ Line " << __LINE__ << " in " << __FILE__ << "\n"; /* NOLINT(*-macro-paren*) */ \
  |  |  |  |   72|      0|      abort();                                                                                                \
  |  |  |  |   73|      0|   } while(0)
  |  |  |  |  ------------------
  |  |  |  |  |  Branch (73:12): [Folded, False: 0]
  |  |  |  |  ------------------
  |  |  ------------------
  |  |   82|      0|      }                                                                                      \
  |  |   83|      4|   } while(0)
  |  |  ------------------
  |  |  |  Branch (83:12): [Folded, False: 4]
  |  |  ------------------
  ------------------
   51|     91|   } else if(lib_idx.has_value().as_bool() && !ref_idx.has_value().as_bool()) {
  ------------------
  |  Branch (51:14): [True: 0, False: 91]
  |  Branch (51:14): [True: 0, False: 91]
  |  Branch (51:47): [True: 0, False: 0]
  ------------------
   52|      0|      FUZZER_WRITE_AND_CRASH("Ref accepted but lib rejected\n");
  ------------------
  |  |   70|      0|   do {                                                                                                       \
  |  |   71|      0|      std::cerr << expr << " @ Line " << __LINE__ << " in " << __FILE__ << "\n"; /* NOLINT(*-macro-paren*) */ \
  |  |   72|      0|      abort();                                                                                                \
  |  |   73|      0|   } while(0)
  |  |  ------------------
  |  |  |  Branch (73:12): [Folded, False: 0]
  |  |  ------------------
  ------------------
   53|     91|   } else if(!lib_idx.has_value().as_bool() && ref_idx.has_value().as_bool()) {
  ------------------
  |  Branch (53:14): [True: 91, False: 0]
  |  Branch (53:14): [True: 0, False: 91]
  |  Branch (53:48): [True: 0, False: 91]
  ------------------
   54|      0|      FUZZER_WRITE_AND_CRASH("Lib accepted but ref rejected\n");
  ------------------
  |  |   70|      0|   do {                                                                                                       \
  |  |   71|      0|      std::cerr << expr << " @ Line " << __LINE__ << " in " << __FILE__ << "\n"; /* NOLINT(*-macro-paren*) */ \
  |  |   72|      0|      abort();                                                                                                \
  |  |   73|      0|   } while(0)
  |  |  ------------------
  |  |  |  Branch (73:12): [Folded, False: 0]
  |  |  ------------------
  ------------------
   55|      0|   }
   56|     95|}
oaep.cpp:_ZN12_GLOBAL__N_114ref_oaep_unpadENSt3__14spanIKhLm18446744073709551615EEERKNS0_6vectorIhNS0_9allocatorIhEEEE:
   14|     95|Botan::CT::Option<size_t> ref_oaep_unpad(std::span<const uint8_t> in, const std::vector<uint8_t>& Phash) {
   15|     95|   const size_t hlen = Phash.size();
   16|       |
   17|     95|   if(in.size() < 2 * hlen + 1) {
  ------------------
  |  Branch (17:7): [True: 7, False: 88]
  ------------------
   18|      7|      return Botan::CT::Option<size_t>();
   19|      7|   }
   20|       |
   21|    313|   for(size_t i = hlen; i != 2 * hlen; ++i) {
  ------------------
  |  Branch (21:25): [True: 260, False: 53]
  ------------------
   22|    260|      if(in[i] != Phash[i - hlen]) {
  ------------------
  |  Branch (22:10): [True: 35, False: 225]
  ------------------
   23|     35|         return Botan::CT::Option<size_t>();
   24|     35|      }
   25|    260|   }
   26|       |
   27|    611|   for(size_t i = 2 * hlen; i != in.size(); ++i) {
  ------------------
  |  Branch (27:29): [True: 599, False: 12]
  ------------------
   28|    599|      if(in[i] != 0x00 && in[i] != 0x01) {
  ------------------
  |  Branch (28:10): [True: 41, False: 558]
  |  Branch (28:27): [True: 37, False: 4]
  ------------------
   29|     37|         return Botan::CT::Option<size_t>();
   30|     37|      }
   31|       |
   32|    562|      if(in[i] == 0x01) {
  ------------------
  |  Branch (32:10): [True: 4, False: 558]
  ------------------
   33|      4|         return Botan::CT::Option<size_t>(i + 1);
   34|      4|      }
   35|    562|   }
   36|       |
   37|     12|   return Botan::CT::Option<size_t>();
   38|     53|}

_ZN5Botan15oaep_find_delimENSt3__14spanIKhLm18446744073709551615EEES3_:
  102|     95|CT::Option<size_t> oaep_find_delim(std::span<const uint8_t> input, std::span<const uint8_t> phash) {
  103|       |   // Too short to be valid, reject immediately
  104|     95|   if(input.size() < 1 + 2 * phash.size()) {
  ------------------
  |  Branch (104:7): [True: 7, False: 88]
  ------------------
  105|      7|      return {};
  106|      7|   }
  107|       |
  108|     88|   size_t delim_idx = 2 * phash.size();
  109|     88|   CT::Mask<uint8_t> waiting_for_delim = CT::Mask<uint8_t>::set();
  110|     88|   CT::Mask<uint8_t> bad_input_m = CT::Mask<uint8_t>::cleared();
  111|       |
  112|   123k|   for(const uint8_t ib : input.subspan(2 * phash.size())) {
  ------------------
  |  Branch (112:25): [True: 123k, False: 88]
  ------------------
  113|   123k|      const auto zero_m = CT::Mask<uint8_t>::is_zero(ib);
  114|   123k|      const auto one_m = CT::Mask<uint8_t>::is_equal(ib, 1);
  115|       |
  116|   123k|      const auto add_m = waiting_for_delim & zero_m;
  117|       |
  118|   123k|      bad_input_m |= waiting_for_delim & ~(zero_m | one_m);
  119|       |
  120|   123k|      delim_idx += add_m.if_set_return(1);
  121|       |
  122|   123k|      waiting_for_delim &= zero_m;
  123|   123k|   }
  124|       |
  125|       |   // If we never saw any non-zero byte, then it's not valid input
  126|     88|   bad_input_m |= waiting_for_delim;
  127|       |
  128|       |   // If the P hash is wrong, then it's not valid
  129|     88|   bad_input_m |= CT::is_not_equal(&input[phash.size()], phash.data(), phash.size());
  130|       |
  131|     88|   delim_idx += 1;
  132|       |
  133|     88|   const auto accept = !(bad_input_m.as_choice());
  134|       |
  135|     88|   return CT::Option(delim_idx, accept);
  136|     95|}

