git: c3ae84bc2a57 - stable/14 - include: Implement N2867.

From: Dag-Erling Smørgrav <des_at_FreeBSD.org>
Date: Thu, 07 Sep 2023 20:28:57 UTC
The branch stable/14 has been updated by des:

URL: https://cgit.FreeBSD.org/src/commit/?id=c3ae84bc2a57e380c6703d7cbbfb6b541bde6391

commit c3ae84bc2a57e380c6703d7cbbfb6b541bde6391
Author:     Dag-Erling Smørgrav <des@FreeBSD.org>
AuthorDate: 2023-09-07 06:14:54 +0000
Commit:     Dag-Erling Smørgrav <des@FreeBSD.org>
CommitDate: 2023-09-07 20:28:31 +0000

    include: Implement N2867.
    
    This adds macros for checked addition, subtraction, and multiplication with semantics similar to the builtins gcc and clang have had for years.
    
    Reviewed by:    kib, emaste
    Differential Revision:  https://reviews.freebsd.org/D41734
    
    (cherry picked from commit e6615b10347caf67f5bc12c9a8e30b8ddd9860ae)
    
    include: Add tests for N2867.
    
    Reviewed by:    imp
    Differential Revision:  https://reviews.freebsd.org/D41735
    
    (cherry picked from commit 4fbb9c43aa44d9145151bb5f77d302ba01fb7551)
    
    less: We have <stdckdint.h> now.
    
    Reviewed by:    delphij
    Differential Revision:  https://reviews.freebsd.org/D41736
    
    (cherry picked from commit cb8dd292c7ec53391dfa25847858dd7ef895f94e)
    
    Approved by:    re (gjb)
---
 etc/mtree/BSD.tests.dist       |   2 +
 include/Makefile               |   2 +-
 include/stdckdint.h            |  40 ++++++++++++++++
 share/man/man3/Makefile        |   4 ++
 share/man/man3/stdckdint.3     | 106 +++++++++++++++++++++++++++++++++++++++++
 tests/Makefile                 |   1 +
 tests/include/Makefile         |   7 +++
 tests/include/stdckdint_test.c |  52 ++++++++++++++++++++
 usr.bin/less/defines.h         |   2 +-
 9 files changed, 214 insertions(+), 2 deletions(-)

diff --git a/etc/mtree/BSD.tests.dist b/etc/mtree/BSD.tests.dist
index fb755b4001ac..04dc24d772c8 100644
--- a/etc/mtree/BSD.tests.dist
+++ b/etc/mtree/BSD.tests.dist
@@ -278,6 +278,8 @@
             ..
         ..
     ..
+    include
+    ..
     lib
         atf
             libatf-c
diff --git a/include/Makefile b/include/Makefile
index f3c9230f6d40..d8cdbfed3663 100644
--- a/include/Makefile
+++ b/include/Makefile
@@ -30,7 +30,7 @@ INCS=	a.out.h ar.h assert.h bitstring.h byteswap.h \
 	pthread_np.h pwd.h ranlib.h readpassphrase.h regex.h \
 	res_update.h resolv.h runetype.h sched.h \
 	search.h semaphore.h setjmp.h \
-	signal.h spawn.h stab.h stdalign.h stdbool.h stddef.h \
+	signal.h spawn.h stab.h stdalign.h stdbool.h stdckdint.h stddef.h \
 	stdnoreturn.h stdio.h stdlib.h string.h stringlist.h \
 	strings.h sysexits.h tar.h termios.h tgmath.h \
 	time.h timeconv.h timers.h ttyent.h \
diff --git a/include/stdckdint.h b/include/stdckdint.h
new file mode 100644
index 000000000000..af3074dded89
--- /dev/null
+++ b/include/stdckdint.h
@@ -0,0 +1,40 @@
+/*-
+ * Copyright (c) 2023 Dag-Erling Smørgrav
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#ifndef __STDC_VERSION_STDCKDINT_H__
+#define __STDC_VERSION_STDCKDINT_H__ 202311L
+
+#include <sys/cdefs.h>
+
+#if __BSD_VISIBLE || __ISO_C_VISIBLE >= 2023
+
+#if __GNUC_PREREQ__(5, 1) || __has_builtin(__builtin_add_overflow)
+#define ckd_add(result, a, b)						\
+	(_Bool)__builtin_add_overflow((a), (b), (result))
+#else
+#define ckd_add(result, a, b)						\
+	_Static_assert(0, "checked addition not supported")
+#endif
+
+#if __GNUC_PREREQ__(5, 1) || __has_builtin(__builtin_sub_overflow)
+#define ckd_sub(result, a, b)						\
+	(_Bool)__builtin_sub_overflow((a), (b), (result))
+#else
+#define ckd_sub(result, a, b)						\
+	_Static_assert(0, "checked subtraction not supported")
+#endif
+
+#if __GNUC_PREREQ__(5, 1) || __has_builtin(__builtin_mul_overflow)
+#define ckd_mul(result, a, b)						\
+	(_Bool)__builtin_mul_overflow((a), (b), (result))
+#else
+#define ckd_mul(result, a, b)						\
+	_Static_assert(0, "checked multiplication not supported")
+#endif
+
+#endif
+
+#endif
diff --git a/share/man/man3/Makefile b/share/man/man3/Makefile
index 5aec58492b8f..7fff5eedd5fb 100644
--- a/share/man/man3/Makefile
+++ b/share/man/man3/Makefile
@@ -29,6 +29,7 @@ MAN=		arb.3 \
 		snl.3 \
 		stats.3 \
 		stdarg.3 \
+		stdckdint.3 \
 		sysexits.3 \
 		tgmath.3 \
 		timeradd.3 \
@@ -310,6 +311,9 @@ MLINKS+=	stdarg.3 va_arg.3 \
 		stdarg.3 va_end.3 \
 		stdarg.3 varargs.3 \
 		stdarg.3 va_start.3
+MLINKS+=	stdckdint.3 ckd_add.3 \
+		stdckdint.3 ckd_sub.3 \
+		stdckdint.3 ckd_mul.3
 MLINKS+=	timeradd.3 timerclear.3 \
 		timeradd.3 timercmp.3 \
 		timeradd.3 timerisset.3 \
diff --git a/share/man/man3/stdckdint.3 b/share/man/man3/stdckdint.3
new file mode 100644
index 000000000000..e3593472c08b
--- /dev/null
+++ b/share/man/man3/stdckdint.3
@@ -0,0 +1,106 @@
+.\"-
+.\" Copyright (c) 2023 Dag-Erling Smørgrav
+.\"
+.\" SPDX-License-Identifier: BSD-2-Clause
+.\"
+.Dd September 5, 2023
+.Dt STDCKDINT 3
+.Os
+.Sh NAME
+.Nm stdckdint
+.Nd checked integer arithmetic
+.Sh SYNOPSIS
+.In stdckdint.h
+.Ft bool
+.Fn ckd_add "type1 *result" "type2 a" "type3 b"
+.Ft bool
+.Fn ckd_sub "type1 *result" "type2 a" "type3 b"
+.Ft bool
+.Fn ckd_mul "type1 *result" "type2 a" "type3 b"
+.Sh DESCRIPTION
+The function-like macros
+.Nm ckd_add ,
+.Nm ckd_sub ,
+and
+.Nm ckd_mul
+perform checked integer addition, subtraction, and multiplication,
+respectively.
+If the result of adding, subtracting, or multiplying
+.Fa a
+and
+.Fa b
+as if their respective types had infinite range fits in
+.Ft type1 ,
+it is stored in the location pointed to by
+.Fa result
+and the macro evaluates to
+.Dv false .
+Otherwise, the macro evaluates to
+.Dv true
+and the contents of the location pointed to by
+.Fa result
+is the result of the operation wrapped to the range of
+.Ft type1 .
+.Sh RETURN VALUES
+The
+.Nm ckd_add ,
+.Nm ckd_sub ,
+and
+.Nm ckd_mul
+macros evaluate to
+.Dv true
+if the requested operation overflowed the result type and
+.Dv false
+otherwise.
+.Sh EXAMPLES
+.Bd -literal -offset indent
+#include <assert.h>
+#include <limits.h>
+#include <stdckdint.h>
+
+int main(void)
+{
+	int result;
+
+	assert(!ckd_add(&result, INT_MAX, 0));
+	assert(result == INT_MAX);
+	assert(ckd_add(&result, INT_MAX, 1));
+	assert(result == INT_MIN);
+
+	assert(!ckd_sub(&result, INT_MIN, 0));
+	assert(result == INT_MIN);
+	assert(ckd_sub(&result, INT_MIN, 1));
+	assert(result == INT_MAX);
+
+	assert(!ckd_mul(&result, INT_MAX / 2, 2));
+	assert(result == INT_MAX - 1);
+	assert(ckd_mul(&result, INT_MAX / 2 + 1, 2));
+	assert(result == INT_MIN);
+
+	return 0;
+}
+.Ed
+.\" .Sh STANDARDS
+.\" The
+.\" .Nm ckd_add ,
+.\" .Nm ckd_sub ,
+.\" and
+.\" .Nm ckd_mul
+.\" macros conform to
+.\" .St -isoC-23 .
+.Sh HISTORY
+The
+.Nm ckd_add ,
+.Nm ckd_sub ,
+and
+.Nm ckd_mul
+macros were first introduced in
+.Fx 14.0 .
+.Sh AUTHORS
+The
+.Nm ckd_add ,
+.Nm ckd_sub ,
+and
+.Nm ckd_mul
+macros and this manual page were written by
+.An Dag-Erling Sm\(/orgrav Aq Mt des@FreeBSD.org .
diff --git a/tests/Makefile b/tests/Makefile
index bd20d063ca71..09c0fbc62a2e 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -9,6 +9,7 @@ KYUAFILE= yes
 
 SUBDIR+= etc
 SUBDIR+= examples
+SUBDIR+= include
 SUBDIR+= sys
 SUBDIR+= atf_python
 
diff --git a/tests/include/Makefile b/tests/include/Makefile
new file mode 100644
index 000000000000..e98d08da1f2b
--- /dev/null
+++ b/tests/include/Makefile
@@ -0,0 +1,7 @@
+.include <bsd.own.mk>
+
+TESTSDIR=	${TESTSBASE}/include
+
+ATF_TESTS_C+=		stdckdint_test
+
+.include <bsd.test.mk>
diff --git a/tests/include/stdckdint_test.c b/tests/include/stdckdint_test.c
new file mode 100644
index 000000000000..89262bbd5500
--- /dev/null
+++ b/tests/include/stdckdint_test.c
@@ -0,0 +1,52 @@
+/*-
+ * Copyright (c) 2023 Dag-Erling Smørgrav
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <limits.h>
+#include <stdckdint.h>
+
+#include <atf-c.h>
+
+ATF_TC_WITHOUT_HEAD(ckd_add);
+ATF_TC_BODY(ckd_add, tc)
+{
+	int result;
+
+	ATF_CHECK(!ckd_add(&result, INT_MAX, 0));
+	ATF_CHECK_EQ(INT_MAX, result);
+	ATF_CHECK(ckd_add(&result, INT_MAX, 1));
+	ATF_CHECK_EQ(INT_MIN, result);
+}
+
+ATF_TC_WITHOUT_HEAD(ckd_sub);
+ATF_TC_BODY(ckd_sub, tc)
+{
+	int result;
+
+	ATF_CHECK(!ckd_sub(&result, INT_MIN, 0));
+	ATF_CHECK_EQ(INT_MIN, result);
+	ATF_CHECK(ckd_sub(&result, INT_MIN, 1));
+	ATF_CHECK_EQ(INT_MAX, result);
+}
+
+ATF_TC_WITHOUT_HEAD(ckd_mul);
+ATF_TC_BODY(ckd_mul, tc)
+{
+	int result;
+
+	ATF_CHECK(!ckd_mul(&result, INT_MAX / 2, 2));
+	ATF_CHECK_EQ(INT_MAX - 1, result);
+	ATF_CHECK(ckd_mul(&result, INT_MAX / 2 + 1, 2));
+	ATF_CHECK_EQ(INT_MIN, result);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+	ATF_TP_ADD_TC(tp, ckd_add);
+	ATF_TP_ADD_TC(tp, ckd_sub);
+	ATF_TP_ADD_TC(tp, ckd_mul);
+	return (atf_no_error());
+
+}
diff --git a/usr.bin/less/defines.h b/usr.bin/less/defines.h
index 018d98a48162..ed9c2603970c 100644
--- a/usr.bin/less/defines.h
+++ b/usr.bin/less/defines.h
@@ -327,7 +327,7 @@
 #define HAVE_STAT_INO 1
 
 /* Define to 1 if you have the <stdckdint.h> header file. */
-/* #undef HAVE_STDCKDINT_H */
+#define HAVE_STDCKDINT_H 1
 
 /* Define to 1 if you have the <stdint.h> header file. */
 #define HAVE_STDINT_H 1