Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ jobs:
clang-cl *
subrange-policy: |
msvc: one-per-minor
containers: |
# ubuntu:25.04 (plucky), the v1.9.3 default for GCC 16, is EOL
# and never gained gcc-16 in apt or toolchain-r/ppa. GCC 16 ships
# natively on ubuntu:26.04 so target that instead.
gcc 16: ubuntu:26.04
standards: '>=11'
latest-factors: |
gcc ASan
Expand Down
3 changes: 2 additions & 1 deletion include/boost/url/impl/decode_view.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,9 @@ decoded_strcmp(decode_view s0, T s1)
auto n = (std::min)(n0, n1);
auto it0 = s0.begin();
auto it1 = s1.begin();
while (n--)
while (n != 0)
{
--n;
const char c0 = *it0++;
const char c1 = *it1++;
if (c0 == c1)
Expand Down
6 changes: 6 additions & 0 deletions include/boost/url/rfc/impl/ipv6_address_rule.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,12 @@ parse(
BOOST_URL_CONSTEXPR_RETURN_EC(
grammar::error::invalid);
}
if(! c)
{
// missing h16 before "."
BOOST_URL_CONSTEXPR_RETURN_EC(
grammar::error::invalid);
}
if(! detail::maybe_octet(
&bytes[2*(7-n)]))
{
Expand Down
12 changes: 8 additions & 4 deletions src/detail/format_args.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -370,9 +370,11 @@ measure(
dn += measure_one(sign, cs);
++n;
}
// Use unsigned to avoid UB when v == LLONG_MIN
// Use bitwise two's-complement negation to obtain |v| without
// tripping signed-overflow (v == LLONG_MIN) or
// unsigned-overflow (0ull - x) sanitizers.
unsigned long long int uv = v < 0
? 0ull - static_cast<unsigned long long int>(v)
? ~static_cast<unsigned long long int>(v) + 1ull
: static_cast<unsigned long long int>(v);
do
{
Expand Down Expand Up @@ -448,10 +450,12 @@ format(
grammar::lut_chars const& cs) const
{
// get n digits
// Use unsigned to avoid UB when v == LLONG_MIN
// Bitwise two's-complement negation to obtain |v| without
// tripping signed-overflow (v == LLONG_MIN) or
// unsigned-overflow (0ull - x) sanitizers.
bool const neg = v < 0;
unsigned long long int uv = neg
? 0ull - static_cast<unsigned long long int>(v)
? ~static_cast<unsigned long long int>(v) + 1ull
: static_cast<unsigned long long int>(v);
unsigned long long int uv0 = uv;
unsigned long long int p = 1;
Expand Down
34 changes: 28 additions & 6 deletions src/grammar/ci_string.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,22 @@
#include <boost/url/detail/config.hpp>
#include <boost/url/grammar/ci_string.hpp>

// FNV-1a (in ci_digest below) relies on modular multiplication
// of unsigned values, which Clang's -fsanitize=integer flags as
// overflow. Suppress the check for that function only. GCC has
// no equivalent sanitizer (it does not flag unsigned overflow),
// so the annotation is Clang-only — applying it on GCC produces
// "attribute directive ignored" under -Werror=attributes.
#if defined(__clang__) && defined(__has_attribute)
# if __has_attribute(no_sanitize)
# define BOOST_URL_NO_SANITIZE_INT_OVERFLOW \
__attribute__((no_sanitize("unsigned-integer-overflow")))
# endif
#endif
#ifndef BOOST_URL_NO_SANITIZE_INT_OVERFLOW
# define BOOST_URL_NO_SANITIZE_INT_OVERFLOW
#endif

namespace boost {
namespace urls {
namespace grammar {
Expand All @@ -32,24 +48,27 @@ ci_is_equal(
auto p2 = s1.data();
char a, b;
// fast loop
while(n--)
while(n != 0)
{
--n;
a = *p1++;
b = *p2++;
if(a != b)
goto slow;
}
return true;
do
for(;;)
{
a = *p1++;
b = *p2++;
slow:
if( to_lower(a) !=
to_lower(b))
return false;
if(n == 0)
break;
--n;
}
while(n--);
return true;
}

Expand All @@ -64,8 +83,9 @@ ci_is_less(
auto p2 = s1.data();
auto n = s0.size() < s1.size()
? s0.size() : s1.size();
while(n--)
while(n != 0)
{
--n;
auto c1 = to_lower(*p1++);
auto c2 = to_lower(*p2++);
if(c1 != c2)
Expand Down Expand Up @@ -102,8 +122,9 @@ ci_compare(
}
auto it0 = s0.data();
auto it1 = s1.data();
while(n--)
while(n != 0)
{
--n;
auto c0 =
to_lower(*it0++);
auto c1 =
Expand All @@ -119,6 +140,7 @@ ci_compare(

//------------------------------------------------

BOOST_URL_NO_SANITIZE_INT_OVERFLOW
std::size_t
ci_digest(
core::string_view s) noexcept
Expand All @@ -138,7 +160,7 @@ ci_digest(
auto hash = hash0;
auto p = s.data();
auto n = s.size();
for(;n--;++p)
for(; n != 0; --n, ++p)
{
// VFALCO NOTE Consider using a lossy
// to_lower which works 4 or 8 chars at a time.
Expand Down
5 changes: 5 additions & 0 deletions test/unit/ipv6_address.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,11 @@ class ipv6_address_test
bad("0:0:0:0:0:0:0:1.2.3.4");
bad("0:0:0:0:0:0:0::1.2.3.4");

// missing h16 before "." (issue #993)
bad("::.");
bad("1::.");
bad("1:2::.");
bad(".");
bad("::1.");
bad("::1.2");
bad("::1.2");
Expand Down
6 changes: 6 additions & 0 deletions test/unit/url_view.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1231,6 +1231,12 @@ class url_view_test
}
}

// Malformed IPv6 host must not read uninitialized memory (issue #993)
{
BOOST_URL_CXX20_CONSTEXPR auto r = parse_uri("https://[::.");
BOOST_TEST(! r.has_value());
}

// Detailed test
{
BOOST_URL_CXX20_CONSTEXPR auto r = parse_uri_reference("http://example.com:8080/path?query=1#frag");
Expand Down
Loading