Skip to content

Security review fixes#982

Open
alandefreitas wants to merge 20 commits intoboostorg:developfrom
alandefreitas:security
Open

Security review fixes#982
alandefreitas wants to merge 20 commits intoboostorg:developfrom
alandefreitas:security

Conversation

@alandefreitas
Copy link
Member

  • Boost.URL underwent two security assessments by Laurel Lye Systems Engineering in February 2026.
  • This PR tracks the triage and resolution of all 1,234 findings across both reports.
  • Each finding was manually reviewed against the source code and categorized as a confirmed bug (and fixed), a false positive, or a deliberate design choice.
  • AI was used only as a supporting tool to help summarize and organize the triage results, propose new test cases, and provide an extra review on each of the code fixes.
  • Of the 1,234 findings, 27 pointed to confirmed bugs resulting in 18 fix commits; the remainder were false positives or by-design patterns.
  • Most false positives stem from situations where the library intentionally delegates responsibility to the caller (primarily BOOST_ASSERT preconditions on internal APIs, non-owning view types, and raw pointer parsing routines).

Summary

v1 (Feb 2, 2026)

Verdict CRITICAL HIGH MEDIUM LOW INFO Total
FIXED 1 9 0 2 3 15
FALSE POSITIVE 3 47 46 186 110 392
BY DESIGN 0 129 445 170 56 800
Total 4 185 491 358 169 1207

v2 (Feb 17, 2026)

Verdict HIGH MEDIUM LOW INFO Total
FIXED 7 3 1 1 12
FALSE POSITIVE 2 2 0 0 4
BY DESIGN 0 0 1 1 2
ALREADY FIXED 0 5 4 0 9
Total 9 10 6 2 27

All Fixes (18 commits)

# Commit Severity Description Source
1 f0a80ad HIGH encode() UB pointer arithmetic for small buffers v1
2 af227d4 CRITICAL url_base loop condition order v1 (C4)
3 61c8962 HIGH LLONG_MIN negation UB in format v1
4 bcd76d8 HIGH ci_less::operator() return type v1
5 fc51e83 HIGH incorrect noexcept in segments_base::front() and back() v1
6 f4f723e HIGH recycled_ptr::get() nullptr when empty v1
7 217c954 HIGH format center-alignment padding v2
8 f1d59ab HIGH decode_view::ends_with with empty string v2
9 1c6e27d HIGH stale pattern n.path after colon-encoding v2
10 ce4755d HIGH ci_is_less OOB read v2 (also corrects v1 H-MISC-13)
11 1f17914 HIGH recycled_ptr copy self-assignment v2
12 0c22bcd MEDIUM url move self-assignment v2
13 7bceccc MEDIUM encode_one signed char right-shift v2
14 d304da8 MEDIUM encode() noexcept on throwing template v2
15 0148a7a LOW port_rule has_number for port zero at end of input v2
16 70b0e49 INFO ci_equal arguments by const reference v2
17 c935b72 LOW decode() noexcept on throwing template v1
18 422153c INFO error_types copy-paste deprecation messages v1

Finding Details

Per-finding triage with a verdict and a rationale for each individual finding.

CRITICAL (4 findings)

C1. Buffer overflow in format_args.cpp string formatting

  • Source: v1
  • Location: src/detail/format_args.cpp:196
  • Issue: Writes encoded output via raw char* from ctx.out() assuming sufficient capacity.
  • FALSE POSITIVE: two-phase measure/format design guarantees buffer is pre-sized correctly.

C2. Buffer overflow in format_args.cpp integer formatting

  • Source: v1
  • Location: src/detail/format_args.cpp:510
  • Issue: Same raw-pointer output pattern in integer formatter.
  • FALSE POSITIVE: same two-phase design as C1.

C3. Buffer overflow in format_args.cpp unsigned integer formatting

  • Source: v1
  • Location: src/detail/format_args.cpp:613
  • Issue: Same unsafe raw-pointer output pattern for unsigned integers.
  • FALSE POSITIVE: same two-phase design as C1.

C4. Out-of-bounds read in url_base.cpp loop condition

  • Source: v1 (also reported as v2-MED-6)
  • Location: include/boost/url/impl/url_base.hpp:248,2092,2616
  • Issue: Loop condition dereferences *it before checking it != end. Three instances.
  • FIXED (af227d4, "fix: url_base loop condition order"): Reordered conditions to while (it != end && *it != '/').
HIGH (185 v1 + 9 v2 = 194 findings)

Confirmed bugs fixed (12 fixes)

Format center-alignment padding heap overflow

  • Source: v2-HIGH-5/6
  • Location: src/detail/format_args.cpp:190
  • Issue: lpad = w / 2 used total width instead of padding amount, so lpad + rpad > pad, causing writes past the pre-measured buffer.
  • FIXED (217c954, "fix: format center-alignment padding"): Changed to lpad = pad / 2.

decode_view::ends_with infinite loop on empty string

  • Source: v2-HIGH-4
  • Location: src/decode_view.cpp:106-124
  • Issue: When s is empty, s.size() is 0, decrementing iterators and n - 1 wraps around causing infinite loop/UB.
  • FIXED (f1d59ab, "fix: decode_view::ends_with with empty string"): Added if(s.empty()) return true; early return.

Stale n.path after colon-encoding in pattern.cpp

  • Source: v2-HIGH-7
  • Location: src/detail/pattern.cpp:268-291
  • Issue: After encoding colons in the first segment (growing path by diff), n.path was not updated. The subsequent // prefix check used the stale value for memmove.
  • FIXED (1c6e27d, "fix: stale pattern n.path after colon-encoding"): Added n.path += diff; after the colon-encoding block.

ci_is_less OOB read on mismatched-length strings

  • Source: v2-HIGH-8/9, corrects v1 H-MISC-13 (was wrongly marked FALSE POSITIVE)
  • Location: src/grammar/ci_string.cpp:58-74
  • Issue: Loop used s0.size() as bound but read from both strings. When s0.size() > s1.size(), OOB read on s1. Also returned false (equal) when s0 is a proper prefix of s1. Unlike ci_is_equal, the public ci_is_less() does NOT check sizes before calling detail.
  • FIXED (ce4755d, "fix: ci_is_less OOB read"): Use min(s0.size(), s1.size()) as loop bound, fall through to size comparison.

recycled_ptr copy assignment self-assignment UAF

  • Source: v2-HIGH-2
  • Location: include/boost/url/grammar/impl/recycled.hpp:185
  • Issue: Self-assignment released the node then tried to re-acquire the same (now-freed) pointer.
  • FIXED (1f17914, "fix: recycled_ptr copy self-assignment"): Added if(this == &other) return *this; guard.

Signed integer overflow: LLONG_MIN negation (3 findings)

  • Source: v1 H-OVERFLOW-1/2/3 (also reported as v2-MED-5)
  • Locations: src/detail/format_args.cpp:367, src/detail/format_args.cpp:453, src/detail/format_args.cpp:519
  • Issue: Negating LLONG_MIN is undefined behavior. Three separate instances.
  • FIXED (61c8962, "fix: LLONG_MIN negation UB in format"): Replaced with unsigned arithmetic throughout.

ci_less::operator() incorrect return type

  • Source: v1 H-MISC-1 (also reported as v2-LOW-1)
  • Location: include/boost/url/grammar/ci_string.hpp:333
  • Issue: Returns std::size_t instead of bool.
  • FIXED (bcd76d8, "fix: ci_less::operator() return type"): Changed return type to bool.

segments_base front()/back() incorrect noexcept

  • Source: v1 H-MISC-24 (also reported as v2-LOW-3/4)
  • Location: include/boost/url/segments_base.hpp:250
  • Issue: front() and back() marked noexcept but can throw.
  • FIXED (fc51e83, "fix: incorrect noexcept in segments_base::front() and back()"): Removed incorrect noexcept.

recycled_ptr::get() null dereference

  • Source: v1 H-MISC-7 (also reported as v2-MED-2)
  • Location: include/boost/url/grammar/recycled.hpp:452
  • Issue: get() dereferenced p_ without null check.
  • FIXED (f4f723e, "fix: recycled_ptr::get() nullptr when empty"): Added null check to return nullptr when empty.

Pointer arithmetic UB in encode() for small buffers

  • Source: v1 (also reported as v2-LOW-2)
  • Location: include/boost/url/impl/encode.hpp
  • Issue: UB pointer arithmetic when encoding into small/empty buffers.
  • FIXED (f0a80ad, "fix: encode() UB pointer arithmetic for small buffers"): Avoid UB pointer arithmetic.

False positives (v2)

Data race on recycled_ptr refcount

  • Source: v2-HIGH-1
  • Location: include/boost/url/grammar/recycled.hpp:97-102
  • FALSE POSITIVE: refs IS std::atomic<std::size_t> when threads enabled (BOOST_URL_DISABLE_THREADS guard). Same issue as v1 Theme 5.

decode() output buffer insufficient

  • Source: v2-HIGH-3
  • Location: include/boost/url/detail/decode.hpp
  • FALSE POSITIVE: decode_unsafe checks dest == end at line 90 and returns early. Truncation is documented behavior.

Theme: BOOST_ASSERT as sole bounds check (29 findings, v1)

TRIAGE: BY DESIGN. Internal _unsafe functions and detail routines. The public API validates all inputs before calling them. The _unsafe suffix explicitly signals the precondition contract. Adding redundant runtime checks would duplicate validation already performed at the public API boundary. Standard Boost/STL design pattern (cf. std::vector::operator[] vs at()).

Finding Location Detail
H-ASSERT-1 detail/encode.hpp:58 Assumes % + two hex digits
H-ASSERT-2 detail/encode.hpp:77 re_encode_unsafe raw pointer writes
H-ASSERT-3 detail/encode.hpp:90 Encode lambda 3-byte write
H-ASSERT-4 detail/encode.hpp:121 Percent-encoding pointer arithmetic
H-ASSERT-5 detail/segments_range.hpp:82 Pointer arithmetic with offsets
H-ASSERT-6 detail/url_impl.hpp:110 size_t to 32-bit narrowing
H-ASSERT-7 detail/normalize.cpp:318 remove_dot_segments buffer write
H-ASSERT-8 detail/normalize.cpp:362 Append lambda bounds
H-ASSERT-9 detail/over_allocator.hpp:95 n == 1 precondition
H-ASSERT-10 detail/params_iter_impl.cpp:89 Percent-decoding length
H-ASSERT-11 detail/pattern.cpp:75 Bracket [/] pairing
H-ASSERT-12 detail/pattern.cpp:78 host.substr(1, host.size()-2)
H-ASSERT-13 detail/pattern.cpp:188 Formatting bracket assumptions
H-ASSERT-13b detail/pattern.cpp:191 Duplicate of H-ASSERT-13: same bracket/substr pattern
H-ASSERT-14 detail/pct_format.cpp:54 End iterator dereference (pct_vmeasure)
H-ASSERT-15 detail/pct_format.cpp:107 Closing brace assumption (pct_vmeasure)
H-ASSERT-16 detail/pct_format.cpp:152 End iterator dereference (pct_vformat)
H-ASSERT-17 detail/pct_format.cpp:205 Closing brace assumption (pct_vformat)
H-ASSERT-18 detail/segments_iter_impl.cpp:113 Percent-encoding OOB
H-ASSERT-19 detail/segments_iter_impl.cpp:156 Increment OOB
H-ASSERT-20 detail/url_impl.cpp:205 Array indexing with enum id
H-ASSERT-21 impl/decode_view.hpp:73 Iterator operator++()
H-ASSERT-22 impl/decode_view.hpp:84 Iterator operator--()
H-ASSERT-23 impl/encode.hpp:95 encoded_size overflow
H-ASSERT-24 impl/encode.hpp:194 encode_unsafe bounds
H-ASSERT-25 detail/format_args.cpp:134 Parse unchecked dereference
H-ASSERT-26 detail/format_args.cpp:346 Integer parse dereference
H-ASSERT-27 rfc/detail/query_part_rule.hpp:54 query_rule "must not fail"
H-ASSERT-28 detail/vformat.cpp:25 Unchecked optional .value()

Theme: Non-owning view/pointer lifetime safety (27 findings, v1)

TRIAGE: BY DESIGN. Non-owning views are fundamental to C++ (cf. std::string_view, std::span). Callers responsible for ensuring referenced data outlives the view. Documented and inherent to the view-based design.

Finding Location Type
H-LIFE-1 authority_view.hpp:242 data() raw pointer
H-LIFE-2 param.hpp:427 param_view non-owning
H-LIFE-3 params_encoded_base.hpp:71 Iterator aliasing
H-LIFE-4 params_encoded_view.hpp:166 Dangling view
H-LIFE-5 params_ref.hpp:91 Raw pointer
H-LIFE-6 params_view.hpp:186 Dangling view
H-LIFE-7 params_view.hpp:253 Dangling view (opts)
H-LIFE-8 segments_encoded_base.hpp:39 Non-owning path_ref
H-LIFE-9 segments_encoded_ref.hpp:84 Raw pointer
H-LIFE-10 segments_encoded_view.hpp:161 Dangling view
H-LIFE-10b segments_encoded_view.hpp:162 Duplicate of H-LIFE-10: same constructor
H-LIFE-11 segments_ref.hpp:80 Raw pointer
H-LIFE-12 segments_view.hpp:160 Dangling view
H-LIFE-13 url_view.hpp:197 Non-owning constructor
H-LIFE-14 url_view.cpp:48 Dangling pi_ pointer
H-LIFE-15 grammar/charset.hpp:207 charset_ref dangling
H-LIFE-16 grammar/range_rule.hpp:235 Non-owning string_view
H-LIFE-17 grammar/string_view_base.hpp:60 Dangling view
H-LIFE-19 detail/optional_string.hpp:38 Dangling string_view
H-LIFE-20 detail/optional_string.hpp:73 Dereferenced optional
H-LIFE-21 detail/format_args.hpp:103 Type erasure UAF
H-LIFE-22 detail/impl/format_args.hpp:70 Dangling pointer
H-LIFE-23 detail/impl/format_args.hpp:80 named_arg rvalue
H-LIFE-24 params_encoded_ref.cpp:34 Raw pointer
H-LIFE-25 segments_ref.cpp:32 Raw pointer
H-LIFE-26 segments_encoded_view.cpp:29 Parse temporary
H-LIFE-27 params_encoded_ref.hpp:82 Non-owning raw url_base* pointer (same pattern as H-LIFE-5)

Theme: Raw pointer buffer APIs (45 findings, v1)

TRIAGE: BY DESIGN. Internal _unsafe functions accept raw pointers with documented preconditions. The public API handles buffer management.

Finding Location Detail
H-RAW-1 detail/any_params_iter.hpp:79 Copy interface
H-RAW-1b detail/any_params_iter.hpp:80 Duplicate of H-RAW-1: same copy() raw pointer interface
H-RAW-2 detail/any_segments_iter.hpp:70 Copy interface
H-RAW-3 detail/decode.hpp:26 decode_one raw pointer
H-RAW-4 detail/decode.hpp:34 decode_bytes_unsafe
H-RAW-5 detail/decode.hpp:42 decode_unsafe raw output
H-RAW-6 detail/format_args.hpp:239 Raw output pointer
H-RAW-7 detail/normalize.hpp:158 remove_dot_segments
H-RAW-7b detail/normalize.hpp:159 Duplicate of H-RAW-7: same function, pointer arithmetic
H-RAW-8a detail/any_params_iter.cpp:115 encode_unsafe
H-RAW-8b detail/any_params_iter.cpp:117 encode_unsafe
H-RAW-8c detail/any_params_iter.cpp:210 Unchecked buffer increment
H-RAW-8d detail/any_params_iter.cpp:265 Unchecked buffer increment
H-RAW-8e detail/any_params_iter.cpp:329 Unchecked buffer increment
H-RAW-8f detail/any_params_iter.cpp:374 Unchecked buffer increment
H-RAW-9a detail/any_segments_iter.cpp:66 Unsafe raw-pointer buffer write
H-RAW-9b detail/any_segments_iter.cpp:153 Unsafe re-encoding APIs
H-RAW-10 detail/decode.cpp:25 decode_one unchecked
H-RAW-11 detail/move_chars.hpp:78 Pointer subtraction
H-RAW-11b detail/move_chars.hpp:81 Duplicate of H-RAW-11: same function, memmove bounds
H-RAW-12 detail/normalize.cpp:33 Unchecked percent-decoding
H-RAW-12b detail/normalize.cpp:40 Duplicate of H-RAW-12: same percent-decoding helper
H-RAW-13 detail/url_impl.cpp:89 Unchecked memcpy
H-RAW-14 url_view_base.cpp:76 Struct hack
H-RAW-15 url_base.hpp:65 Raw buffer management
H-RAW-15b url_base.hpp:82 Duplicate of H-RAW-15: same op_t buffer move
H-RAW-16 impl/decode.hpp:35 Raw decode forwarding
H-RAW-16b impl/decode.hpp:43 Duplicate of H-RAW-16: same decode forwarding function
H-RAW-17 static_url.cpp:23 Unchecked write
H-RAW-17b static_url.cpp:28 Duplicate of H-RAW-17: same constructor, null-terminator write
H-RAW-18 url_base.cpp:115 memcpy/terminator
H-RAW-18b url_base.cpp:138 Duplicate of H-RAW-18: same copy() function
H-RAW-18c url_base.cpp:239 Dereference-before-bounds-check (duplicate of C4, ALREADY FIXED af227d4)
H-RAW-19 url.cpp:76 Allocation overflow
H-RAW-20 params_view.cpp:41 Unchecked .value()
H-RAW-20b params_view.cpp:52 Duplicate of H-RAW-20: same .value() pattern, second constructor
H-RAW-21 ipv6_address.hpp:248 Buffer bounds
H-RAW-21b ipv6_address.hpp:372 Raw char* in print_impl private function (same pattern)
H-RAW-22 ipv4_address.cpp:32 Bytes access
H-RAW-22b ipv4_address.cpp:33 Duplicate of H-RAW-22: same bytes access constructor
H-RAW-23 ipv6_address.cpp:26 memcpy
H-RAW-24 authority_view.cpp:263 memcpy
H-RAW-25 rfc/detail/host_rule.cpp:55 memcpy
H-RAW-25b rfc/detail/host_rule.cpp:98 Duplicate of H-RAW-25: same memcpy pattern for IPv4
H-RAW-26 url_view_base.cpp:91 Duplicate of H-RAW-14: same object-adjacent storage memcpy

Theme: Unchecked percent-encoding assumptions (13 findings, v1)

TRIAGE: BY DESIGN. Internal decode/encode routines operate on data already validated by the public parsing API (RFC 3986 grammar). pct_string_view validates on construction.

Finding Location Detail
H-PCT-1 decode_view.cpp:27 operator* OOB
H-PCT-1b decode_view.cpp:32 Duplicate of H-PCT-1: same operator* dereference
H-PCT-2 decode_view.cpp:53 remove_prefix past end
H-PCT-3 decode_view.cpp:69 remove_suffix past begin
H-PCT-4 decode_view.hpp:473 remove_prefix no bounds check
H-PCT-5 decode_view.hpp:496 remove_suffix no bounds check
H-PCT-6 impl/decode_view.hpp:84 Iterator under-read
H-PCT-7 pct_string_view.cpp:27 Buffer overflow
H-PCT-8 pct_string_view.hpp:435 Unsafe factory
H-PCT-8b pct_string_view.hpp:436 Duplicate of H-PCT-8: same make_pct_string_view_unsafe
H-PCT-9 rfc/impl/pct_encoded_rule.hpp:37 Overflow
H-PCT-10 detail/params_iter_impl.cpp:220 Unsafe string views
H-PCT-11 detail/normalize.cpp:266 BY DESIGN: path_ends_with reverse decoding (BOOST_ASSERT preconditions)

Theme: Race conditions in recycled<T> (6 findings, v1)

TRIAGE: FALSE POSITIVE. The report missed #if !defined(BOOST_URL_DISABLE_THREADS) guards. When threads are enabled, refs is std::atomic<std::size_t> (recycled.hpp:98-99), and the freelist is protected by std::mutex m_ (recycled.hpp:119).

Finding Location Detail
H-RACE-1 grammar/impl/recycled.hpp:33 Destructor freelist traversal (precondition: no refs)
H-RACE-2 grammar/impl/recycled.hpp:66 Uninitialized refs (init'd in U ctor under mutex)
H-RACE-3 grammar/impl/recycled.hpp:78 Non-atomic decrement (is atomic)
H-RACE-4 grammar/impl/recycled.hpp:149 Non-atomic increment (is atomic)
H-RACE-5 grammar/impl/recycled.hpp:191 Non-atomic in copy assignment (is atomic)
H-RACE-6 grammar/recycled.hpp:78 UAF risk (mutex-protected freelist)

Theme: Incorrect noexcept specifications (5 findings, v1)

TRIAGE: FALSE POSITIVE. These functions take pct_string_view, which validates percent-encoding on construction. By the time these functions are called, data is already validated.

Finding Location Function
H-NOEXCEPT-1 params_encoded_base.hpp:322 count()
H-NOEXCEPT-2 params_encoded_base.hpp:414 find()
H-NOEXCEPT-3 params_encoded_base.hpp:467 find() (iterator overload)
H-NOEXCEPT-4 params_encoded_base.hpp:514 find_last()
H-NOEXCEPT-5 params_encoded_base.hpp:562 find_last() (iterator overload)

Miscellaneous (42 findings, v1)

Finding Location Verdict Detail
H-MISC-1 grammar/ci_string.hpp:333 FIXED (bcd76d8, "fix: ci_less::operator() return type") ci_less return type (see "Confirmed bugs" above)
H-MISC-2 grammar/detail/charset.hpp:135 FALSE POSITIVE _mm_loadu_si128 is unaligned load
H-MISC-3 grammar/detail/charset.hpp:163 FALSE POSITIVE Same as H-MISC-2
H-MISC-4 grammar/impl/not_empty_rule.hpp:29 FALSE POSITIVE Result checked with if(!rv)
H-MISC-5 grammar/impl/range_rule.hpp:163 FALSE POSITIVE Standard aligned_storage pattern
H-MISC-6 grammar/recycled.hpp:440 FALSE POSITIVE bin_ set in ALL constructors
H-MISC-7 grammar/recycled.hpp:452 FIXED (f4f723e, "fix: recycled_ptr::get() nullptr when empty") get() null deref (see "Confirmed bugs" above)
H-MISC-8 grammar/recycled.hpp:482 BY DESIGN Precondition not this->empty()
H-MISC-9a grammar/string_token.hpp:254 FALSE POSITIVE s_.resize(n) before &s_[0], n==0 unused
H-MISC-9b grammar/string_token.hpp:306 FALSE POSITIVE Duplicate of H-MISC-9a
H-MISC-9c grammar/string_token.hpp:369 FALSE POSITIVE Duplicate of H-MISC-9a
H-MISC-9d grammar/string_token.hpp:436 FALSE POSITIVE Duplicate of H-MISC-9a
H-MISC-10 grammar/literal_rule.hpp:46 BY DESIGN Requires valid string literal
H-MISC-11 grammar/lut_chars.hpp:90 FALSE POSITIVE Null-terminator check handles null
H-MISC-11b grammar/lut_chars.hpp:94 FALSE POSITIVE Duplicate of H-MISC-11: same string constructor
H-MISC-12 grammar/ci_string.cpp:35 FALSE POSITIVE ci_is_equal checks sizes before detail call
H-MISC-13 grammar/ci_string.cpp:65 FP FIXED (ce4755d, "fix: ci_is_less OOB read") ci_is_less does NOT check sizes (corrected by v2-HIGH-8/9)
H-MISC-14 grammar/literal_rule.cpp:32 FALSE POSITIVE Same-allocation pointer subtraction
H-MISC-15 grammar/impl/tuple_rule.hpp:74 FALSE POSITIVE Template metaprogramming, not runtime
H-MISC-16 grammar/token_rule.hpp:36 FALSE POSITIVE Safe empty_value retrieval
H-MISC-17 grammar/tuple_rule.hpp:61 FALSE POSITIVE Checks errors with if(!rv)
H-MISC-18 impl/params_base.hpp:50 FALSE POSITIVE No pointer aliasing
H-MISC-19 impl/params_encoded_ref.hpp:31 FALSE POSITIVE Pointer only deref'd on valid instances
H-MISC-20 impl/segments_encoded_ref.hpp:143 BY DESIGN Precondition !this->empty()
H-MISC-21 impl/segments_ref.hpp:144 BY DESIGN Same as H-MISC-20
H-MISC-22 params_encoded_ref.cpp:86 FALSE POSITIVE Header-only, deref on valid instance
H-MISC-23 params_ref.cpp:70 FALSE POSITIVE Resized before access, n > 0 checked
H-MISC-24 segments_base.hpp:250 FIXED (fc51e83, "fix: incorrect noexcept in segments_base::front() and back()") noexcept front/back (see "Confirmed bugs" above)
H-MISC-25 detail/format_args.hpp:103 FALSE POSITIVE Correctly typed function pointer
H-MISC-26 detail/impl/format_args.hpp:111 FALSE POSITIVE Standard type-erasure const_cast pattern
H-MISC-27 detail/over_allocator.hpp:119 FALSE POSITIVE n==1 constrained, safe
H-MISC-28 authority_view.cpp:58 FALSE POSITIVE Constructor not noexcept, .value() throws intentionally
H-MISC-29 rfc/absolute_uri_rule.cpp:77 FALSE POSITIVE All internal ops are noexcept
H-MISC-30 rfc/detail/hier_part_rule.cpp:55 FALSE POSITIVE Bounds-checks before access
H-MISC-31 rfc/detail/ip_literal_rule.hpp:48 FALSE POSITIVE Checks it == end first
H-MISC-32 rfc/detail/port_rule.cpp:47 FALSE POSITIVE start <= it guaranteed
H-MISC-33 rfc/detail/port_rule.cpp:53 FALSE POSITIVE it != end checked first
H-MISC-34 rfc/origin_form_rule.cpp:26 FALSE POSITIVE Safe if(it == end || ...)
H-MISC-35 rfc/query_rule.cpp:52 FALSE POSITIVE Same-buffer subtraction, bounded counter
H-MISC-36 scheme.cpp:29 BY DESIGN Switch on size, case 0 handled
H-MISC-37 detail/format_args.cpp:39 FALSE POSITIVE Returns by value, not reference
H-MISC-38 params_encoded_ref.hpp:230 FALSE POSITIVE u_ always valid (private ctor, friend of url_base)

Signed integer overflow (3 findings, v1)

TRIAGE: FIXED (61c8962). Negation of LLONG_MIN is undefined behavior. Three instances, all fixed.

Finding Location Detail
H-OVERFLOW-1 detail/format_args.cpp:367 LLONG_MIN negation in string formatter
H-OVERFLOW-2 detail/format_args.cpp:453 LLONG_MIN negation in integer formatter
H-OVERFLOW-3 detail/format_args.cpp:519 LLONG_MIN negation in unsigned formatter

Scanner duplicates (15 findings, v1)

The v1 report merged two scanner runs (gpt-5.2 and claude-sonnet-4-5). These 15 findings are exact duplicates (same file and line) reported by both scanners. Each inherits the verdict of the original.

Finding Location Duplicate of
H-DUP-1 detail/encode.hpp:58 H-ASSERT-1
H-DUP-3 grammar/impl/range_rule.hpp:163 H-MISC-5
H-DUP-4 grammar/impl/recycled.hpp:78 H-RACE-3
H-DUP-5 detail/decode.cpp:25 H-RAW-10
H-DUP-6 detail/pct_format.cpp:54 H-ASSERT-14
H-DUP-7 detail/pct_format.cpp:107 H-ASSERT-15
H-DUP-8 detail/pct_format.cpp:152 H-ASSERT-16
H-DUP-9 detail/pct_format.cpp:205 H-ASSERT-17
H-DUP-10 detail/url_impl.cpp:89 H-RAW-13
H-DUP-11 grammar/ci_string.cpp:35 H-MISC-12
H-DUP-12 grammar/ci_string.cpp:65 H-MISC-13
H-DUP-13 ipv6_address.cpp:26 H-RAW-23
H-DUP-14 rfc/detail/host_rule.cpp:55 H-RAW-25
H-DUP-16 segments_ref.hpp:80 H-LIFE-11
H-DUP-17 url_view.cpp:48 H-LIFE-14
MEDIUM (491 v1 + 10 v2 = 501 findings)

v2 MEDIUM (10 findings in report, 9 unique after deduplication)

MED-1. Signed char right-shift in encode_one

  • Location: include/boost/url/detail/impl/format_args.hpp:215
  • Issue: Right-shifting a signed negative char is implementation-defined; can produce incorrect hex digit index.
  • FIXED (7bceccc, "fix: encode_one signed char right-shift"): Cast c to unsigned char before shift.

MED-2. recycled_ptr::get() null dereference

  • Location: include/boost/url/grammar/recycled.hpp:452
  • ALREADY FIXED in v1: H-MISC-7 (f4f723e, "fix: recycled_ptr::get() nullptr when empty").

MED-3. encode() noexcept but calls throwing prepare()

  • Location: include/boost/url/impl/encode.hpp:281
  • Issue: token.prepare(n) can throw std::bad_alloc, but encode() was marked noexcept, causing std::terminate.
  • FIXED (d304da8, "fix: encode() noexcept on throwing template"): Removed noexcept from both declaration and definition.

MED-4. Unbounded remove_suffix on decode_view

  • Location: src/decode_view.cpp:72
  • FALSE POSITIVE: Has BOOST_ASSERT(n <= dn_). Same precondition pattern as std::string_view::remove_suffix.

MED-5. LLONG_MIN negation UB in format

  • Location: src/detail/format_args.cpp
  • ALREADY FIXED in v1: H-OVERFLOW-1/2/3 (61c8962, "fix: LLONG_MIN negation UB in format").

MED-6. OOB read in url_base loop condition

  • Location: include/boost/url/impl/url_base.hpp
  • ALREADY FIXED in v1: C4 (af227d4, "fix: url_base loop condition order").

MED-7. url::operator=(url&&) self-move-assignment UAF

  • Location: include/boost/url/impl/url.hpp:63
  • Issue: Self-move deallocated s_ then assigned the dangling pointer back.
  • FIXED (0c22bcd, "fix: url move self-assignment"): Added if(this == &u) return *this; guard.

MED-8. recycled_ptr copy assignment self-assignment UAF

  • Location: include/boost/url/grammar/impl/recycled.hpp:187
  • Issue: Copy assignment without self-assignment guard: releases node, then re-increments ref on the released node.
  • Duplicate of HIGH finding 5115 (same bug). Tracked and fixed as HIGH-11 (1f17914, "fix: recycled_ptr copy self-assignment").

MED-9. Unbounded remove_prefix on decode_view

  • Location: src/decode_view.cpp:55
  • FALSE POSITIVE: Has BOOST_ASSERT(n <= dn_). Same as MED-4.

MED-10. params_iter_impl decrement multi-equals

  • Location: src/detail/params_iter_impl.cpp
  • ALREADY FIXED on develop before security branch (a87998a, "fix(params): correct decoded_size in params_iter_impl::decrement() for values containing '='").

v1 MEDIUM (491 findings)

No confirmed bugs among the 491 v1 MEDIUM items. All findings fall into the same patterns as the HIGH findings: by-design precondition-based APIs, non-owning view types, internal raw pointer operations, and false positives about thread safety, noexcept, and integer overflow.

Category Count Verdict
ASSERT 179 BY DESIGN
RAW_PTR 131 BY DESIGN
LIFETIME 95 BY DESIGN
NOEXCEPT 28 FALSE POSITIVE
PCT_ENCODING 24 BY DESIGN
OVERFLOW 16 BY DESIGN / FP
RACE 13 FALSE POSITIVE
OTHER_FP 5 FALSE POSITIVE
TOTAL 491
LOW (358 v1 + 6 v2 = 364 findings)

v2 LOW (6 findings)

LOW-1. ci_less::operator() return type

  • Location: include/boost/url/grammar/ci_string.hpp:333
  • ALREADY FIXED in v1: H-MISC-1 (bcd76d8, "fix: ci_less::operator() return type").

LOW-2. Pointer arithmetic UB in encode()

  • Location: include/boost/url/impl/encode.hpp
  • ALREADY FIXED in v1 (f0a80ad, "fix: encode() UB pointer arithmetic for small buffers").

LOW-3/4. front()/back() noexcept on segments

  • Location: include/boost/url/segments_base.hpp:250
  • ALREADY FIXED in v1: H-MISC-24 (fc51e83, "fix: incorrect noexcept in segments_base::front() and back()").

LOW-5. port_rule has_number wrong for port zero at end of input

  • Location: include/boost/url/rfc/detail/impl/port_rule.hpp:71
  • Issue: When port is all leading zeros at end of input (it == end), has_number was incorrectly false because it checked it != end instead of it != start.
  • FIXED (0148a7a, "fix: port_rule has_number for port zero at end of input"): Changed to t.has_number = it != start;.

LOW-6. Static destruction order fiasco in recycled bin

  • Location: include/boost/url/grammar/impl/recycled.hpp:66
  • BY DESIGN: Documented precondition. Leaked singleton pattern would change behavior.

v1 LOW (358 findings)

Category Count Verdict
CONFIRMED_BUG 2 FIXED
ASSERT 19 BY DESIGN
LIFETIME 17 BY DESIGN
RAW_PTR 81 BY DESIGN
PCT_ENCODING 5 BY DESIGN
OVERFLOW 48 BY DESIGN
NOEXCEPT 38 FALSE POSITIVE
RACE 1 FALSE POSITIVE
OTHER_FP 147 FALSE POSITIVE
Total 358

Verdict totals: FIXED 2, FALSE POSITIVE 186, BY DESIGN 170

Two confirmed bugs, both the same underlying issue: decode() with StringToken is marked noexcept but token.prepare(n) allocates and can throw std::bad_alloc, causing std::terminate. Fixed by removing noexcept from decode() (commit c935b72, "fix: decode() noexcept on throwing template").

INFORMATIONAL (169 v1 + 2 v2 = 171 findings)

v2 INFORMATIONAL (2 findings)

INFO-1. ci_equal pass-by-value

  • Location: include/boost/url/grammar/ci_string.hpp:298-299
  • Issue: operator() took String0 and String1 by value, causing unnecessary copies for non-trivial string types.
  • FIXED (70b0e49, "fix: ci_equal arguments by const reference"): Changed to String0 const& and String1 const&.

INFO-2. param self-move-assignment

  • BY DESIGN: Self-move-assignment is a programming error per C++ convention.

v1 INFORMATIONAL (169 findings)

Category Count Description
OTHER_FP 93 Style, documentation, portability nitpicks, modernization suggestions
RAW_PTR 27 Raw pointer buffer APIs in internal/parsing functions
LIFETIME 12 Non-owning view/pointer lifetime concerns
NOEXCEPT 11 noexcept on parse functions or constructors (by design, uses result<>)
ASSERT 7 BOOST_ASSERT as sole bounds check in internal functions
OVERFLOW 6 Integer overflow/narrowing in bounded arithmetic
RACE 4 Thread-safety/synchronization concerns
PCT_ENCODING 3 Unchecked percent-encoding / unsafe view construction
CONFIRMED_BUG 5 Confirmed bugs or issues worth fixing

Confirmed bugs: 3 copy-paste doc bugs in error_types.hpp (FIXED 422153c), 1 missing BOOST_URL_DECL on format_spec_rule_t::parse, 1 duplicate header include. No critical security issues among the INFORMATIONAL findings.

@cppalliance-bot
Copy link

cppalliance-bot commented Feb 28, 2026

An automated preview of the documentation is available at https://982.url.prtest2.cppalliance.org/index.html

If more commits are pushed to the pull request, the docs will rebuild at the same URL.

2026-03-02 19:45:25 UTC

@cppalliance-bot
Copy link

cppalliance-bot commented Feb 28, 2026

GCOVR code coverage report https://982.url.prtest2.cppalliance.org/gcovr/index.html
LCOV code coverage report https://982.url.prtest2.cppalliance.org/genhtml/index.html
Coverage Diff Report https://982.url.prtest2.cppalliance.org/diff-report/index.html

Build time: 2026-03-02 19:57:08 UTC

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