Skip to content

Fix several wrongs#1267

Merged
trcrsired merged 1 commit intocppfastio:nextfrom
nukyan:fixes
Mar 24, 2026
Merged

Fix several wrongs#1267
trcrsired merged 1 commit intocppfastio:nextfrom
nukyan:fixes

Conversation

@nukyan
Copy link

@nukyan nukyan commented Mar 23, 2026

Authored by Opus 4.6.

  Potential Issues Found in fast_io

  1. CONFIRMED BUG: Type alias copy-paste errors in simd_find.h

  include/fast_io_core_impl/simd_find.h:10 — Named unsigned_char_type but uses make_signed_t:
  using unsigned_char_type = ::std::make_signed_t<char_type>;  // should be make_unsigned_t

  include/fast_io_core_impl/simd_find.h:216 — Named signed_char_type but uses make_unsigned_t:
  using signed_char_type = ::std::make_unsigned_t<unsigned_char_type>;  // should be make_signed_t

  Both are swapped. This will produce incorrect SIMD comparisons when signed/unsigned semantics differ (e.g.,
  characters > 127).

  ---
  2. CONFIRMED BUG: Return type mismatch in lc_numbers/cond.h

  include/fast_io_i18n/lc_numbers/cond.h:106,110 — Function returns char_type* but two branches return
  ::std::size_t:
  else if constexpr (reserve_printable<char_type, T1>)
  {
      constexpr ::std::size_t sz{print_reserve_size(io_reserve_type<char_type, T1>)};
      return sz;           // line 106: returns size_t, not char_type*
  }
  else
  {
      return print_reserve_size(io_reserve_type<char_type, T1>, c);  // line 110: same issue
  }

  These branches will cause a compilation error if instantiated, or if SFINAE doesn't exclude them, they
  produce undefined behavior via implicit integer-to-pointer conversion.

  ---
  3. CONFIRMED: Empty AES encryption operator in aes.h

  include/fast_io_crypto/cipher/aes.h:139-140:
  inline void operator()(::std::byte const *from, ::std::size_t blocks, ::std::byte *to) noexcept
  {}

  The key schedule is computed in the constructor, but the actual encryption function is a no-op. Anyone using
   this AES implementation gets no encryption.

  ---
  4. CONFIRMED: Syntax error in disabled code in read_all.h

  include/fast_io_core_impl/read_all.h:107 (inside #if 0):
  to_read-=remains/;  // missing right operand

  Plus an incomplete if statement at line 113 and missing braces. Won't compile if re-enabled.

  ---
  5. Integer overflow risk in dynamic_output_buffer.h

  include/fast_io_core_impl/dynamic_output_buffer.h:70:
  ::std::size_t to_allocate{bfsz + diff};

  The addition bfsz + diff has no overflow check. If it wraps, to_allocate becomes smaller than diff, leading
  to an undersized allocation followed by an out-of-bounds write at line 101. The subsequent doubling logic
  (lines 74-84) doesn't guard against the initial addition overflowing.

  ---
  6. PKCS7 padding unsigned loop boundary risk

  include/fast_io_crypto/padding/pkcs7_padding.h:20:
  for (::std::size_t i(block_size - 1u); i >= block_size - remaining_length; --i)

  The guard at line 13 prevents remaining_length >= block_size, but if remaining_length is 0, pad is 0x00 and
  the loop doesn't execute — which is semantically wrong for PKCS7 (a full block of padding value block_size
  should be added when input is block-aligned). The unsigned decrement of i past 0 would wrap to SIZE_MAX if
  the guard were ever relaxed.

  ---
  7. MSVC vector growth: correct but asymmetric with non-MSVC path

  include/fast_io_dsal/impl/vector.h:52-63:
  #if defined(_MSC_VER) && !defined(__clang__)
      ::std::size_t mx{SIZE_MAX / size};
      if (newcap > mx) [[unlikely]]
          ::fast_io::fast_terminate();
      newcap *= size;
  #else
      if (__builtin_mul_overflow(size, newcap, __builtin_addressof(newcap))) [[unlikely]]
          __builtin_trap();
  #endif

  The MSVC path is technically correct (the multiplication can't overflow after the check), but uses
  fast_terminate() while the non-MSVC path uses __builtin_trap(). This behavioral asymmetry means different
  error handling on different compilers for the same condition.

  ---
  8. String push_back potential edge case

  include/fast_io_dsal/impl/string.h:77:
  ::std::size_t const bfsizep1{bfsize + 1u};

  If bfsize == SIZE_MAX (theoretically impossible from pointer subtraction but not statically prevented),
  bfsizep1 wraps to 0, bypasses the check at line 80, and bfsizep1 << 1u produces 0, leading to a zero-size
  allocation.

  ---
  Summary by severity

  ┌──────────┬──────────────────────────────────────────────┬────────────────────────────┐
  │ Severity │                    Issue                     │          Location          │
  ├──────────┼──────────────────────────────────────────────┼────────────────────────────┤
  │ Bug      │ make_signed_t/make_unsigned_t swapped        │ simd_find.h:10,216         │
  ├──────────┼──────────────────────────────────────────────┼────────────────────────────┤
  │ Bug      │ Returns size_t instead of char_type*         │ lc_numbers/cond.h:106,110  │
  ├──────────┼──────────────────────────────────────────────┼────────────────────────────┤
  │ Bug      │ AES operator() is empty (no encryption)      │ aes.h:139-140              │
  ├──────────┼──────────────────────────────────────────────┼────────────────────────────┤
  │ Bug      │ Syntax error in disabled code                │ read_all.h:107             │
  ├──────────┼──────────────────────────────────────────────┼────────────────────────────┤
  │ High     │ Unchecked size_t addition can overflow       │ dynamic_output_buffer.h:70 │
  ├──────────┼──────────────────────────────────────────────┼────────────────────────────┤
  │ Medium   │ PKCS7 remaining_length==0 semantics          │ pkcs7_padding.h:20         │
  ├──────────┼──────────────────────────────────────────────┼────────────────────────────┤
  │ Medium   │ Inconsistent overflow behavior (MSVC vs GCC) │ vector.h:52-63             │
  ├──────────┼──────────────────────────────────────────────┼────────────────────────────┤
  │ Low      │ Theoretical SIZE_MAX wraparound              │ string.h:77                │
  └──────────┴──────────────────────────────────────────────┴────────────────────────────┘

  The most impactful issues are #1 (wrong SIMD comparisons at runtime), #2 (compilation failure or UB on
  certain type instantiations), and #3 (AES encryption silently does nothing).

@trcrsired trcrsired merged commit 01ebc56 into cppfastio:next Mar 24, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants