diff --git a/doc/modules/ROOT/nav.adoc b/doc/modules/ROOT/nav.adoc index 82bda900..f799144d 100644 --- a/doc/modules/ROOT/nav.adoc +++ b/doc/modules/ROOT/nav.adoc @@ -9,6 +9,7 @@ ** xref:examples.adoc#examples_checked[Checked Arithmetic] ** xref:examples.adoc#examples_strict[Strict Arithmetic] ** xref:examples.adoc#examples_generic[Generic Policy-Parameterized Arithmetic] +** xref:examples.adoc#examples_safety_profile[Integer Safety Profile] ** xref:examples.adoc#examples_literals[Literals] ** xref:examples.adoc#examples_charconv[Character Conversion] ** xref:examples.adoc#examples_fmt_format[Formatting] diff --git a/doc/modules/ROOT/pages/examples.adoc b/doc/modules/ROOT/pages/examples.adoc index fa686417..17cf9b12 100644 --- a/doc/modules/ROOT/pages/examples.adoc +++ b/doc/modules/ROOT/pages/examples.adoc @@ -249,6 +249,37 @@ add(max, 1) = 4294967295 ---- ==== +[#examples_safety_profile] +== Integer Safety Profile + +This example demonstrates how Boost.SafeNumbers can emulate an integer safety profile, analogous to the memory safety profiles being proposed for C++. +Under such a profile, a conforming implementation could transparently replace the fundamental integer types with checked equivalents, so that overflow and underflow become diagnosable errors instead of silent wrap-around (for unsigned types) or undefined behavior (for signed types). + +The canonical substitution would look like `#define std::uint32_t boost::safe_numbers::u32`. +This is not legal C++ -- macro names cannot be qualified identifiers -- but it illustrates what a compiler driving the profile could do automatically when `BOOST_SAFE_NUMBERS_INTEGER_SAFETY_PROFILE` is set. +The example approximates the same effect with a type alias so it compiles either way. + +.This https://github.com/boostorg/safe_numbers/blob/develop/examples/safety_profile.cpp[example] demonstrates emulating an integer safety profile to turn a silent unsigned-overflow allocation bug into a diagnosable error. +==== +[source, c++] +---- +include::example$safety_profile.cpp[] +---- + +Output without `BOOST_SAFE_NUMBERS_INTEGER_SAFETY_PROFILE` (silent wrap-around): +---- +element_count = 2147483657 +element_size = 4 +total_bytes = 36 +Manual post-hoc overflow check fired +---- + +Output with `BOOST_SAFE_NUMBERS_INTEGER_SAFETY_PROFILE` defined (overflow diagnosed at its source): +---- +Safety profile caught overflow: Overflow detected in u32 multiplication +---- +==== + [#examples_literals] == Literals diff --git a/examples/safety_profile.cpp b/examples/safety_profile.cpp new file mode 100644 index 00000000..04bd6d27 --- /dev/null +++ b/examples/safety_profile.cpp @@ -0,0 +1,79 @@ +// Copyright 2026 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt +// +// This example demonstrates how Boost.SafeNumbers can emulate an integer +// safety profile, analogous to the memory safety profiles being proposed +// for C++. +// Under such a profile, a conforming implementation could +// transparently replace the fundamental integer types with checked +// equivalents, so that overflow and underflow become diagnosable errors +// instead of silent wrap-around (which can mask real bugs) or undefined +// behavior (for signed types). +// +// The canonical substitution would look like: +// +// #define std::uint32_t boost::safe_numbers::u32 +// +// This is not legal C++ since macro names cannot be qualified identifiers, +// but it illustrates what a compiler driving the profile could do +// automatically when BOOST_SAFE_NUMBERS_INTEGER_SAFETY_PROFILE is set. +// In the meantime, the same effect can be opted into locally with a type +// alias, which is what we do below so the example compiles either way. + +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable: 4127) // Conditional expression is constant +#endif + +#ifdef BOOST_SAFE_NUMBERS_INTEGER_SAFETY_PROFILE +using u32 = boost::safe_numbers::u32; +#else +using u32 = std::uint32_t; +#endif + +int main() +{ + try + { + // A classic allocation-size bug: the caller supplies an element + // count and element size, and we compute the total number of + // bytes to allocate. For large inputs the multiplication + // overflows a 32-bit unsigned value and wraps to a small number, + // which would then silently underallocate. + // The subsequent writes then overflow the buffer. + const u32 element_count {std::numeric_limits::max() / 2U + 10U}; + const u32 element_size {4U}; + const u32 total_bytes {element_count * element_size}; + + std::cout << "element_count = " << element_count << '\n'; + std::cout << "element_size = " << element_size << '\n'; + std::cout << "total_bytes = " << total_bytes << '\n'; + + if (total_bytes < element_count) + { + std::cout << "Manual post-hoc overflow check fired\n"; + } + else + { + std::cout << "No overflow detected -- but the value is wrong\n"; + } + } + catch (const std::exception& e) + { + // With BOOST_SAFE_NUMBERS_INTEGER_SAFETY_PROFILE defined, the + // multiplication above throws before total_bytes can be used, + // so the bug is surfaced at its source rather than at the + // eventual out-of-bounds write. + std::cerr << "Safety profile caught overflow: " << e.what() << '\n'; + } + + return 0; +} diff --git a/test/Jamfile b/test/Jamfile index 70f32e77..1bcb7b4b 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -233,3 +233,5 @@ run ../examples/std_numeric_usage.cpp ; run ../examples/random.cpp ; run ../examples/basic_bounded_usage.cpp ; compile-fail ../examples/compile_fail_basic_bounded_usage_constexpr.cpp ; +run ../examples/safety_profile.cpp : : : : safety_profile_off ; +run ../examples/safety_profile.cpp : : : BOOST_SAFE_NUMBERS_INTEGER_SAFETY_PROFILE : safety_profile_on ;