tech-userlevel archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: Reuse strtonum(3) and reallocarray(3) from OpenBSD



Hello,

1. I'm attaching a scratch of the overflow library (libo by Xi Wang based,
integers operations +-*/ with check for overflow), with the following API:

int overflow_add(type *r, type a, type b);
int overflow_sub(type *r, type a, type b);
int overflow_mul(type *r, type a, type b);
int overflow_div(type *r, type a, type b);

type is generic type, one of the basic integer types in C:
- char, unsigned char, signed char, 
- short, unsigned short,
- int, unsigned int,
- long, unsigned long,
- long long, unsigned long long.

the pseudo code:
*r = a OPERATION_with_overflow_check b

return value is 0 or 1:
0 - no overflow
1 - overflow

I'm attaching a test.c, to build:
gcc -lutil test.c overflow.c

Before I will fix the bugs in the lib, please tell me whether the API is OK.
Do we want to handle char and short as type? For some reason signed char,
char and unsigned char (3 distinct types) are badly handled by _Generic.
clang and gcc warns about it with enabled warnings (-Wall).

The lib is intended to be C11 compatible will fallback to GCC extensions.

2. The overflow library will be used for reallocarray(3).

3. Another example use is to replace strsuftoll(3) and strsuftollx(3) with improved
strsufto{i,u}(3). The new functions will base on strto{i,u}(3) and reuse the overflow
operators for safe multiplications (not undefined behavior based checking).

I'm attaching scratch (never compiled) of strsuftoi(3) to this mail.
Here is the description:

/*
 * Reimplement strsuftoll(3) and strsuftollx(3) as strsuftoi(3) and strsuftou(3)
 *
 *    The functions strsuftoi(3) and strsuftou(3) convert nptr into a number of
 *    type (u)intmax_t, checking that the result is not smaller than lo or
 *    larger than hi.  Two or more numbers may be separated by an
 *    ``x'' to indicate a product.
 *
 * strsufto{i,u}() is intended to base on strto{i,u}(3)
 * This reimplementation improves the API:
 * - making it locale aware,
 * - improved error handling.
 * - (u)intmax_t instead of long long,
 * - not returning English strings,
 * - not calling exit(1) for error in strsuftoll(3),
 * - not vulnerable to buffer overflows when used incorrectly,
 *
 * ERRORS
 *    [EINVAL]           The base is not between 2 and 36 and does not contain
 *                       the special value 0.
 *
 *    [ERANGE]           The given string was out of range; the value converted
 *                       has been clamped.
 *
 *    In addition to the above errors strtoi() returns:
 *
 *    [ECANCELED]        The string did not contain any characters that were
 *                       converted.
 *
 *    [ENOTSUP]          The string contained non-numeric characters that did
 *                       not get converted.  In this case, endptr points to the
 *                       first unconverted character.
 *
 *    [ERANGE]           The range given was invalid, i.e.  lo > hi.
 *
 *    In addition to the above errors strsuftoi() returns:
 *
 *    [E2BIG]            Too many products (>16)
 */


Please review the API of strsuftoi, do we want it to replace strsuftoll{,x}(3)?
Do we want to have there 'base' parameter? Do we want signed and unsigned versions?

The original usage of strsuftoll could be replaced with estrsuftoi(3), that
exits gracefully on error with a verbose message.

Regards,

Some references:
http://lteo.net/blog/2014/10/28/reallocarray-in-openbsd-integer-overflow-detection-for-free/
http://kqueue.org/blog/2012/03/16/fast-integer-overflow-detection/
http://netbsd.gw.com/cgi-bin/man-cgi?strsuftoll++NetBSD-current
/*
 * Copyright (c) 2015 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Kamil Rytarowski.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <sys/param.h>
#include <sys/types.h>
#include <sys/cdefs.h>
#include <limits.h>

#include "overflow.h"

/*
 * XXX: It seems that fine-grained extensions are only in Clang
 */
#ifndef __has_builtin
#define __has_builtin(x) 0
#endif

int overflow_add_char(char *r, char a, char b)
{
#if __has_builtin(__builtin_add_overflow) || __GNUC_PREREQ__(5,0)
	return __builtin_add_overflow(a,b,r);
#else

#if (CHAR_MIN < 0)
	if (b < 0)
		return overflow_sub_char(r, a, -b);
#endif

	if (a > CHAR_MAX - b)
		return 1;

	*r = a + b;
	return 0;
#endif
}

int overflow_add_schar(signed char *r, signed char a, signed char b)
{
#if __has_builtin(__builtin_add_overflow) || __GNUC_PREREQ__(5,0)
	return __builtin_add_overflow(a,b,r);
#else
	if (b < 0)
		return overflow_sub_schar(r, a, -b);

	if (a > SCHAR_MAX - b)
		return 1;

	*r = a + b;
	return 0;
#endif
}

int overflow_add_shrt(short *r, short a, short b)
{
#if __has_builtin(__builtin_add_overflow) || __GNUC_PREREQ__(5,0)
	return __builtin_add_overflow(a,b,r);
#else
	if (b < 0)
		return overflow_sub_shrt(r, a, -b);

	if (a > SHRT_MAX - b)
		return 1;

	*r = a + b;
	return 0;
#endif
}

int overflow_add_int(int *r, int a, int b)
{
#if __has_builtin(__builtin_add_overflow) || __GNUC_PREREQ__(5,0)
	return __builtin_add_overflow(a,b,r);
#elif __has_builtin(__builtin_sadd_overflow)
	return __builtin_sadd_overflow(a,b,r);
#else
	if (b < 0)
		return overflow_sub_int(r, a, -b);

	if (a > INT_MAX - b)
		return 1;

	*r = a + b;
	return 0;
#endif
}

int overflow_add_long(long *r, long a, long b)
{
#if __has_builtin(__builtin_add_overflow) || __GNUC_PREREQ__(5,0)
	return __builtin_add_overflow(a,b,r);
#elif __has_builtin(__builtin_saddl_overflow)
	return __builtin_saddl_overflow(a,b,r);
#else
	if (b < 0)
		return overflow_sub_long(r, a, -b);

	if (a > LONG_MAX - b)
		return 1;

	*r = a + b;
	return 0;
#endif
}

int overflow_add_llong(long long *r, long long a, long long b)
{
#if __has_builtin(__builtin_add_overflow) || __GNUC_PREREQ__(5,0)
	return __builtin_add_overflow(a,b,r);
#elif __has_builtin(__builtin_saddll_overflow)
	return __builtin_saddll_overflow(a,b,r);
#else
	if (b < 0)
		return overflow_sub_llong(r, a, -b);

	if (a > LLONG_MAX - b)
		return 1;

	*r = a + b;
	return 0;
#endif
}

int overflow_add_uchar(unsigned char *r, unsigned char a, unsigned char b)
{
#if __has_builtin(__builtin_add_overflow) || __GNUC_PREREQ__(5,0)
	return __builtin_add_overflow(a,b,r);
#else
	if (a > UCHAR_MAX - b)
		return 1;

	*r = a + b;
	return 0;
#endif
}

int overflow_add_ushrt(unsigned short *r, unsigned short a, unsigned short b)
{
#if __has_builtin(__builtin_add_overflow) || __GNUC_PREREQ__(5,0)
	return __builtin_add_overflow(a,b,r);
#else
	if (a > USHRT_MAX - b)
		return 1;

	*r = a + b;
	return 0;
#endif
}

int overflow_add_uint(unsigned int *r, unsigned int a, unsigned int b)
{
#if __has_builtin(__builtin_add_overflow) || __GNUC_PREREQ__(5,0)
	return __builtin_add_overflow(a,b,r);
#elif __has_builtin(__builtin_sadd_overflow)
	return __builtin_uadd_overflow(a,b,r);
#else
	if (a > UINT_MAX - b)
		return 1;

	*r = a + b;
	return 0;
#endif
}

int overflow_add_ulong(unsigned long *r, unsigned long a, unsigned long b)
{
#if __has_builtin(__builtin_add_overflow) || __GNUC_PREREQ__(5,0)
	return __builtin_add_overflow(a,b,r);
#elif __has_builtin(__builtin_saddl_overflow)
	return __builtin_uaddl_overflow(a,b,r);
#else
	if (a > ULONG_MAX - b)
		return 1;

	*r = a + b;
	return 0;
#endif
}

int overflow_add_ullong(unsigned long long *r, unsigned long long a, unsigned long long b)
{
#if __has_builtin(__builtin_add_overflow) || __GNUC_PREREQ__(5,0)
	return __builtin_add_overflow(a,b,r);
#elif __has_builtin(__builtin_saddll_overflow)
	return __builtin_uaddll_overflow(a,b,r);
#else
	if (a > ULLONG_MAX - b)
		return 1;

	*r = a + b;
	return 0;
#endif
}

int overflow_sub_char(char *r, char a, char b)
{
#if __has_builtin(__builtin_sub_overflow) || __GNUC_PREREQ__(5,0)
	return __builtin_sub_overflow(a,b,r);
#else

#if (CHAR_MIN < 0)
	if (b < 0)
		return overflow_add_char(r, a, -b);
#endif

	if (a < CHAR_MIN + b)
		return 1;

	*r = a - b;
	return 0;
#endif
}

int overflow_sub_schar(signed char *r, signed char a, signed char b)
{
#if __has_builtin(__builtin_sub_overflow) || __GNUC_PREREQ__(5,0)
	return __builtin_sub_overflow(a,b,r);
#else
	if (b < 0)
		return overflow_add_schar(r, a, -b);

	if (a < SCHAR_MIN + b)
		return 1;

	*r = a - b;
	return 0;
#endif
}

int overflow_sub_shrt(short *r, short a, short b)
{
#if __has_builtin(__builtin_sub_overflow) || __GNUC_PREREQ__(5,0)
	return __builtin_sub_overflow(a,b,r);
#else
	if (b < 0)
		return overflow_add_shrt(r, a, -b);

	if (a < SHRT_MIN + b)
		return 1;

	*r = a - b;
	return 0;
#endif
}

int overflow_sub_int(int *r, int a, int b)
{
#if __has_builtin(__builtin_sub_overflow) || __GNUC_PREREQ__(5,0)
	return __builtin_sub_overflow(a,b,r);
#elif __has_builtin(__builtin_ssub_overflow)
	return __builtin_ssub_overflow(a,b,r);
#else
	if (b < 0)
		return overflow_sub_int(r, a, -b);

	if (a < INT_MIN + b)
		return 1;

	*r = a - b;
	return 0;
#endif
}

int overflow_sub_long(long *r, long a, long b)
{
#if __has_builtin(__builtin_sub_overflow) || __GNUC_PREREQ__(5,0)
	return __builtin_sub_overflow(a,b,r);
#elif __has_builtin(__builtin_ssubl_overflow)
	return __builtin_ssubl_overflow(a,b,r);
#else
	if (b < 0)
		return overflow_sub_long(r, a, -b);

	if (a < LONG_MIN + b)
		return 1;

	*r = a - b;
	return 0;
#endif
}

int overflow_sub_llong(long long *r, long long a, long long b)
{
#if __has_builtin(__builtin_sub_overflow) || __GNUC_PREREQ__(5,0)
	return __builtin_sub_overflow(a,b,r);
#elif __has_builtin(__builtin_ssubll_overflow)
	return __builtin_ssubll_overflow(a,b,r);
#else
	if (b < 0)
		return overflow_sub_llong(r, a, -b);

	if (a < LLONG_MIN + b)
		return 1;

	*r = a - b;
	return 0;
#endif
}

int overflow_sub_uchar(unsigned char *r, unsigned char a, unsigned char b)
{
#if __has_builtin(__builtin_sub_overflow) || __GNUC_PREREQ__(5,0)
	return __builtin_sub_overflow(a,b,r);
#else
	if (a < b)
		return 1;

	*r = a - b;
	return 0;
#endif
}

int overflow_sub_ushrt(unsigned short *r, unsigned short a, unsigned short b)
{
#if __has_builtin(__builtin_sub_overflow) || __GNUC_PREREQ__(5,0)
	return __builtin_sub_overflow(a,b,r);
#else
	if (a < b)
		return 1;

	*r = a - b;
	return 0;
#endif
}

int overflow_sub_uint(unsigned int *r, unsigned int a, unsigned int b)
{
#if __has_builtin(__builtin_sub_overflow) || __GNUC_PREREQ__(5,0)
	return __builtin_sub_overflow(a,b,r);
#elif __has_builtin(__builtin_ssub_overflow)
	return __builtin_usub_overflow(a,b,r);
#else
	if (a < b)
		return 1;

	*r = a - b;
	return 0;
#endif
}

int overflow_sub_ulong(unsigned long *r, unsigned long a, unsigned long b)
{
#if __has_builtin(__builtin_sub_overflow) || __GNUC_PREREQ__(5,0)
	return __builtin_sub_overflow(a,b,r);
#elif __has_builtin(__builtin_ssubl_overflow)
	return __builtin_usubl_overflow(a,b,r);
#else
	if (a < b)
		return 1;

	*r = a - b;
	return 0;
#endif
}

int overflow_sub_ullong(unsigned long long *r, unsigned long long a, unsigned long long b)
{
#if __has_builtin(__builtin_sub_overflow) || __GNUC_PREREQ__(5,0)
	return __builtin_sub_overflow(a,b,r);
#elif __has_builtin(__builtin_usubll_overflow)
	return __builtin_usubll_overflow(a,b,r);
#else
	if (a < b)
		return 1;

	*r = a - b;
	return 0;
#endif
}

int overflow_mul_char(char *r, char a, char b)
{
#if __has_builtin(__builtin_mul_overflow) || __GNUC_PREREQ__(5,0)
	return __builtin_mul_overflow(a,b,r);
#else
	if (b != 0) {
#if CHAR_MIN < 0
		if ((((a > 0 && b > 0) || (a < 0 && b < 0)) && (a > CHAR_MAX / b)) ||
		    ((((a < 0 && b > 0) || (a > 0 && b < 0)) && (a < CHAR_MIN / b)))) {
			return 1;
		}
#else
		if (a > CHAR_MAX / b) {
			return 1;
		}
#endif
	}

	*r = a * b;
	return 0;
#endif
}

int overflow_mul_schar(signed char *r, signed char a, signed char b)
{
#if __has_builtin(__builtin_mul_overflow) || __GNUC_PREREQ__(5,0)
	return __builtin_mul_overflow(a,b,r);
#else
	if (b != 0 &&
	    ((((a > 0 && b > 0) || (a < 0 && b < 0)) && (a > SCHAR_MAX / b)) ||
	    ((((a < 0 && b > 0) || (a > 0 && b < 0)) && (a < SCHAR_MIN / b))))) {
		return 1;
	}

	*r = a * b;
	return 0;
#endif
}

int overflow_mul_shrt(short *r, short a, short b)
{
#if __has_builtin(__builtin_mul_overflow) || __GNUC_PREREQ__(5,0)
	return __builtin_mul_overflow(a,b,r);
#else
	if (b != 0 &&
	    ((((a > 0 && b > 0) || (a < 0 && b < 0)) && (a > SHRT_MAX / b)) ||
	    ((((a < 0 && b > 0) || (a > 0 && b < 0)) && (a < SHRT_MIN / b))))) {
		return 1;
	}

	*r = a * b;
	return 0;
#endif
}

int overflow_mul_int(int *r, int a, int b)
{
#if __has_builtin(__builtin_mul_overflow) || __GNUC_PREREQ__(5,0)
	return __builtin_mul_overflow(a,b,r);
#elif __has_builtin(__builtin_smul_overflow)
	return __builtin_smul_overflow(a,b,r);
#else
	if (b != 0 &&
	    ((((a > 0 && b > 0) || (a < 0 && b < 0)) && (a > INT_MAX / b)) ||
	    ((((a < 0 && b > 0) || (a > 0 && b < 0)) && (a < INT_MIN / b))))) {
		return 1;
	}

	*r = a * b;
	return 0;
#endif
}

int overflow_mul_long(long *r, long a, long b)
{
#if __has_builtin(__builtin_mul_overflow) || __GNUC_PREREQ__(5,0)
	return __builtin_mul_overflow(a,b,r);
#elif __has_builtin(__builtin_smull_overflow)
	return __builtin_smull_overflow(a,b,r);
#else
	if (b != 0 &&
	    ((((a > 0 && b > 0) || (a < 0 && b < 0)) && (a > LONG_MAX / b)) ||
	    ((((a < 0 && b > 0) || (a > 0 && b < 0)) && (a < LONG_MIN / b))))) {
		return 1;
	}

	*r = a * b;
	return 0;
#endif
}

int overflow_mul_llong(long long *r, long long a, long long b)
{
#if __has_builtin(__builtin_mul_overflow) || __GNUC_PREREQ__(5,0)
	return __builtin_mul_overflow(a,b,r);
#elif __has_builtin(__builtin_smulll_overflow)
	return __builtin_smulll_overflow(a,b,r);
#else
	if (b != 0 &&
	    ((((a > 0 && b > 0) || (a < 0 && b < 0)) && (a > LLONG_MAX / b)) ||
	    ((((a < 0 && b > 0) || (a > 0 && b < 0)) && (a < LLONG_MIN / b))))) {
		return 1;
	}

	*r = a * b;
	return 0;
#endif
}

int overflow_mul_uchar(unsigned char *r, unsigned char a, unsigned char b)
{
#if __has_builtin(__builtin_mul_overflow) || __GNUC_PREREQ__(5,0)
	return __builtin_mul_overflow(a,b,r);
#else
	if (b != 0 && a > UCHAR_MAX / b)
		return 1;

	*r = a * b;
	return 0;
#endif
}

int overflow_mul_ushrt(unsigned short *r, unsigned short a, unsigned short b)
{
#if __has_builtin(__builtin_mul_overflow) || __GNUC_PREREQ__(5,0)
	return __builtin_mul_overflow(a,b,r);
#else
	if (b != 0 && a > USHRT_MAX / b)
		return 1;

	*r = a * b;
	return 0;
#endif
}

int overflow_mul_uint(unsigned int *r, unsigned int a, unsigned int b)
{
#if __has_builtin(__builtin_mul_overflow) || __GNUC_PREREQ__(5,0)
	return __builtin_mul_overflow(a,b,r);
#elif __has_builtin(__builtin_umul_overflow)
	return __builtin_umul_overflow(a,b,r);
#else
	if (b != 0 && a > UINT_MAX / b)
		return 1;

	*r = a * b;
	return 0;
#endif
}

int overflow_mul_ulong(unsigned long *r, unsigned long a, unsigned long b)
{
#if __has_builtin(__builtin_mul_overflow) || __GNUC_PREREQ__(5,0)
	return __builtin_mul_overflow(a,b,r);
#elif __has_builtin(__builtin_umull_overflow)
	return __builtin_umull_overflow(a,b,r);
#else
	if (b != 0 && a > ULONG_MAX / b)
		return 1;

	*r = a * b;
	return 0;
#endif
}

int overflow_mul_ullong(unsigned long long *r, unsigned long long a, unsigned long long b)
{
#if __has_builtin(__builtin_mul_overflow) || __GNUC_PREREQ__(5,0)
	return __builtin_mul_overflow(a,b,r);
#elif __has_builtin(__builtin_umulll_overflow)
	return __builtin_umulll_overflow(a,b,r);
#else
	if (b != 0 && a > ULLONG_MAX / b)
		return 1;

	*r = a * b;
	return 0;
#endif
}

int overflow_div_char(char *r, char a, char b)
{
	if (b == 0)
		return 1;

#if (CHAR_MIN < 0) && (CHAR_MIN < (-1 * CHAR_MAX))
	if (a == CHAR_MIN && b == -1)
		return 1;
#endif

	*r = a / b;
	return 0;
}

int overflow_div_schar(signed char *r, signed char a, signed char b)
{
	if (b == 0)
		return 1;

#if (SCHAR_MIN < (-1 * SCHAR_MAX))
	if (a == SCHAR_MIN && b == -1)
		return 1;
#endif

	*r = a / b;
	return 0;
}

int overflow_div_shrt(short *r, short a, short b)
{
	if (b == 0)
		return 1;

#if (SHRT_MIN < (-1 * SHRT_MAX))
	if (a == SHRT_MIN && b == -1)
		return 1;
#endif

	*r = a / b;
	return 0;
}

int overflow_div_int(int *r, int a, int b)
{
	if (b == 0)
		return 1;

#if (INT_MIN < (-1 * INT_MAX))
	if (a == INT_MIN && b == -1)
		return 1;
#endif

	*r = a / b;
	return 0;
}

int overflow_div_long(long *r, long a, long b)
{
	if (b == 0)
		return 1;

#if (LONG_MIN < (-1 * LONG_MAX))
	if (a == LONG_MIN && b == -1)
		return 1;
#endif

	*r = a / b;
	return 0;
}

int overflow_div_llong(long long *r, long long a, long long b)
{
	if (b == 0)
		return 1;

#if (LLONG_MIN < (-1 * LLONG_MAX))
	if (a == LLONG_MIN && b == -1)
		return 1;
#endif

	*r = a / b;
	return 0;
}

int overflow_div_uchar(unsigned char *r, unsigned char a, unsigned char b)
{
	if (b == 0)
		return 1;

	*r = a / b;
	return 0;
}

int overflow_div_ushrt(unsigned short *r, unsigned short a, unsigned short b)
{
	if (b == 0)
		return 1;

	*r = a / b;
	return 0;
}

int overflow_div_uint(unsigned int *r, unsigned int a, unsigned int b)
{
	if (b == 0)
		return 1;

	*r = a / b;
	return 0;
}

int overflow_div_ulong(unsigned long *r, unsigned long a, unsigned long b)
{
	if (b == 0)
		return 1;

	*r = a / b;
	return 0;
}

int overflow_div_ullong(unsigned long long *r, unsigned long long a, unsigned long long b)
{
	if (b == 0)
		return 1;

	*r = a / b;
	return 0;
}
/*
 * Copyright (c) 2012-2015 Xi Wang. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#ifndef OVERFLOW_H
#define OVERFLOW_H

int overflow_add_char(char *r, char a, char b);
int overflow_add_schar(signed char *r, signed char a, signed char b);
int overflow_add_shrt(short *r, short a, short b);
int overflow_add_int(int *r, int a, int b);
int overflow_add_long(long *r, long a, long b);
int overflow_add_llong(long long *r, long long a, long long b);
int overflow_add_uchar(unsigned char *r, unsigned char a, unsigned char b);
int overflow_add_ushrt(unsigned short *r, unsigned short a, unsigned short b);
int overflow_add_uint(unsigned int *r, unsigned int a, unsigned int b);
int overflow_add_ulong(unsigned long *r, unsigned long a, unsigned long b);
int overflow_add_ullong(unsigned long long *r, unsigned long long a, unsigned long long b);

int overflow_sub_char(char *r, char a, char b);
int overflow_sub_schar(signed char *r, signed char a, signed char b);
int overflow_sub_shrt(short *r, short a, short b);
int overflow_sub_int(int *r, int a, int b);
int overflow_sub_long(long *r, long a, long b);
int overflow_sub_llong(long long *r, long long a, long long b);
int overflow_sub_uchar(unsigned char *r, unsigned char a, unsigned char b);
int overflow_sub_ushrt(unsigned short *r, unsigned short a, unsigned short b);
int overflow_sub_uint(unsigned int *r, unsigned int a, unsigned int b);
int overflow_sub_ulong(unsigned long *r, unsigned long a, unsigned long b);
int overflow_sub_ullong(unsigned long long *r, unsigned long long a, unsigned long long b);

int overflow_mul_char(char *r, char a, char b);
int overflow_mul_schar(signed char *r, signed char a, signed char b);
int overflow_mul_shrt(short *r, short a, short b);
int overflow_mul_int(int *r, int a, int b);
int overflow_mul_long(long *r, long a, long b);
int overflow_mul_llong(long long *r, long long a, long long b);
int overflow_mul_uchar(unsigned char *r, unsigned char a, unsigned char b);
int overflow_mul_ushrt(unsigned short *r, unsigned short a, unsigned short b);
int overflow_mul_uint(unsigned int *r, unsigned int a, unsigned int b);
int overflow_mul_ulong(unsigned long *r, unsigned long a, unsigned long b);
int overflow_mul_ullong(unsigned long long *r, unsigned long long a, unsigned long long b);

int overflow_div_char(char *r, char a, char b);
int overflow_div_schar(signed char *r, signed char a, signed char b);
int overflow_div_shrt(short *r, short a, short b);
int overflow_div_int(int *r, int a, int b);
int overflow_div_long(long *r, long a, long b);
int overflow_div_llong(long long *r, long long a, long long b);
int overflow_div_uchar(unsigned char *r, unsigned char a, unsigned char b);
int overflow_div_ushrt(unsigned short *r, unsigned short a, unsigned short b);
int overflow_div_uint(unsigned int *r, unsigned int a, unsigned int b);
int overflow_div_ulong(unsigned long *r, unsigned long a, unsigned long b);
int overflow_div_ullong(unsigned long long *r, unsigned long long a, unsigned long long b);

#if (__STDC_VERSION__ + 0) >= 201112L

#define overflow_add(r, a, b) _Generic((r),				\
	char *: overflow_add_char,					\
	signed char*: overflow_add_schar,				\
	short*: overflow_add_short,					\
	int*: overflow_add_int,						\
	long*: overflow_add_long,					\
	long long*: overflow_add_llong,					\
	unsigned char*: overflow_add_uchar,				\
	unsigned short*: overflow_add_ushort,				\
	unsigned int*: overflow_add_uint,				\
	unsigned long*: overflow_add_ulong,				\
	unsigned long long*: overflow_add_ullong)) ((r), (a), (b))

#define overflow_sub(r, a, b) _Generic((r),				\
	char *: overflow_sub_char,					\
	signed char*: overflow_sub_schar,				\
	short*: overflow_sub_short,					\
	int*: overflow_sub_int,						\
	long*: overflow_sub_long,					\
	long long*: overflow_sub_llong,					\
	unsigned char*: overflow_sub_uchar,				\
	unsigned short*: overflow_sub_ushort,				\
	unsigned int*: overflow_sub_uint,				\
	unsigned long*: overflow_sub_ulong,				\
	unsigned long long*: overflow_sub_ullong)) ((r), (a), (b))

#define overflow_mul(r, a, b) _Generic((r),				\
	char *: overflow_mul_char,					\
	signed char*: overflow_mul_schar,				\
	short*: overflow_mul_short,					\
	int*: overflow_mul_int,						\
	long*: overflow_mul_long,					\
	long long*: overflow_mul_llong,					\
	unsigned char*: overflow_mul_uchar,				\
	unsigned short*: overflow_mul_ushort,				\
	unsigned int*: overflow_mul_uint,				\
	unsigned long*: overflow_mul_ulong,				\
	unsigned long long*: overflow_mul_ullong)) ((r), (a), (b))

#define overflow_div(r, a, b) _Generic((r),				\
	char *: overflow_div_char,					\
	signed char*: overflow_div_schar,				\
	short*: overflow_div_short,					\
	int*: overflow_div_int,						\
	long*: overflow_div_long,					\
	long long*: overflow_div_llong,					\
	unsigned char*: overflow_div_uchar,				\
	unsigned short*: overflow_div_ushort,				\
	unsigned int*: overflow_div_uint,				\
	unsigned long*: overflow_div_ulong,				\
	unsigned long long*: overflow_div_ullong)) ((r), (a), (b))

#else

/* We can get rid of this ugly macro once GCC supports C11 _Generic
 * or Clang's __attribute__((overloadable)).  __builtin_choose_expr
 * doesn't work well with -Wconversion.
 */
#define OVERFLOW_GENERIC(op, r, a, b)					\
	__builtin_choose_expr(						\
	__builtin_types_compatible_p(typeof(*(r)), char),		\
	overflow_##op##_char((signed char *)r, a, b),			\
	__builtin_choose_expr(						\
	__builtin_types_compatible_p(typeof(*(r)), signed char),	\
	overflow_##op##_schar((signed char *)r, a, b),			\
	__builtin_choose_expr(						\
	__builtin_types_compatible_p(typeof(*(r)), short),		\
	overflow_##op##_shrt((short *)r, a, b),			\
	__builtin_choose_expr(						\
	__builtin_types_compatible_p(typeof(*(r)), int),		\
	overflow_##op##_int((int *)r, a, b),				\
	__builtin_choose_expr(						\
	__builtin_types_compatible_p(typeof(*(r)), long),		\
	overflow_##op##_long((long *)r, a, b),				\
	__builtin_choose_expr(						\
	__builtin_types_compatible_p(typeof(*(r)), long long),		\
	overflow_##op##_llong((long long *)r, a, b),			\
	__builtin_choose_expr(						\
	__builtin_types_compatible_p(typeof(*(r)), unsigned char),	\
	overflow_##op##_uchar((unsigned char *)r, a, b),		\
	__builtin_choose_expr(						\
	__builtin_types_compatible_p(typeof(*(r)), unsigned short),	\
	overflow_##op##_ushrt((unsigned short *)r, a, b),		\
	__builtin_choose_expr(						\
	__builtin_types_compatible_p(typeof(*(r)), unsigned int),	\
	overflow_##op##_uint((unsigned int *)r, a, b),			\
	__builtin_choose_expr(						\
	__builtin_types_compatible_p(typeof(*(r)), unsigned long),	\
	overflow_##op##_ulong((unsigned long *)r, a, b),		\
	__builtin_choose_expr(						\
	__builtin_types_compatible_p(typeof(*(r)), unsigned long long),	\
	overflow_##op##_ullong((unsigned long long *)r, a, b),		\
	(void)0)))))))))))

#define overflow_add(r, a, b)	OVERFLOW_GENERIC(add, (r), (a), (b))
#define overflow_sub(r, a, b)	OVERFLOW_GENERIC(sub, (r), (a), (b))
#define overflow_mul(r, a, b)	OVERFLOW_GENERIC(mul, (r), (a), (b))
#define overflow_div(r, a, b)	OVERFLOW_GENERIC(div, (r), (a), (b))

#endif

#endif	/* OVERFLOW_H */
/* in <stdlib.h> */

_Static_assert(INTMAX_MAX > 1152921504606846976LL); /* STRSUFTO_SIZE_EIB */

#define STRSUFTO_SIZE_BLOCK 512
#define STRSUFTO_SIZE_KIB ((intmax_t)1024)
#define STRSUFTO_SIZE_MIB ((intmax_t)1024 * STRSUFTO_SIZE_KIB)
#define STRSUFTO_SIZE_GIB ((intmax_t)1024 * STRSUFTO_SIZE_MIB)
#define STRSUFTO_SIZE_TIB ((intmax_t)1024 * STRSUFTO_SIZE_GIB)
#define STRSUFTO_SIZE_PIB ((intmax_t)1024 * STRSUFTO_SIZE_TIB)
#define STRSUFTO_SIZE_EIB ((intmax_t)1024 * STRSUFTO_SIZE_PIB)
#define STRSUFTO_SIZE_WORD sizeof(int)
#define STRSUFTO_RECURSION_LIMIT 16

/*
 * Convert an expression of the following forms to a (u)int64_t.
 * 	1) A positive decimal number.
 *	2) A positive decimal number followed by a b (mult by 512).
 *	3) A positive decimal number followed by a k (mult by 1024).
 *	4) A positive decimal number followed by a m (mult by 1048576).
 *	5) A positive decimal number followed by a g (mult by 1073741824).
 *	6) A positive decimal number followed by a t (mult by 1099511627776).
 *	7) A positive decimal number followed by a w (mult by sizeof int)
 *	8) Two or more positive decimal numbers (with/without k,b or w).
 *	   separated by x (also * for backwards compatibility), specifying
 *	   the product of the indicated values.
 * Returns the result upon successful conversion, or exits with an
 * appropriate error.
 * 
 */

/*
 * Reimplement strsuftoll(3) and strsuftollx(3) as strsuftoi(3) and strsuftou(3)
 *
 *    The functions strsuftoi(3) and strsuftou(3) convert nptr into a number of
 *    type (u)intmax_t, checking that the result is not smaller than lo or
 *    larger than hi.  Two or more numbers may be separated by an
 *    ``x'' to indicate a product.
 *
 * strsufto{i,u}() is intended to base on strto{i,u}(3)
 * This reimplementation improves the API:
 * - making it locale aware,
 * - improved error handling.
 * - (u)intmax_t instead of long long,
 * - not returning English strings,
 * - not calling exit(1) for error in strsuftoll(3),
 * - not vulnerable to buffer overflows when used incorrectly,
 *
 * ERRORS
 *    [EINVAL]           The base is not between 2 and 36 and does not contain
 *                       the special value 0.
 *
 *    [ERANGE]           The given string was out of range; the value converted
 *                       has been clamped.
 *
 *    In addition to the above errors strtoi() returns:
 *
 *    [ECANCELED]        The string did not contain any characters that were
 *                       converted.
 *
 *    [ENOTSUP]          The string contained non-numeric characters that did
 *                       not get converted.  In this case, endptr points to the
 *                       first unconverted character.
 *
 *    [ERANGE]           The range given was invalid, i.e.  lo > hi.
 *
 *    In addition to the above errors strsuftoi() returns:
 *
 *    [E2BIG]            Too many products (>16)
 */

intmax_t
strsuftoi(const char * __restrict nptr, char ** __restrict endptr, int base,
          intmax_t lo, intmax_t hi, int * rstatus /* locale_t */)
{
	intmax_t im = 1;
	intmax_t rv;
	int e;
	char *ep;
	int rep;
	int depth = 0;

	if (endptr == NULL)
		endptr = &ep;

	if (rstatus == NULL)
		rstatus = &rep;

	do {
		rv = strtoi(nptr, endptr, base, INTMAX_MIN, INTMAX_MAX, &e);
		im *= rv;
		*rstatus = e;
		if (e == 0 || e != ENOTSUP)
			break;

		switch (*endptr) {
		case 'b':
			im *= STRSUFTO_SIZE_BLOCK;	/* 1 block */
			++endptr;
			break;
		case 'k':
			im *= STRSUFTO_SIZE_KIB;
			++endptr;
			break;
		case 'm':
			im *= STRSUFTO_SIZE_MIB;
			++endptr;
			break;
		case 'g':
			im *= STRSUFTO_SIZE_GIB;
			++endptr;
			break;
		case 't':
			im *= STRSUFTO_SIZE_TIB;
			++endptr;
			break;
		case 'p':
			im *= STRSUFTO_SIZE_PIB;
			++endptr;
			break;
		case 'e':
			im *= STRSUFTO_SIZE_EIB;
			++endptr;
			break;
		case 'w':
			im *= sizeof(int);
			++endptr;
			break;
		}

		if (*endptr != 'x' &&
		    *endptr != '*' /* Backward compatibility */)
			break;

		if (++depth > STRSUFTO_RECURSION_LIMIT) {
			*rstatus = E2BIG;
			break;
		}
	} while (1);

	if (*rstatus == ECANCELED && depth > 0)
		*rstatus = ENOTSUP;

	if (im < lo) {
		if (*rstatus == 0)
			*rstatus = ERANGE;
		return lo;
	}

	if (im > hi) {
		if (*rstatus == 0)
			*rstatus = ERANGE;
		return hi;
	}

	return rv;
}
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <err.h>
#include <stdint.h>
#include <inttypes.h>
#include <limits.h>
#include <util.h>

#include "overflow.h"

#define FUN(name,type,min,max, wrapped, print) \
void test_##name(char *a, char *b) \
{ \
	type v1 = wrapped(a, 0, min, max); \
	type v2 = wrapped(b, 0, min, max); \
\
	type res; \
	bool o; \
\
	o = overflow_add(&res, v1, v2); \
	printf("[%s] %" #print " + %" #print " = %" #print " [status %s]\n", #name , v1, v2, res, o ? "overflow" : "ok"); \
	o = overflow_sub(&res, v1, v2); \
	printf("[%s] %" #print " - %" #print " = %" #print " [status %s]\n", #name , v1, v2, res, o ? "overflow" : "ok"); \
	o = overflow_mul(&res, v1, v2); \
	printf("[%s] %" #print " * %" #print " = %" #print " [status %s]\n", #name , v1, v2, res, o ? "overflow" : "ok"); \
	o = overflow_div(&res, v1, v2); \
	printf("[%s] %" #print " / %" #print " = %" #print " [status %s]\n", #name , v1, v2, res, o ? "overflow" : "ok"); \
}

FUN(char,char,CHAR_MIN,CHAR_MAX,estrtoi, hhu)
FUN(schar,signed char,SCHAR_MIN,SCHAR_MAX,estrtoi, hhd)
FUN(uchar,unsigned char,0,UCHAR_MAX,estrtou, hhu)
FUN(shrt,short,0,SHRT_MAX,estrtoi, hd)
FUN(ushrt,unsigned short,0,USHRT_MAX,estrtou, hu)
FUN(int,int,INT_MIN,INT_MAX,estrtoi, d)
FUN(uint,unsigned int,0,UINT_MAX,estrtou, u)
FUN(long,long,LONG_MIN,LONG_MAX,estrtoi, ld)
FUN(ulong,unsigned long,0,ULONG_MAX,estrtou, lu)
FUN(llong,long long,LLONG_MIN,LLONG_MAX,estrtoi, lld)
FUN(ullong,unsigned long long,0,ULLONG_MAX,estrtou, llu)

#define IF_CHECK(name) \
if (strcmp(argv[1], #name ) == 0) { \
	test_##name (argv[2], argv[3]); \
}

int main(int argc, char **argv)
{
	if (argc < 4)
		errx(EXIT_FAILURE, "usage: ./prog TYPE NUM1 NUM2");

	IF_CHECK(char)
	else IF_CHECK(schar)
	else IF_CHECK(uchar)
	else IF_CHECK(shrt)
	else IF_CHECK(ushrt)
	else IF_CHECK(int)
	else IF_CHECK(uint)
	else IF_CHECK(long)
	else IF_CHECK(ulong)
	else IF_CHECK(llong)
	else IF_CHECK(ullong)
	else errx(EXIT_FAILURE, "usage: ./prog TYPE NUM1 NUM2");

	return 0;
}


Home | Main Index | Thread Index | Old Index