Add <isc/overflow.h> for checked mul, add, and sub

The `ISC_OVERFLOW_XXX()` macros are usually wrappers around
`__builtin_xxx_overflow()`, with alternative implementations
for compilers that lack the builtins.

Replace the overflow checks in `isc/time.c` with the new macros.
This commit is contained in:
Tony Finch 2023-06-06 15:11:13 +01:00 committed by Ondřej Surý
parent bf6f8abb2a
commit 7474cad4ad
No known key found for this signature in database
GPG key ID: 2820F37E873DEA41
3 changed files with 99 additions and 16 deletions

View file

@ -61,6 +61,7 @@ libisc_la_HEADERS = \
include/isc/nonce.h \
include/isc/once.h \
include/isc/os.h \
include/isc/overflow.h \
include/isc/parseint.h \
include/isc/pause.h \
include/isc/portset.h \

View file

@ -0,0 +1,95 @@
/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* SPDX-License-Identifier: MPL-2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
#pragma once
#include <isc/util.h>
/*
* It is awkward to support signed numbers as well, so keep it simple
* (with a safety check).
*/
#define ISC_OVERFLOW_IS_UNSIGNED(a) \
({ \
STATIC_ASSERT((typeof(a))-1 > 0, \
"overflow checks require unsigned types"); \
(a); \
})
#define ISC_OVERFLOW_UINT_MAX(a) ISC_OVERFLOW_IS_UNSIGNED((typeof(a))-1)
#define ISC_OVERFLOW_UINT_MIN(a) ISC_OVERFLOW_IS_UNSIGNED(0)
/*
* Return true on overflow, e.g.
*
* bool overflow = ISC_OVERFLOW_MUL(count, sizeof(array[0]), &bytes);
* INSIST(!overflow);
*/
#if HAVE_BUILTIN_MUL_OVERFLOW
#define ISC_OVERFLOW_MUL(a, b, cp) __builtin_mul_overflow(a, b, cp)
#else
#define ISC_OVERFLOW_MUL(a, b, cp) \
((ISC_OVERFLOW_UINT_MAX(a) / (b) > (a)) ? (*(cp) = (a) * (b), false) \
: true)
#endif
#if HAVE_BUILTIN_ADD_OVERFLOW
#define ISC_OVERFLOW_ADD(a, b, cp) __builtin_add_overflow(a, b, cp)
#else
#define ISC_OVERFLOW_ADD(a, b, cp) \
((ISC_OVERFLOW_UINT_MAX(a) - (b) > (a)) ? (*(cp) = (a) + (b), false) \
: true)
#endif
#if HAVE_BUILTIN_SUB_OVERFLOW
#define ISC_OVERFLOW_SUB(a, b, cp) __builtin_sub_overflow(a, b, cp)
#else
#define ISC_OVERFLOW_SUB(a, b, cp) \
((ISC_OVERFLOW_UINT_MIN(a) + (b) < (a)) ? (*(cp) = (a) - (b), false) \
: true)
#endif
#define ISC_CHECKED_MUL(a, b) \
({ \
typeof(a) _c; \
bool _overflow = ISC_OVERFLOW_MUL(a, b, &_c); \
INSIST(!_overflow); \
_c; \
})
#define ISC_CHECKED_ADD(a, b) \
({ \
typeof(a) _c; \
bool _overflow = ISC_OVERFLOW_ADD(a, b, &_c); \
INSIST(!_overflow); \
_c; \
})
#define ISC_CHECKED_SUB(a, b) \
({ \
typeof(a) _c; \
bool _overflow = ISC_OVERFLOW_SUB(a, b, cp); \
INSIST(!_overflow); \
_c; \
})
#define ISC_CHECKED_MUL_ADD(a, b, c) \
({ \
size_t _d; \
bool _overflow = ISC_OVERFLOW_MUL(a, b, &_d) || \
ISC_OVERFLOW_ADD(_d, c, &_d); \
INSIST(!_overflow); \
_d; \
})

View file

@ -23,6 +23,7 @@
#include <time.h>
#include <isc/log.h>
#include <isc/overflow.h>
#include <isc/strerr.h>
#include <isc/string.h>
#include <isc/time.h>
@ -186,16 +187,9 @@ isc_time_add(const isc_time_t *t, const isc_interval_t *i, isc_time_t *result) {
REQUIRE(t->nanoseconds < NS_PER_SEC && i->nanoseconds < NS_PER_SEC);
/* Seconds */
#if HAVE_BUILTIN_ADD_OVERFLOW
if (__builtin_add_overflow(t->seconds, i->seconds, &result->seconds)) {
if (ISC_OVERFLOW_ADD(t->seconds, i->seconds, &result->seconds)) {
return (ISC_R_RANGE);
}
#else
if (t->seconds > UINT_MAX - i->seconds) {
return (ISC_R_RANGE);
}
result->seconds = t->seconds + i->seconds;
#endif
/* Nanoseconds */
result->nanoseconds = t->nanoseconds + i->nanoseconds;
@ -217,16 +211,9 @@ isc_time_subtract(const isc_time_t *t, const isc_interval_t *i,
REQUIRE(t->nanoseconds < NS_PER_SEC && i->nanoseconds < NS_PER_SEC);
/* Seconds */
#if HAVE_BUILTIN_SUB_OVERFLOW
if (__builtin_sub_overflow(t->seconds, i->seconds, &result->seconds)) {
if (ISC_OVERFLOW_SUB(t->seconds, i->seconds, &result->seconds)) {
return (ISC_R_RANGE);
}
#else
if (t->seconds < i->seconds) {
return (ISC_R_RANGE);
}
result->seconds = t->seconds - i->seconds;
#endif
/* Nanoseconds */
if (t->nanoseconds >= i->nanoseconds) {