Hi,

I noticed that some parts of OpenBSD use awkward techniques to detect
undefined behavior in arithmetic operations, for example:

> static size_t
> poolBytesToAllocateFor(int blockSize) {
>   /* Unprotected math would be:
>   ** return offsetof(BLOCK, s) + blockSize * sizeof(XML_Char);
>   **
>   ** Detect overflow, avoiding _signed_ overflow undefined behavior
>   ** For a + b * c we check b * c in isolation first, so that addition of
a
>   ** on top has no chance of making us accept a small non-negative number
>   */
>   const size_t stretch = sizeof(XML_Char); /* can be 4 bytes */
>
>   if (blockSize <= 0)
>     return 0;
>
>   if (blockSize > (int)(INT_MAX / stretch))
>     return 0;
>
>   {
>     const int stretchedBlockSize = blockSize * (int)stretch;
>     const int bytesToAllocate
>         = (int)(offsetof(BLOCK, s) + (unsigned)stretchedBlockSize);
>     if (bytesToAllocate < 0)
>       return 0;
>
>     return (size_t)bytesToAllocate;
>   }
> }

The snippet was taken from lib/libexpat/lib/xmlparse.c

This does not fully protect the code because bytesToAllocate might
overflow in the unsigned addition resulting in bytesToAllocate > 0. Note
that unsigned overflow is not undefined behavior but leads to unexpected
results.

C23 solves this problem using <stdckdint.h> [1] which transforms the
above function into:

> static size_t
> poolBytesToAllocateFor(int blockSize) {
>   size_t add, mul;
>   if (ckd_mul(&mul, blockSize, sizeof(XML_Char)))
>     return 0;
>   if (ckd_add(&add, mul, offsetof(BLOCK, s)))
>     return 0;
>   return add;
> }

Is it worth bringing this functionality in OpenBSD? As a starting point
I was thinking that we can make use of __builtin_add_overflow et al.
when compiling with Clang. GCC is trickier to solve as the builtins were
implemented from version 5.

Best Regards,
Lucian Popescu

[1] https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2681.pdf

Reply via email to