From 6648c660deb1fd35592b199a00fc0cf4c72a4c28 Mon Sep 17 00:00:00 2001 From: Cameron Bytheway Date: Tue, 2 Mar 2021 20:19:38 -0800 Subject: [PATCH] utils: add safety_macros codegen script (#2423) --- codebuild/bin/grep_simple_mistakes.sh | 6 +- docs/SAFETY-MACROS.md | 468 ++++++++ error/s2n_errno.c | 2 +- scripts/s2n_safety_macros.py | 759 ++++++++++++ tests/cbmc/proofs/s2n_array_insert/Makefile | 2 +- .../proofs/s2n_array_insert_and_copy/Makefile | 2 +- tests/cbmc/proofs/s2n_array_pushback/Makefile | 2 +- tests/cbmc/stubs/s2n_ensure.c | 28 + tests/s2n_test.h | 13 + .../working/s2n-cbc/stubs/s2n_hash.c | 3 +- .../s2n-record-read-aead/copy_as_needed.sh | 2 + tests/sidetrail/working/stubs/s2n_hash.c | 3 +- tests/testlib/s2n_test_certs.c | 2 +- tests/unit/s2n_safety_macros_test.c | 1042 +++++++++++++++++ utils/s2n_ensure.c | 3 +- utils/s2n_safety.h | 334 +++--- utils/s2n_safety_macros.h | 532 +++++++++ 17 files changed, 3000 insertions(+), 203 deletions(-) create mode 100644 docs/SAFETY-MACROS.md create mode 100644 scripts/s2n_safety_macros.py create mode 100644 tests/cbmc/stubs/s2n_ensure.c create mode 100644 tests/unit/s2n_safety_macros_test.c create mode 100644 utils/s2n_safety_macros.h diff --git a/codebuild/bin/grep_simple_mistakes.sh b/codebuild/bin/grep_simple_mistakes.sh index fe7d66fd531..63ce61ccf28 100755 --- a/codebuild/bin/grep_simple_mistakes.sh +++ b/codebuild/bin/grep_simple_mistakes.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). @@ -65,14 +65,14 @@ for file in $S2N_FILES_ASSERT_NOTNULL_CHECK; do # $line_one definitely contains an assignment from s2n_stuffer_raw_read(), # because that's what we grepped for. So verify that either $line_one or # $line_two contains a null check. - manual_null_check_regex="(.*(if|ENSURE_POSIX).*=\ NULL)|(ENSURE_REF)" + manual_null_check_regex="(.*(if|ENSURE_POSIX|POSIX_ENSURE).*=\ NULL)|(ENSURE_REF)" if [[ $line_one == *"notnull_check("* ]] || [[ $line_one =~ $manual_null_check_regex ]] ||\ [[ $line_two == *"notnull_check("* ]] || [[ $line_two =~ $manual_null_check_regex ]]; then # Found a notnull_check continue else FAILED=1 - printf "\e[1;34mFound a call to s2n_stuffer_raw_read without a notnull_check in $file:\e[0m\n$line_one\n\n" + printf "\e[1;34mFound a call to s2n_stuffer_raw_read without an ENSURE_REF in $file:\e[0m\n$line_one\n\n" fi done < <(grep -rnE -A 1 "=\ss2n_stuffer_raw_read\(.*\)" $file) done diff --git a/docs/SAFETY-MACROS.md b/docs/SAFETY-MACROS.md new file mode 100644 index 00000000000..03392164102 --- /dev/null +++ b/docs/SAFETY-MACROS.md @@ -0,0 +1,468 @@ + +[//]: # (DO NOT DIRECTLY MODIFY THIS FILE:) +[//]: # (The code in this file is generated from scripts/s2n_safety_macros.py and any modifications) +[//]: # (should be in there.) + +# S2N Safety Macros + +## Macros for functions that return `S2N_RESULT` + + +### RESULT_BAIL(error) + +Sets the global `s2n_errno` to `error` and returns with an `S2N_RESULT_ERROR` + + +### RESULT_ENSURE(condition, error) + +Ensures the `condition` is `true`, otherwise the function will `RESULT_BAIL` with `error` + + +### RESULT_DEBUG_ENSURE(condition, error) + +Ensures the `condition` is `true`, otherwise the function will `RESULT_BAIL` with `error` + +NOTE: The condition will _only_ be checked when the code is compiled in debug mode. + In release mode, the check is removed. + + +### RESULT_ENSURE_OK(result, error) + +Ensures `s2n_result_is_ok(result)`, otherwise the function will `RESULT_BAIL` with `error` + +This can be useful for overriding the global `s2n_errno` + + +### RESULT_ENSURE_GTE(a, b) + +Ensures `a` is greater than or equal to `b`, otherwise the function will `RESULT_BAIL` with a `S2N_ERR_SAFETY` error + + +### RESULT_ENSURE_LTE(a, b) + +Ensures `a` is less than or equal to `b`, otherwise the function will `RESULT_BAIL` with a `S2N_ERR_SAFETY` error + + +### RESULT_ENSURE_GT(a, b) + +Ensures `a` is greater than `b`, otherwise the function will `RESULT_BAIL` with a `S2N_ERR_SAFETY` error + + +### RESULT_ENSURE_LT(a, b) + +Ensures `a` is less than `b`, otherwise the function will `RESULT_BAIL` with a `S2N_ERR_SAFETY` error + + +### RESULT_ENSURE_EQ(a, b) + +Ensures `a` is equal to `b`, otherwise the function will `RESULT_BAIL` with a `S2N_ERR_SAFETY` error + + +### RESULT_ENSURE_NE(a, b) + +Ensures `a` is not equal to `b`, otherwise the function will `RESULT_BAIL` with a `S2N_ERR_SAFETY` error + + +### RESULT_ENSURE_INCLUSIVE_RANGE(min, n, max) + +Ensures `min <= n <= max`, otherwise the function will `RESULT_BAIL` with `S2N_ERR_SAFETY` + + +### RESULT_ENSURE_EXCLUSIVE_RANGE(min, n, max) + +Ensures `min < n < max`, otherwise the function will `RESULT_BAIL` with `S2N_ERR_SAFETY` + + +### RESULT_ENSURE_REF(x) + +Ensures `x` is a readable reference, otherwise the function will `RESULT_BAIL` with `S2N_ERR_NULL` + + +### RESULT_ENSURE_MUT(x) + +Ensures `x` is a mutable reference, otherwise the function will `RESULT_BAIL` with `S2N_ERR_NULL` + + +### RESULT_PRECONDITION(result) + +Ensures the `result` is `S2N_RESULT_OK`, otherwise the function will return an error signal + +`RESULT_PRECONDITION` should be used at the beginning of a function to make assertions about +the provided arguments. By default, it is functionally equivalent to `RESULT_GUARD_RESULT(result)` +but can be altered by a testing environment to provide additional guarantees. + + +### RESULT_POSTCONDITION(result) + +Ensures the `result` is `S2N_RESULT_OK`, otherwise the function will return an error signal + +NOTE: The condition will _only_ be checked when the code is compiled in debug mode. + In release mode, the check is removed. + +`RESULT_POSTCONDITION` should be used at the end of a function to make assertions about +the resulting state. In debug mode, it is functionally equivalent to `RESULT_GUARD_RESULT(result)`. +In production builds, it becomes a no-op. This can also be altered by a testing environment +to provide additional guarantees. + + +### RESULT_CHECKED_MEMCPY(destination, source, len) + +Performs a safer memcpy. + +The following checks are performed: + +* `destination` is non-null +* `source` is non-null + +Callers will still need to ensure the following: + +* The size of the data pointed to by both the `destination` and `source` parameters, + shall be at least `len` bytes. + + +### RESULT_CHECKED_MEMSET(destination, value, len) + +Performs a safer memset + +The following checks are performed: + +* `destination` is non-null + +Callers will still need to ensure the following: + +* The size of the data pointed to by the `destination` parameter shall be at least + `len` bytes. + + +### RESULT_GUARD(result) + +Ensures `s2n_result_is_ok(result)`, otherwise the function will return `S2N_RESULT_ERROR` + + +### RESULT_GUARD_OSSL(result, error) + +Ensures `result == _OSSL_SUCCESS`, otherwise the function will `RESULT_BAIL` with `error` + + +### RESULT_GUARD_RESULT(result) + +Ensures `s2n_result_is_ok(result)`, otherwise the function will return `S2N_RESULT_ERROR` + + +### RESULT_GUARD_POSIX(result) + +Ensures `(result) >= S2N_SUCCESS`, otherwise the function will return `S2N_RESULT_ERROR` + + +### RESULT_GUARD_PTR(result) + +Ensures `(result) != NULL`, otherwise the function will return `S2N_RESULT_ERROR` + + +## Macros for functions that return `int` (POSIX error signal) + + +### POSIX_BAIL(error) + +Sets the global `s2n_errno` to `error` and returns with an `S2N_FAILURE` + + +### POSIX_ENSURE(condition, error) + +Ensures the `condition` is `true`, otherwise the function will `POSIX_BAIL` with `error` + + +### POSIX_DEBUG_ENSURE(condition, error) + +Ensures the `condition` is `true`, otherwise the function will `POSIX_BAIL` with `error` + +NOTE: The condition will _only_ be checked when the code is compiled in debug mode. + In release mode, the check is removed. + + +### POSIX_ENSURE_OK(result, error) + +Ensures `(result) >= S2N_SUCCESS`, otherwise the function will `POSIX_BAIL` with `error` + +This can be useful for overriding the global `s2n_errno` + + +### POSIX_ENSURE_GTE(a, b) + +Ensures `a` is greater than or equal to `b`, otherwise the function will `POSIX_BAIL` with a `S2N_ERR_SAFETY` error + + +### POSIX_ENSURE_LTE(a, b) + +Ensures `a` is less than or equal to `b`, otherwise the function will `POSIX_BAIL` with a `S2N_ERR_SAFETY` error + + +### POSIX_ENSURE_GT(a, b) + +Ensures `a` is greater than `b`, otherwise the function will `POSIX_BAIL` with a `S2N_ERR_SAFETY` error + + +### POSIX_ENSURE_LT(a, b) + +Ensures `a` is less than `b`, otherwise the function will `POSIX_BAIL` with a `S2N_ERR_SAFETY` error + + +### POSIX_ENSURE_EQ(a, b) + +Ensures `a` is equal to `b`, otherwise the function will `POSIX_BAIL` with a `S2N_ERR_SAFETY` error + + +### POSIX_ENSURE_NE(a, b) + +Ensures `a` is not equal to `b`, otherwise the function will `POSIX_BAIL` with a `S2N_ERR_SAFETY` error + + +### POSIX_ENSURE_INCLUSIVE_RANGE(min, n, max) + +Ensures `min <= n <= max`, otherwise the function will `POSIX_BAIL` with `S2N_ERR_SAFETY` + + +### POSIX_ENSURE_EXCLUSIVE_RANGE(min, n, max) + +Ensures `min < n < max`, otherwise the function will `POSIX_BAIL` with `S2N_ERR_SAFETY` + + +### POSIX_ENSURE_REF(x) + +Ensures `x` is a readable reference, otherwise the function will `POSIX_BAIL` with `S2N_ERR_NULL` + + +### POSIX_ENSURE_MUT(x) + +Ensures `x` is a mutable reference, otherwise the function will `POSIX_BAIL` with `S2N_ERR_NULL` + + +### POSIX_PRECONDITION(result) + +Ensures the `result` is `S2N_RESULT_OK`, otherwise the function will return an error signal + +`POSIX_PRECONDITION` should be used at the beginning of a function to make assertions about +the provided arguments. By default, it is functionally equivalent to `POSIX_GUARD_RESULT(result)` +but can be altered by a testing environment to provide additional guarantees. + + +### POSIX_POSTCONDITION(result) + +Ensures the `result` is `S2N_RESULT_OK`, otherwise the function will return an error signal + +NOTE: The condition will _only_ be checked when the code is compiled in debug mode. + In release mode, the check is removed. + +`POSIX_POSTCONDITION` should be used at the end of a function to make assertions about +the resulting state. In debug mode, it is functionally equivalent to `POSIX_GUARD_RESULT(result)`. +In production builds, it becomes a no-op. This can also be altered by a testing environment +to provide additional guarantees. + + +### POSIX_CHECKED_MEMCPY(destination, source, len) + +Performs a safer memcpy. + +The following checks are performed: + +* `destination` is non-null +* `source` is non-null + +Callers will still need to ensure the following: + +* The size of the data pointed to by both the `destination` and `source` parameters, + shall be at least `len` bytes. + + +### POSIX_CHECKED_MEMSET(destination, value, len) + +Performs a safer memset + +The following checks are performed: + +* `destination` is non-null + +Callers will still need to ensure the following: + +* The size of the data pointed to by the `destination` parameter shall be at least + `len` bytes. + + +### POSIX_GUARD(result) + +Ensures `(result) >= S2N_SUCCESS`, otherwise the function will return `S2N_FAILURE` + + +### POSIX_GUARD_OSSL(result, error) + +Ensures `result == _OSSL_SUCCESS`, otherwise the function will `POSIX_BAIL` with `error` + + +### POSIX_GUARD_RESULT(result) + +Ensures `s2n_result_is_ok(result)`, otherwise the function will return `S2N_FAILURE` + + +### POSIX_GUARD_POSIX(result) + +Ensures `(result) >= S2N_SUCCESS`, otherwise the function will return `S2N_FAILURE` + + +### POSIX_GUARD_PTR(result) + +Ensures `(result) != NULL`, otherwise the function will return `S2N_FAILURE` + + +## Macros for functions that return a pointer + + +### PTR_BAIL(error) + +Sets the global `s2n_errno` to `error` and returns with an `NULL` + + +### PTR_ENSURE(condition, error) + +Ensures the `condition` is `true`, otherwise the function will `PTR_BAIL` with `error` + + +### PTR_DEBUG_ENSURE(condition, error) + +Ensures the `condition` is `true`, otherwise the function will `PTR_BAIL` with `error` + +NOTE: The condition will _only_ be checked when the code is compiled in debug mode. + In release mode, the check is removed. + + +### PTR_ENSURE_OK(result, error) + +Ensures `(result) != NULL`, otherwise the function will `PTR_BAIL` with `error` + +This can be useful for overriding the global `s2n_errno` + + +### PTR_ENSURE_GTE(a, b) + +Ensures `a` is greater than or equal to `b`, otherwise the function will `PTR_BAIL` with a `S2N_ERR_SAFETY` error + + +### PTR_ENSURE_LTE(a, b) + +Ensures `a` is less than or equal to `b`, otherwise the function will `PTR_BAIL` with a `S2N_ERR_SAFETY` error + + +### PTR_ENSURE_GT(a, b) + +Ensures `a` is greater than `b`, otherwise the function will `PTR_BAIL` with a `S2N_ERR_SAFETY` error + + +### PTR_ENSURE_LT(a, b) + +Ensures `a` is less than `b`, otherwise the function will `PTR_BAIL` with a `S2N_ERR_SAFETY` error + + +### PTR_ENSURE_EQ(a, b) + +Ensures `a` is equal to `b`, otherwise the function will `PTR_BAIL` with a `S2N_ERR_SAFETY` error + + +### PTR_ENSURE_NE(a, b) + +Ensures `a` is not equal to `b`, otherwise the function will `PTR_BAIL` with a `S2N_ERR_SAFETY` error + + +### PTR_ENSURE_INCLUSIVE_RANGE(min, n, max) + +Ensures `min <= n <= max`, otherwise the function will `PTR_BAIL` with `S2N_ERR_SAFETY` + + +### PTR_ENSURE_EXCLUSIVE_RANGE(min, n, max) + +Ensures `min < n < max`, otherwise the function will `PTR_BAIL` with `S2N_ERR_SAFETY` + + +### PTR_ENSURE_REF(x) + +Ensures `x` is a readable reference, otherwise the function will `PTR_BAIL` with `S2N_ERR_NULL` + + +### PTR_ENSURE_MUT(x) + +Ensures `x` is a mutable reference, otherwise the function will `PTR_BAIL` with `S2N_ERR_NULL` + + +### PTR_PRECONDITION(result) + +Ensures the `result` is `S2N_RESULT_OK`, otherwise the function will return an error signal + +`PTR_PRECONDITION` should be used at the beginning of a function to make assertions about +the provided arguments. By default, it is functionally equivalent to `PTR_GUARD_RESULT(result)` +but can be altered by a testing environment to provide additional guarantees. + + +### PTR_POSTCONDITION(result) + +Ensures the `result` is `S2N_RESULT_OK`, otherwise the function will return an error signal + +NOTE: The condition will _only_ be checked when the code is compiled in debug mode. + In release mode, the check is removed. + +`PTR_POSTCONDITION` should be used at the end of a function to make assertions about +the resulting state. In debug mode, it is functionally equivalent to `PTR_GUARD_RESULT(result)`. +In production builds, it becomes a no-op. This can also be altered by a testing environment +to provide additional guarantees. + + +### PTR_CHECKED_MEMCPY(destination, source, len) + +Performs a safer memcpy. + +The following checks are performed: + +* `destination` is non-null +* `source` is non-null + +Callers will still need to ensure the following: + +* The size of the data pointed to by both the `destination` and `source` parameters, + shall be at least `len` bytes. + + +### PTR_CHECKED_MEMSET(destination, value, len) + +Performs a safer memset + +The following checks are performed: + +* `destination` is non-null + +Callers will still need to ensure the following: + +* The size of the data pointed to by the `destination` parameter shall be at least + `len` bytes. + + +### PTR_GUARD(result) + +Ensures `(result) != NULL`, otherwise the function will return `NULL` + + +### PTR_GUARD_OSSL(result, error) + +Ensures `result == _OSSL_SUCCESS`, otherwise the function will `PTR_BAIL` with `error` + + +### PTR_GUARD_RESULT(result) + +Ensures `s2n_result_is_ok(result)`, otherwise the function will return `NULL` + + +### PTR_GUARD_POSIX(result) + +Ensures `(result) >= S2N_SUCCESS`, otherwise the function will return `NULL` + + +### PTR_GUARD_PTR(result) + +Ensures `(result) != NULL`, otherwise the function will return `NULL` + diff --git a/error/s2n_errno.c b/error/s2n_errno.c index 7f683819aea..e9b30fa94d4 100755 --- a/error/s2n_errno.c +++ b/error/s2n_errno.c @@ -371,7 +371,7 @@ int s2n_calculate_stacktrace(void) } int old_errno = errno; - GUARD(s2n_free_stacktrace()); + POSIX_GUARD(s2n_free_stacktrace()); void *array[MAX_BACKTRACE_DEPTH]; tl_stacktrace.trace_size = backtrace(array, MAX_BACKTRACE_DEPTH); tl_stacktrace.trace = backtrace_symbols(array, tl_stacktrace.trace_size); diff --git a/scripts/s2n_safety_macros.py b/scripts/s2n_safety_macros.py new file mode 100644 index 00000000000..6c25cf7b466 --- /dev/null +++ b/scripts/s2n_safety_macros.py @@ -0,0 +1,759 @@ +import textwrap + +copyright = """ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +""" + +header = copyright + """ +#pragma once + +/** + * DO NOT DIRECTLY MODIFY THIS FILE: + * + * The code in this file is generated from scripts/s2n_safety_macros.py and any modifications + * should be in there. + */ + +#include "error/s2n_errno.h" +#include "utils/s2n_ensure.h" +#include "utils/s2n_result.h" + +/** + * The goal of s2n_safety is to provide helpers to perform common + * checks, which help with code readability. + */ + +/* Success signal value for OpenSSL functions */ +#define _OSSL_SUCCESS 1 + +""" + +POSIX = dict( + name = "POSIX", + is_ok = "(result) >= S2N_SUCCESS", + ok = "S2N_SUCCESS", + error = "S2N_FAILURE", + ret = "int", + ret_doc = "`int` (POSIX error signal)", + expect_ok = "EXPECT_SUCCESS", + expect_err = "EXPECT_FAILURE_WITH_ERRNO", +) + +PTR = dict( + name = "PTR", + is_ok = "(result) != NULL", + ok = '"ok"', + error = "NULL", + ret = "const char*", + ret_doc = "a pointer", + expect_ok = "EXPECT_NOT_NULL", + expect_err = "EXPECT_NULL_WITH_ERRNO", +) + +RESULT = dict( + name = "RESULT", + is_ok = "s2n_result_is_ok(result)", + ok = "S2N_RESULT_OK", + error = "S2N_RESULT_ERROR", + ret = "s2n_result", + ret_doc = "`S2N_RESULT`", + expect_ok = "EXPECT_OK", + expect_err = "EXPECT_ERROR_WITH_ERRNO", +) + +DEFAULT = dict( + name = "", + is_ok = RESULT['is_ok'], + ok = RESULT['ok'], + error = RESULT['error'], + ret = RESULT['ret'], + expect_ok = RESULT['expect_ok'], + expect_err = RESULT['expect_err'], +) + +# TODO add DEFAULT and remove RESULT once all PR branches are up-to-date +CONTEXTS = [RESULT, POSIX, PTR] + +max_prefix_len = max(map(lambda c: len(c['name']), CONTEXTS)) + +def cmp_check(op): + return '__S2N_ENSURE((a) ' + op + ' (b), {bail}(S2N_ERR_SAFETY))' + + ## TODO ensure type compatibility + # return '''\\ + # do {{ \\ + # static_assert(__builtin_types_compatible_p(__typeof(a), __typeof(b)), "types do not match"); \\ + # __typeof(a) __tmp_a = ( a ); \\ + # __typeof(b) __tmp_b = ( b ); \\ + # __S2N_ENSURE(__tmp_a ''' + op + ''' __tmp_b, {bail}(S2N_ERR_SAFETY)); \\ + # }} while(0) + # ''' + +MACROS = { + 'BAIL(error)': dict( + doc = 'Sets the global `s2n_errno` to `error` and returns with an `{error}`', + impl = 'do {{ _S2N_ERROR((error)); return {error}; }} while (0)', + harness = ''' + static {ret} {bail}_harness() + {{ + {bail}(S2N_ERR_SAFETY); + return {ok}; + }} + ''', + tests = [ + '{expect_err}({bail}_harness(), S2N_ERR_SAFETY);' + ], + ), + 'ENSURE(condition, error)': dict( + doc = 'Ensures the `condition` is `true`, otherwise the function will `{bail}` with `error`', + impl = '__S2N_ENSURE((condition), {bail}(error))', + harness = ''' + static {ret} {prefix}ENSURE_harness(bool is_ok) + {{ + {prefix}ENSURE(is_ok, S2N_ERR_SAFETY); + return {ok}; + }} + ''', + tests = [ + '{expect_ok}({prefix}ENSURE_harness(true));', + '{expect_err}({prefix}ENSURE_harness(false), S2N_ERR_SAFETY);' + ], + ), + 'DEBUG_ENSURE(condition, error)': dict( + doc = ''' + Ensures the `condition` is `true`, otherwise the function will `{bail}` with `error` + + NOTE: The condition will _only_ be checked when the code is compiled in debug mode. + In release mode, the check is removed. + ''', + impl = '__S2N_ENSURE_DEBUG((condition), {bail}(error))', + harness = ''' + static {ret} {prefix}DEBUG_ENSURE_harness(bool is_ok) + {{ + {prefix}DEBUG_ENSURE(is_ok, S2N_ERR_SAFETY); + return {ok}; + }} + ''', + tests = [ + '{expect_ok}({prefix}DEBUG_ENSURE_harness(true));', + '#ifdef NDEBUG', + '{expect_ok}({prefix}DEBUG_ENSURE_harness(false));', + '#else', + '{expect_err}({prefix}DEBUG_ENSURE_harness(false), S2N_ERR_SAFETY);', + '#endif', + ], + ), + 'ENSURE_OK(result, error)': dict( + doc = ''' + Ensures `{is_ok}`, otherwise the function will `{bail}` with `error` + + This can be useful for overriding the global `s2n_errno` + ''', + impl = '__S2N_ENSURE({is_ok}, {bail}(error))', + harness = ''' + static {ret} {prefix}ENSURE_OK_harness(bool is_ok) + {{ + {prefix}ENSURE_OK({prefix}ENSURE_harness(is_ok), S2N_ERR_IO); + return {ok}; + }} + ''', + tests = [ + '{expect_ok}({prefix}ENSURE_OK_harness(true));', + '{expect_err}({prefix}ENSURE_OK_harness(false), S2N_ERR_IO);' + ], + ), + 'ENSURE_GTE(a, b)': dict( + doc = ''' + Ensures `a` is greater than or equal to `b`, otherwise the function will `{bail}` with a `S2N_ERR_SAFETY` error + ''', + impl = cmp_check('>='), + harness = ''' + static {ret} {prefix}ENSURE_GTE_harness_uint32(uint32_t a, uint32_t b) + {{ + {prefix}ENSURE_GTE(a, b); + /* test the inverse */ + {prefix}ENSURE_LTE(b, a); + return {ok}; + }} + + static {ret} {prefix}ENSURE_GTE_harness_int32(int32_t a, int32_t b) + {{ + {prefix}ENSURE_GTE(a, b); + /* test the inverse */ + {prefix}ENSURE_LTE(b, a); + return {ok}; + }} + ''', + tests = [ + '{expect_ok}({prefix}ENSURE_GTE_harness_uint32(0, 0));', + '{expect_ok}({prefix}ENSURE_GTE_harness_uint32(1, 0));', + '{expect_err}({prefix}ENSURE_GTE_harness_uint32(0, 1), S2N_ERR_SAFETY);', + '{expect_ok}({prefix}ENSURE_GTE_harness_int32(-1, -2));', + '{expect_ok}({prefix}ENSURE_GTE_harness_int32(-1, -1));', + '{expect_err}({prefix}ENSURE_GTE_harness_int32(-2, -1), S2N_ERR_SAFETY);', + ], + ), + 'ENSURE_LTE(a, b)': dict( + doc = ''' + Ensures `a` is less than or equal to `b`, otherwise the function will `{bail}` with a `S2N_ERR_SAFETY` error + ''', + impl = cmp_check('<='), + harness = ''' + static {ret} {prefix}ENSURE_LTE_harness_uint32(uint32_t a, uint32_t b) + {{ + {prefix}ENSURE_LTE(a, b); + /* test the inverse */ + {prefix}ENSURE_GTE(b, a); + return {ok}; + }} + + static {ret} {prefix}ENSURE_LTE_harness_int32(int32_t a, int32_t b) + {{ + {prefix}ENSURE_LTE(a, b); + /* test the inverse */ + {prefix}ENSURE_GTE(b, a); + return {ok}; + }} + ''', + tests = [ + '{expect_ok}({prefix}ENSURE_LTE_harness_uint32(0, 0));', + '{expect_ok}({prefix}ENSURE_LTE_harness_uint32(0, 1));', + '{expect_err}({prefix}ENSURE_LTE_harness_uint32(1, 0), S2N_ERR_SAFETY);', + '{expect_ok}({prefix}ENSURE_LTE_harness_int32(-2, -1));', + '{expect_ok}({prefix}ENSURE_LTE_harness_int32(-1, -1));', + '{expect_err}({prefix}ENSURE_LTE_harness_int32(-1, -2), S2N_ERR_SAFETY);', + ], + ), + 'ENSURE_GT(a, b)': dict( + doc = ''' + Ensures `a` is greater than `b`, otherwise the function will `{bail}` with a `S2N_ERR_SAFETY` error + ''', + impl = cmp_check('>'), + harness = ''' + static {ret} {prefix}ENSURE_GT_harness_uint32(uint32_t a, uint32_t b) + {{ + {prefix}ENSURE_GT(a, b); + /* test the inverse */ + {prefix}ENSURE_LT(b, a); + return {ok}; + }} + + static {ret} {prefix}ENSURE_GT_harness_int32(int32_t a, int32_t b) + {{ + {prefix}ENSURE_GT(a, b); + /* test the inverse */ + {prefix}ENSURE_LT(b, a); + return {ok}; + }} + ''', + tests = [ + '{expect_err}({prefix}ENSURE_GT_harness_uint32(0, 0), S2N_ERR_SAFETY);', + '{expect_ok}({prefix}ENSURE_GT_harness_uint32(1, 0));', + '{expect_err}({prefix}ENSURE_GT_harness_uint32(0, 1), S2N_ERR_SAFETY);', + '{expect_ok}({prefix}ENSURE_GT_harness_int32(-1, -2));', + '{expect_err}({prefix}ENSURE_GT_harness_int32(-1, -1), S2N_ERR_SAFETY);', + '{expect_err}({prefix}ENSURE_GT_harness_int32(-2, -1), S2N_ERR_SAFETY);', + ], + ), + 'ENSURE_LT(a, b)': dict( + doc = ''' + Ensures `a` is less than `b`, otherwise the function will `{bail}` with a `S2N_ERR_SAFETY` error + ''', + impl = cmp_check('<'), + harness = ''' + static {ret} {prefix}ENSURE_LT_harness_uint32(uint32_t a, uint32_t b) + {{ + {prefix}ENSURE_LT(a, b); + /* test the inverse */ + {prefix}ENSURE_GT(b, a); + return {ok}; + }} + + static {ret} {prefix}ENSURE_LT_harness_int32(int32_t a, int32_t b) + {{ + {prefix}ENSURE_LT(a, b); + /* test the inverse */ + {prefix}ENSURE_GT(b, a); + return {ok}; + }} + ''', + tests = [ + '{expect_err}({prefix}ENSURE_LT_harness_uint32(0, 0), S2N_ERR_SAFETY);', + '{expect_ok}({prefix}ENSURE_LT_harness_uint32(0, 1));', + '{expect_err}({prefix}ENSURE_LT_harness_uint32(1, 0), S2N_ERR_SAFETY);', + '{expect_ok}({prefix}ENSURE_LT_harness_int32(-2, -1));', + '{expect_err}({prefix}ENSURE_LT_harness_int32(-1, -1), S2N_ERR_SAFETY);', + '{expect_err}({prefix}ENSURE_LT_harness_int32(-1, -2), S2N_ERR_SAFETY);', + ], + ), + 'ENSURE_EQ(a, b)': dict( + doc = ''' + Ensures `a` is equal to `b`, otherwise the function will `{bail}` with a `S2N_ERR_SAFETY` error + ''', + impl = cmp_check('=='), + harness = ''' + static {ret} {prefix}ENSURE_EQ_harness_uint32(uint32_t a, uint32_t b) + {{ + {prefix}ENSURE_EQ(a, b); + {prefix}ENSURE_EQ(b, a); + return {ok}; + }} + + static {ret} {prefix}ENSURE_EQ_harness_int32(int32_t a, int32_t b) + {{ + {prefix}ENSURE_EQ(a, b); + {prefix}ENSURE_EQ(b, a); + return {ok}; + }} + ''', + tests = [ + '{expect_ok}({prefix}ENSURE_EQ_harness_uint32(0, 0));', + '{expect_err}({prefix}ENSURE_EQ_harness_uint32(1, 0), S2N_ERR_SAFETY);', + '{expect_ok}({prefix}ENSURE_EQ_harness_int32(-1, -1));', + '{expect_err}({prefix}ENSURE_EQ_harness_int32(-2, -1), S2N_ERR_SAFETY);', + ], + ), + 'ENSURE_NE(a, b)': dict( + doc = ''' + Ensures `a` is not equal to `b`, otherwise the function will `{bail}` with a `S2N_ERR_SAFETY` error + ''', + impl = cmp_check('!='), + harness = ''' + static {ret} {prefix}ENSURE_NE_harness_uint32(uint32_t a, uint32_t b) + {{ + {prefix}ENSURE_NE(a, b); + {prefix}ENSURE_NE(b, a); + return {ok}; + }} + + static {ret} {prefix}ENSURE_NE_harness_int32(int32_t a, int32_t b) + {{ + {prefix}ENSURE_NE(a, b); + {prefix}ENSURE_NE(b, a); + return {ok}; + }} + ''', + tests = [ + '{expect_ok}({prefix}ENSURE_NE_harness_uint32(1, 0));', + '{expect_err}({prefix}ENSURE_NE_harness_uint32(0, 0), S2N_ERR_SAFETY);', + '{expect_ok}({prefix}ENSURE_NE_harness_int32(-2, -1));', + '{expect_err}({prefix}ENSURE_NE_harness_int32(-1, -1), S2N_ERR_SAFETY);', + ], + ), + 'ENSURE_INCLUSIVE_RANGE(min, n, max)': dict( + doc = 'Ensures `min <= n <= max`, otherwise the function will `{bail}` with `S2N_ERR_SAFETY`', + impl = ''' \\ + do {{ \\ + __typeof(n) __tmp_n = ( n ); \\ + __typeof(n) __tmp_min = ( min ); \\ + __typeof(n) __tmp_max = ( max ); \\ + {prefix}ENSURE_GTE(__tmp_n, __tmp_min); \\ + {prefix}ENSURE_LTE(__tmp_n, __tmp_max); \\ + }} while(0)''', + harness = ''' + static {ret} {prefix}ENSURE_INCLUSIVE_RANGE_harness_uint32(uint32_t a, uint32_t b, uint32_t c) + {{ + {prefix}ENSURE_INCLUSIVE_RANGE(a, b, c); + return {ok}; + }} + + static {ret} {prefix}ENSURE_INCLUSIVE_RANGE_harness_int32(int32_t a, int32_t b, int32_t c) + {{ + {prefix}ENSURE_INCLUSIVE_RANGE(a, b, c); + return {ok}; + }} + ''', + tests = [ + '{expect_err}({prefix}ENSURE_INCLUSIVE_RANGE_harness_uint32(1, 0, 2), S2N_ERR_SAFETY);', + '{expect_ok}({prefix}ENSURE_INCLUSIVE_RANGE_harness_uint32(1, 1, 2));', + '{expect_ok}({prefix}ENSURE_INCLUSIVE_RANGE_harness_uint32(1, 2, 2));', + '{expect_err}({prefix}ENSURE_INCLUSIVE_RANGE_harness_uint32(1, 3, 2), S2N_ERR_SAFETY);', + + '{expect_err}({prefix}ENSURE_INCLUSIVE_RANGE_harness_int32(-2, -3, -1), S2N_ERR_SAFETY);', + '{expect_ok}({prefix}ENSURE_INCLUSIVE_RANGE_harness_int32(-2, -2, -1));', + '{expect_ok}({prefix}ENSURE_INCLUSIVE_RANGE_harness_int32(-2, -1, -1));', + '{expect_err}({prefix}ENSURE_INCLUSIVE_RANGE_harness_int32(-2, 0, -1), S2N_ERR_SAFETY);', + ], + ), + 'ENSURE_EXCLUSIVE_RANGE(min, n, max)': dict( + doc = 'Ensures `min < n < max`, otherwise the function will `{bail}` with `S2N_ERR_SAFETY`', + impl = ''' \\ + do {{ \\ + __typeof(n) __tmp_n = ( n ); \\ + __typeof(n) __tmp_min = ( min ); \\ + __typeof(n) __tmp_max = ( max ); \\ + {prefix}ENSURE_GT(__tmp_n, __tmp_min); \\ + {prefix}ENSURE_LT(__tmp_n, __tmp_max); \\ + }} while(0)''', + harness = ''' + static {ret} {prefix}ENSURE_EXCLUSIVE_RANGE_harness_uint32(uint32_t a, uint32_t b, uint32_t c) + {{ + {prefix}ENSURE_EXCLUSIVE_RANGE(a, b, c); + return {ok}; + }} + + static {ret} {prefix}ENSURE_EXCLUSIVE_RANGE_harness_int32(int32_t a, int32_t b, int32_t c) + {{ + {prefix}ENSURE_EXCLUSIVE_RANGE(a, b, c); + return {ok}; + }} + ''', + tests = [ + '{expect_err}({prefix}ENSURE_EXCLUSIVE_RANGE_harness_uint32(1, 0, 3), S2N_ERR_SAFETY);', + '{expect_err}({prefix}ENSURE_EXCLUSIVE_RANGE_harness_uint32(1, 1, 3), S2N_ERR_SAFETY);', + '{expect_ok}({prefix}ENSURE_EXCLUSIVE_RANGE_harness_uint32(1, 2, 3));', + '{expect_err}({prefix}ENSURE_EXCLUSIVE_RANGE_harness_uint32(1, 3, 3), S2N_ERR_SAFETY);', + '{expect_err}({prefix}ENSURE_EXCLUSIVE_RANGE_harness_uint32(1, 4, 3), S2N_ERR_SAFETY);', + + '{expect_err}({prefix}ENSURE_EXCLUSIVE_RANGE_harness_int32(-3, -4, -1), S2N_ERR_SAFETY);', + '{expect_err}({prefix}ENSURE_EXCLUSIVE_RANGE_harness_int32(-3, -3, -1), S2N_ERR_SAFETY);', + '{expect_ok}({prefix}ENSURE_EXCLUSIVE_RANGE_harness_int32(-3, -2, -1));', + '{expect_err}({prefix}ENSURE_EXCLUSIVE_RANGE_harness_int32(-3, -1, -1), S2N_ERR_SAFETY);', + '{expect_err}({prefix}ENSURE_EXCLUSIVE_RANGE_harness_int32(-3, 0, -1), S2N_ERR_SAFETY);', + ], + ), + 'ENSURE_REF(x)': dict( + doc = 'Ensures `x` is a readable reference, otherwise the function will `{bail}` with `S2N_ERR_NULL`', + impl = '__S2N_ENSURE(S2N_OBJECT_PTR_IS_READABLE(x), {bail}(S2N_ERR_NULL))', + harness = ''' + static {ret} {prefix}ENSURE_REF_harness(const char* str) + {{ + {prefix}ENSURE_REF(str); + return {ok}; + }} + ''', + tests = [ + '{expect_ok}({prefix}ENSURE_REF_harness(""));', + '{expect_ok}({prefix}ENSURE_REF_harness("ok"));', + '{expect_err}({prefix}ENSURE_REF_harness(NULL), S2N_ERR_NULL);', + ], + ), + 'ENSURE_MUT(x)': dict( + doc = 'Ensures `x` is a mutable reference, otherwise the function will `{bail}` with `S2N_ERR_NULL`', + impl = '__S2N_ENSURE(S2N_OBJECT_PTR_IS_WRITABLE(x), {bail}(S2N_ERR_NULL))', + harness = ''' + static {ret} {prefix}ENSURE_MUT_harness(uint32_t* v) + {{ + {prefix}ENSURE_MUT(v); + return {ok}; + }} + ''', + tests = [ + 'uint32_t {prefix}ensure_mut_test = 0;', + '{expect_ok}({prefix}ENSURE_MUT_harness(&{prefix}ensure_mut_test));', + '{prefix}ensure_mut_test = 1;', + '{expect_ok}({prefix}ENSURE_MUT_harness(&{prefix}ensure_mut_test));', + '{expect_err}({prefix}ENSURE_MUT_harness(NULL), S2N_ERR_NULL);', + ], + ), + 'PRECONDITION(result)': dict( + doc = ''' + Ensures the `result` is `S2N_RESULT_OK`, otherwise the function will return an error signal + + `{prefix}PRECONDITION` should be used at the beginning of a function to make assertions about + the provided arguments. By default, it is functionally equivalent to `{prefix}GUARD_RESULT(result)` + but can be altered by a testing environment to provide additional guarantees. + ''', + impl = '{prefix}GUARD_RESULT(__S2N_ENSURE_PRECONDITION((result)))', + harness = ''' + static S2N_RESULT {prefix}PRECONDITION_harness_check(bool is_ok) + {{ + RESULT_ENSURE(is_ok, S2N_ERR_SAFETY); + return S2N_RESULT_OK; + }} + + static {ret} {prefix}PRECONDITION_harness(s2n_result result) + {{ + {prefix}PRECONDITION(result); + return {ok}; + }} + ''', + tests = [ + '{expect_ok}({prefix}PRECONDITION_harness({prefix}PRECONDITION_harness_check(true)));', + '{expect_err}({prefix}PRECONDITION_harness({prefix}PRECONDITION_harness_check(false)), S2N_ERR_SAFETY);', + ], + ), + 'POSTCONDITION(result)': dict( + doc = ''' + Ensures the `result` is `S2N_RESULT_OK`, otherwise the function will return an error signal + + NOTE: The condition will _only_ be checked when the code is compiled in debug mode. + In release mode, the check is removed. + + `{prefix}POSTCONDITION` should be used at the end of a function to make assertions about + the resulting state. In debug mode, it is functionally equivalent to `{prefix}GUARD_RESULT(result)`. + In production builds, it becomes a no-op. This can also be altered by a testing environment + to provide additional guarantees. + ''', + impl = '{prefix}GUARD_RESULT(__S2N_ENSURE_POSTCONDITION((result)))', + harness = ''' + static S2N_RESULT {prefix}POSTCONDITION_harness_check(bool is_ok) + {{ + RESULT_ENSURE(is_ok, S2N_ERR_SAFETY); + return S2N_RESULT_OK; + }} + + static {ret} {prefix}POSTCONDITION_harness(s2n_result result) + {{ + {prefix}POSTCONDITION(result); + return {ok}; + }} + ''', + tests = [ + '{expect_ok}({prefix}POSTCONDITION_harness({prefix}POSTCONDITION_harness_check(true)));', + '#ifdef NDEBUG', + '{expect_ok}({prefix}POSTCONDITION_harness({prefix}POSTCONDITION_harness_check(false)));', + '#else', + '{expect_err}({prefix}POSTCONDITION_harness({prefix}POSTCONDITION_harness_check(false)), S2N_ERR_SAFETY);', + '#endif', + ], + ), + 'CHECKED_MEMCPY(destination, source, len)': dict( + doc = ''' + Performs a safer memcpy. + + The following checks are performed: + + * `destination` is non-null + * `source` is non-null + + Callers will still need to ensure the following: + + * The size of the data pointed to by both the `destination` and `source` parameters, + shall be at least `len` bytes. + ''', + impl = '__S2N_ENSURE_SAFE_MEMCPY((destination), (source), (len), {prefix}GUARD_PTR)', + harness = ''' + static {ret} {prefix}CHECKED_MEMCPY_harness(uint32_t* dest, uint32_t* source, size_t len) + {{ + {prefix}CHECKED_MEMCPY(dest, source, len); + return {ok}; + }} + ''', + tests = [ + 'uint32_t {prefix}_checked_memcpy_dest = 1;', + 'uint32_t {prefix}_checked_memcpy_source = 2;', + '{expect_ok}({prefix}CHECKED_MEMCPY_harness(&{prefix}_checked_memcpy_dest, &{prefix}_checked_memcpy_source, 0));', + 'EXPECT_EQUAL({prefix}_checked_memcpy_dest, 1);', + '{expect_err}({prefix}CHECKED_MEMCPY_harness(NULL, &{prefix}_checked_memcpy_source, 4), S2N_ERR_NULL);', + '{expect_err}({prefix}CHECKED_MEMCPY_harness(&{prefix}_checked_memcpy_dest, NULL, 4), S2N_ERR_NULL);', + '{expect_ok}({prefix}CHECKED_MEMCPY_harness(&{prefix}_checked_memcpy_dest, &{prefix}_checked_memcpy_source, 4));', + 'EXPECT_EQUAL({prefix}_checked_memcpy_dest, {prefix}_checked_memcpy_source);' + ], + ), + 'CHECKED_MEMSET(destination, value, len)': dict( + doc = ''' + Performs a safer memset + + The following checks are performed: + + * `destination` is non-null + + Callers will still need to ensure the following: + + * The size of the data pointed to by the `destination` parameter shall be at least + `len` bytes. + ''', + impl = '__S2N_ENSURE_SAFE_MEMSET((destination), (value), (len), {prefix}ENSURE_REF)', + harness = ''' + static {ret} {prefix}CHECKED_MEMSET_harness(uint32_t* dest, uint8_t value, size_t len) + {{ + {prefix}CHECKED_MEMSET(dest, value, len); + return {ok}; + }} + ''', + tests = [ + 'uint32_t {prefix}_checked_memset_dest = 1;', + '{expect_ok}({prefix}CHECKED_MEMSET_harness(&{prefix}_checked_memset_dest, 0x42, 0));', + 'EXPECT_EQUAL({prefix}_checked_memset_dest, 1);', + '{expect_err}({prefix}CHECKED_MEMSET_harness(NULL, 0x42, 1), S2N_ERR_NULL);', + '{expect_ok}({prefix}CHECKED_MEMSET_harness(&{prefix}_checked_memset_dest, 0x42, 4));', + 'EXPECT_EQUAL({prefix}_checked_memset_dest, 0x42424242);' + ], + ), + 'GUARD(result)': dict( + doc = 'Ensures `{is_ok}`, otherwise the function will return `{error}`', + impl = '__S2N_ENSURE({is_ok}, return {error})', + harness = ''' + static {ret} {prefix}GUARD_harness({ret} result) + {{ + {prefix}GUARD(result); + return {ok}; + }} + ''', + tests = [ + '{expect_ok}({prefix}GUARD_harness({prefix}ENSURE_harness(true)));', + '{expect_err}({prefix}GUARD_harness({prefix}ENSURE_harness(false)), S2N_ERR_SAFETY);', + ], + ), + 'GUARD_OSSL(result, error)': dict( + doc = 'Ensures `result == _OSSL_SUCCESS`, otherwise the function will `{bail}` with `error`', + impl = '__S2N_ENSURE((result) == _OSSL_SUCCESS, {bail}(error))', + harness = ''' + static {ret} {prefix}GUARD_OSSL_harness(int result, int error) + {{ + {prefix}GUARD_OSSL(result, error); + return {ok}; + }} + ''', + tests = [ + '{expect_ok}({prefix}GUARD_OSSL_harness(1, S2N_ERR_SAFETY));', + '{expect_err}({prefix}GUARD_OSSL_harness(0, S2N_ERR_SAFETY), S2N_ERR_SAFETY);', + ], + ), +} + +max_macro_len = max(map(len, MACROS.keys())) + 8 + +def push_macro(args): + macro_indent = ' ' * (max_macro_len - len(args['macro'])) + + h = "" + h += '/**\n' + + for line in args['doc'].split('\n'): + h += ' *' + if len(line) > 0: + h += ' ' + line + h += '\n' + + h += ' */\n' + h += '#define ' + h += args['prefix'] + h += args['macro'] + h += args['indent'] + h += macro_indent + h += args['impl'].format_map(args) + h += '\n\n' + + return h + +for context in CONTEXTS: + # initialize contexts + if len(context['name']) > 0: + context['prefix'] = context['name'] + '_' + context['suffix'] = '_' + context['name'] + else: + context['prefix'] = '' + context['suffix'] = '' + + context['indent'] = ' ' * (max_prefix_len - len(context['prefix'])) + context['bail'] = '{prefix}BAIL'.format_map(context) + +harnesses = "" +docs = """ +[//]: # (DO NOT DIRECTLY MODIFY THIS FILE:) +[//]: # (The code in this file is generated from scripts/s2n_safety_macros.py and any modifications) +[//]: # (should be in there.) + +# S2N Safety Macros +""" +checks = [] + +def push_doc(args): + args['doc'] = textwrap.dedent(args['doc']).format_map(args).strip() + + return textwrap.dedent(""" + ### {prefix}{macro} + + {doc} + + """).format_map(args) + +for context in CONTEXTS: + docs += textwrap.dedent(""" + ## Macros for functions that return {ret_doc} + + """).format_map(context) + + for name, value in MACROS.items(): + args = {'macro': name} + args.update(context) + args.update(value) + + docs += push_doc(args) + header += push_macro(args) + + harness = value.get('harness', None) + if harness != None: + harnesses += textwrap.dedent(harness).format_map(context) + checks.append('/* ' + context['prefix'] + name + ' */') + assert len(value['tests']) > 0, "{} is missing tests".format(name) + for check in value['tests']: + checks.append(check.format_map(context)) + checks.append('') + + for other in CONTEXTS: + if len(other['suffix']) > 0: + doc = 'Ensures `{is_ok}`, otherwise the function will return `{error}`' + impl = '__S2N_ENSURE({is_ok}, return {error})' + args = { + 'prefix': context['prefix'], + 'suffix': other['suffix'], + 'is_ok': other['is_ok'], + 'ok': other['ok'], + 'error': context['error'], + 'indent': context['indent'], + 'doc': doc, + 'impl': impl, + } + args['macro'] = 'GUARD{suffix}(result)'.format_map(args) + docs += push_doc(args) + header += push_macro(args) + +def write(f, contents): + header_file = open(f, "w") + header_file.write(contents) + header_file.close() + +write("utils/s2n_safety_macros.h", header) + +test = copyright + ''' +#include "s2n_test.h" + +#include "utils/s2n_safety.h" + +/** + * DO NOT DIRECTLY MODIFY THIS FILE: + * + * The code in this file is generated from scripts/s2n_safety_macros.py and any modifications + * should be in there. + */ + +/* harnesses */ +''' +test += harnesses +test += ''' +int main(int argc, char **argv) +{ + BEGIN_TEST(); + +''' +for check in checks: + if len(check) > 0: + test += ' ' + check + test += '\n' + +test += ''' + END_TEST(); + return S2N_SUCCESS; +} +''' + +write("tests/unit/s2n_safety_macros_test.c", test) + +write("docs/SAFETY-MACROS.md", docs) + diff --git a/tests/cbmc/proofs/s2n_array_insert/Makefile b/tests/cbmc/proofs/s2n_array_insert/Makefile index bb23bff1e70..aabe7cc8da3 100644 --- a/tests/cbmc/proofs/s2n_array_insert/Makefile +++ b/tests/cbmc/proofs/s2n_array_insert/Makefile @@ -37,11 +37,11 @@ PROOF_SOURCES += $(PROOF_STUB)/mlock.c PROOF_SOURCES += $(PROOF_STUB)/munlock.c PROOF_SOURCES += $(PROOF_STUB)/posix_memalign_override.c PROOF_SOURCES += $(PROOF_STUB)/s2n_calculate_stacktrace.c +PROOF_SOURCES += $(PROOF_STUB)/s2n_ensure.c PROOF_SOURCES += $(PROOF_STUB)/sysconf.c PROJECT_SOURCES += $(SRCDIR)/utils/s2n_array.c PROJECT_SOURCES += $(SRCDIR)/utils/s2n_blob.c -PROJECT_SOURCES += $(SRCDIR)/utils/s2n_ensure.c PROJECT_SOURCES += $(SRCDIR)/utils/s2n_mem.c PROJECT_SOURCES += $(SRCDIR)/utils/s2n_result.c PROJECT_SOURCES += $(SRCDIR)/utils/s2n_safety.c diff --git a/tests/cbmc/proofs/s2n_array_insert_and_copy/Makefile b/tests/cbmc/proofs/s2n_array_insert_and_copy/Makefile index 90a6d5759ce..0224e3fbf3a 100644 --- a/tests/cbmc/proofs/s2n_array_insert_and_copy/Makefile +++ b/tests/cbmc/proofs/s2n_array_insert_and_copy/Makefile @@ -37,11 +37,11 @@ PROOF_SOURCES += $(PROOF_STUB)/mlock.c PROOF_SOURCES += $(PROOF_STUB)/munlock.c PROOF_SOURCES += $(PROOF_STUB)/posix_memalign_override.c PROOF_SOURCES += $(PROOF_STUB)/s2n_calculate_stacktrace.c +PROOF_SOURCES += $(PROOF_STUB)/s2n_ensure.c PROOF_SOURCES += $(PROOF_STUB)/sysconf.c PROJECT_SOURCES += $(SRCDIR)/utils/s2n_array.c PROJECT_SOURCES += $(SRCDIR)/utils/s2n_blob.c -PROJECT_SOURCES += $(SRCDIR)/utils/s2n_ensure.c PROJECT_SOURCES += $(SRCDIR)/utils/s2n_mem.c PROJECT_SOURCES += $(SRCDIR)/utils/s2n_result.c PROJECT_SOURCES += $(SRCDIR)/utils/s2n_safety.c diff --git a/tests/cbmc/proofs/s2n_array_pushback/Makefile b/tests/cbmc/proofs/s2n_array_pushback/Makefile index 60d89090e34..0296876b30e 100644 --- a/tests/cbmc/proofs/s2n_array_pushback/Makefile +++ b/tests/cbmc/proofs/s2n_array_pushback/Makefile @@ -37,11 +37,11 @@ PROOF_SOURCES += $(PROOF_STUB)/mlock.c PROOF_SOURCES += $(PROOF_STUB)/munlock.c PROOF_SOURCES += $(PROOF_STUB)/posix_memalign_override.c PROOF_SOURCES += $(PROOF_STUB)/s2n_calculate_stacktrace.c +PROOF_SOURCES += $(PROOF_STUB)/s2n_ensure.c PROOF_SOURCES += $(PROOF_STUB)/sysconf.c PROJECT_SOURCES += $(SRCDIR)/utils/s2n_array.c PROJECT_SOURCES += $(SRCDIR)/utils/s2n_blob.c -PROJECT_SOURCES += $(SRCDIR)/utils/s2n_ensure.c PROJECT_SOURCES += $(SRCDIR)/utils/s2n_mem.c PROJECT_SOURCES += $(SRCDIR)/utils/s2n_result.c PROJECT_SOURCES += $(SRCDIR)/utils/s2n_safety.c diff --git a/tests/cbmc/stubs/s2n_ensure.c b/tests/cbmc/stubs/s2n_ensure.c new file mode 100644 index 00000000000..d5b5e1bc323 --- /dev/null +++ b/tests/cbmc/stubs/s2n_ensure.c @@ -0,0 +1,28 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "utils/s2n_safety.h" + +void* s2n_ensure_memcpy_trace(void *restrict to, const void *restrict from, size_t size, const char *debug_str) +{ + if (to == NULL || from == NULL) { + s2n_errno = S2N_ERR_NULL; + s2n_debug_str = debug_str; + return NULL; + } + + /* use memcpy rather than memmove with CBMC to improve proof performance */ + return memcpy(to, from, size); +} diff --git a/tests/s2n_test.h b/tests/s2n_test.h index ed89dc57139..c32acd80c0e 100644 --- a/tests/s2n_test.h +++ b/tests/s2n_test.h @@ -153,6 +153,19 @@ int test_count; RESET_ERRNO(); \ } while(0) +#define EXPECT_NULL_WITH_ERRNO_NO_RESET( function_call, err ) \ + do { \ + EXPECT_NULL( (function_call) ); \ + EXPECT_EQUAL(s2n_errno, err); \ + EXPECT_NOT_NULL(s2n_debug_str); \ + } while(0) + +#define EXPECT_NULL_WITH_ERRNO( function_call, err ) \ + do { \ + EXPECT_NULL_WITH_ERRNO_NO_RESET( function_call, err ); \ + RESET_ERRNO(); \ + } while(0) + #define EXPECT_SUCCESS( function_call ) EXPECT_NOT_EQUAL( (function_call) , -1 ) /* for use with S2N_RESULT */ #define EXPECT_OK( function_call ) EXPECT_TRUE( s2n_result_is_ok(function_call) ) diff --git a/tests/sidetrail/working/s2n-cbc/stubs/s2n_hash.c b/tests/sidetrail/working/s2n-cbc/stubs/s2n_hash.c index d45e277ccf0..a0567de8c25 100644 --- a/tests/sidetrail/working/s2n-cbc/stubs/s2n_hash.c +++ b/tests/sidetrail/working/s2n-cbc/stubs/s2n_hash.c @@ -15,11 +15,12 @@ #include #include +#include #include "crypto/s2n_hash.h" #include "error/s2n_errno.h" -#include #include "ct-verif.h" #include "sidetrail.h" +#include "utils/s2n_safety.h" int s2n_hash_digest_size(s2n_hash_algorithm alg, uint8_t *out) { diff --git a/tests/sidetrail/working/s2n-record-read-aead/copy_as_needed.sh b/tests/sidetrail/working/s2n-record-read-aead/copy_as_needed.sh index 3924b853688..ab5aa2777c5 100755 --- a/tests/sidetrail/working/s2n-record-read-aead/copy_as_needed.sh +++ b/tests/sidetrail/working/s2n-record-read-aead/copy_as_needed.sh @@ -48,7 +48,9 @@ patch -p5 < ../patches/cbc.patch mkdir -p utils cp $S2N_BASE/utils/s2n_blob.c utils/ cp $S2N_BASE/utils/s2n_result.c utils/ +cp $S2N_BASE/utils/s2n_result.h utils/ cp $S2N_BASE/utils/s2n_safety.c utils/ +cp $S2N_BASE/utils/s2n_safety.h utils/ patch -p1 < ../patches/safety.patch cp s2n_annotations.h utils/ diff --git a/tests/sidetrail/working/stubs/s2n_hash.c b/tests/sidetrail/working/stubs/s2n_hash.c index d45e277ccf0..a0567de8c25 100644 --- a/tests/sidetrail/working/stubs/s2n_hash.c +++ b/tests/sidetrail/working/stubs/s2n_hash.c @@ -15,11 +15,12 @@ #include #include +#include #include "crypto/s2n_hash.h" #include "error/s2n_errno.h" -#include #include "ct-verif.h" #include "sidetrail.h" +#include "utils/s2n_safety.h" int s2n_hash_digest_size(s2n_hash_algorithm alg, uint8_t *out) { diff --git a/tests/testlib/s2n_test_certs.c b/tests/testlib/s2n_test_certs.c index 81a6a664596..8e9b1b5ffe9 100644 --- a/tests/testlib/s2n_test_certs.c +++ b/tests/testlib/s2n_test_certs.c @@ -33,7 +33,7 @@ int s2n_test_cert_chain_and_key_new(struct s2n_cert_chain_and_key **chain_and_ke GUARD(s2n_read_test_pem(cert_chain_file, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); GUARD(s2n_read_test_pem(private_key_file, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); - notnull_check(*chain_and_key = s2n_cert_chain_and_key_new()); + POSIX_GUARD_PTR(*chain_and_key = s2n_cert_chain_and_key_new()); GUARD(s2n_cert_chain_and_key_load_pem(*chain_and_key, cert_chain_pem, private_key_pem)); return S2N_SUCCESS; diff --git a/tests/unit/s2n_safety_macros_test.c b/tests/unit/s2n_safety_macros_test.c new file mode 100644 index 00000000000..f8b85cdcfbd --- /dev/null +++ b/tests/unit/s2n_safety_macros_test.c @@ -0,0 +1,1042 @@ + +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "s2n_test.h" + +#include "utils/s2n_safety.h" + +/** + * DO NOT DIRECTLY MODIFY THIS FILE: + * + * The code in this file is generated from scripts/s2n_safety_macros.py and any modifications + * should be in there. + */ + +/* harnesses */ + +static s2n_result RESULT_BAIL_harness() +{ + RESULT_BAIL(S2N_ERR_SAFETY); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_ENSURE_harness(bool is_ok) +{ + RESULT_ENSURE(is_ok, S2N_ERR_SAFETY); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_DEBUG_ENSURE_harness(bool is_ok) +{ + RESULT_DEBUG_ENSURE(is_ok, S2N_ERR_SAFETY); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_ENSURE_OK_harness(bool is_ok) +{ + RESULT_ENSURE_OK(RESULT_ENSURE_harness(is_ok), S2N_ERR_IO); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_ENSURE_GTE_harness_uint32(uint32_t a, uint32_t b) +{ + RESULT_ENSURE_GTE(a, b); + /* test the inverse */ + RESULT_ENSURE_LTE(b, a); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_ENSURE_GTE_harness_int32(int32_t a, int32_t b) +{ + RESULT_ENSURE_GTE(a, b); + /* test the inverse */ + RESULT_ENSURE_LTE(b, a); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_ENSURE_LTE_harness_uint32(uint32_t a, uint32_t b) +{ + RESULT_ENSURE_LTE(a, b); + /* test the inverse */ + RESULT_ENSURE_GTE(b, a); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_ENSURE_LTE_harness_int32(int32_t a, int32_t b) +{ + RESULT_ENSURE_LTE(a, b); + /* test the inverse */ + RESULT_ENSURE_GTE(b, a); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_ENSURE_GT_harness_uint32(uint32_t a, uint32_t b) +{ + RESULT_ENSURE_GT(a, b); + /* test the inverse */ + RESULT_ENSURE_LT(b, a); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_ENSURE_GT_harness_int32(int32_t a, int32_t b) +{ + RESULT_ENSURE_GT(a, b); + /* test the inverse */ + RESULT_ENSURE_LT(b, a); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_ENSURE_LT_harness_uint32(uint32_t a, uint32_t b) +{ + RESULT_ENSURE_LT(a, b); + /* test the inverse */ + RESULT_ENSURE_GT(b, a); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_ENSURE_LT_harness_int32(int32_t a, int32_t b) +{ + RESULT_ENSURE_LT(a, b); + /* test the inverse */ + RESULT_ENSURE_GT(b, a); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_ENSURE_EQ_harness_uint32(uint32_t a, uint32_t b) +{ + RESULT_ENSURE_EQ(a, b); + RESULT_ENSURE_EQ(b, a); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_ENSURE_EQ_harness_int32(int32_t a, int32_t b) +{ + RESULT_ENSURE_EQ(a, b); + RESULT_ENSURE_EQ(b, a); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_ENSURE_NE_harness_uint32(uint32_t a, uint32_t b) +{ + RESULT_ENSURE_NE(a, b); + RESULT_ENSURE_NE(b, a); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_ENSURE_NE_harness_int32(int32_t a, int32_t b) +{ + RESULT_ENSURE_NE(a, b); + RESULT_ENSURE_NE(b, a); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_ENSURE_INCLUSIVE_RANGE_harness_uint32(uint32_t a, uint32_t b, uint32_t c) +{ + RESULT_ENSURE_INCLUSIVE_RANGE(a, b, c); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_ENSURE_INCLUSIVE_RANGE_harness_int32(int32_t a, int32_t b, int32_t c) +{ + RESULT_ENSURE_INCLUSIVE_RANGE(a, b, c); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_ENSURE_EXCLUSIVE_RANGE_harness_uint32(uint32_t a, uint32_t b, uint32_t c) +{ + RESULT_ENSURE_EXCLUSIVE_RANGE(a, b, c); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_ENSURE_EXCLUSIVE_RANGE_harness_int32(int32_t a, int32_t b, int32_t c) +{ + RESULT_ENSURE_EXCLUSIVE_RANGE(a, b, c); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_ENSURE_REF_harness(const char* str) +{ + RESULT_ENSURE_REF(str); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_ENSURE_MUT_harness(uint32_t* v) +{ + RESULT_ENSURE_MUT(v); + return S2N_RESULT_OK; +} + +static S2N_RESULT RESULT_PRECONDITION_harness_check(bool is_ok) +{ + RESULT_ENSURE(is_ok, S2N_ERR_SAFETY); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_PRECONDITION_harness(s2n_result result) +{ + RESULT_PRECONDITION(result); + return S2N_RESULT_OK; +} + +static S2N_RESULT RESULT_POSTCONDITION_harness_check(bool is_ok) +{ + RESULT_ENSURE(is_ok, S2N_ERR_SAFETY); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_POSTCONDITION_harness(s2n_result result) +{ + RESULT_POSTCONDITION(result); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_CHECKED_MEMCPY_harness(uint32_t* dest, uint32_t* source, size_t len) +{ + RESULT_CHECKED_MEMCPY(dest, source, len); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_CHECKED_MEMSET_harness(uint32_t* dest, uint8_t value, size_t len) +{ + RESULT_CHECKED_MEMSET(dest, value, len); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_GUARD_harness(s2n_result result) +{ + RESULT_GUARD(result); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_GUARD_OSSL_harness(int result, int error) +{ + RESULT_GUARD_OSSL(result, error); + return S2N_RESULT_OK; +} + +static int POSIX_BAIL_harness() +{ + POSIX_BAIL(S2N_ERR_SAFETY); + return S2N_SUCCESS; +} + +static int POSIX_ENSURE_harness(bool is_ok) +{ + POSIX_ENSURE(is_ok, S2N_ERR_SAFETY); + return S2N_SUCCESS; +} + +static int POSIX_DEBUG_ENSURE_harness(bool is_ok) +{ + POSIX_DEBUG_ENSURE(is_ok, S2N_ERR_SAFETY); + return S2N_SUCCESS; +} + +static int POSIX_ENSURE_OK_harness(bool is_ok) +{ + POSIX_ENSURE_OK(POSIX_ENSURE_harness(is_ok), S2N_ERR_IO); + return S2N_SUCCESS; +} + +static int POSIX_ENSURE_GTE_harness_uint32(uint32_t a, uint32_t b) +{ + POSIX_ENSURE_GTE(a, b); + /* test the inverse */ + POSIX_ENSURE_LTE(b, a); + return S2N_SUCCESS; +} + +static int POSIX_ENSURE_GTE_harness_int32(int32_t a, int32_t b) +{ + POSIX_ENSURE_GTE(a, b); + /* test the inverse */ + POSIX_ENSURE_LTE(b, a); + return S2N_SUCCESS; +} + +static int POSIX_ENSURE_LTE_harness_uint32(uint32_t a, uint32_t b) +{ + POSIX_ENSURE_LTE(a, b); + /* test the inverse */ + POSIX_ENSURE_GTE(b, a); + return S2N_SUCCESS; +} + +static int POSIX_ENSURE_LTE_harness_int32(int32_t a, int32_t b) +{ + POSIX_ENSURE_LTE(a, b); + /* test the inverse */ + POSIX_ENSURE_GTE(b, a); + return S2N_SUCCESS; +} + +static int POSIX_ENSURE_GT_harness_uint32(uint32_t a, uint32_t b) +{ + POSIX_ENSURE_GT(a, b); + /* test the inverse */ + POSIX_ENSURE_LT(b, a); + return S2N_SUCCESS; +} + +static int POSIX_ENSURE_GT_harness_int32(int32_t a, int32_t b) +{ + POSIX_ENSURE_GT(a, b); + /* test the inverse */ + POSIX_ENSURE_LT(b, a); + return S2N_SUCCESS; +} + +static int POSIX_ENSURE_LT_harness_uint32(uint32_t a, uint32_t b) +{ + POSIX_ENSURE_LT(a, b); + /* test the inverse */ + POSIX_ENSURE_GT(b, a); + return S2N_SUCCESS; +} + +static int POSIX_ENSURE_LT_harness_int32(int32_t a, int32_t b) +{ + POSIX_ENSURE_LT(a, b); + /* test the inverse */ + POSIX_ENSURE_GT(b, a); + return S2N_SUCCESS; +} + +static int POSIX_ENSURE_EQ_harness_uint32(uint32_t a, uint32_t b) +{ + POSIX_ENSURE_EQ(a, b); + POSIX_ENSURE_EQ(b, a); + return S2N_SUCCESS; +} + +static int POSIX_ENSURE_EQ_harness_int32(int32_t a, int32_t b) +{ + POSIX_ENSURE_EQ(a, b); + POSIX_ENSURE_EQ(b, a); + return S2N_SUCCESS; +} + +static int POSIX_ENSURE_NE_harness_uint32(uint32_t a, uint32_t b) +{ + POSIX_ENSURE_NE(a, b); + POSIX_ENSURE_NE(b, a); + return S2N_SUCCESS; +} + +static int POSIX_ENSURE_NE_harness_int32(int32_t a, int32_t b) +{ + POSIX_ENSURE_NE(a, b); + POSIX_ENSURE_NE(b, a); + return S2N_SUCCESS; +} + +static int POSIX_ENSURE_INCLUSIVE_RANGE_harness_uint32(uint32_t a, uint32_t b, uint32_t c) +{ + POSIX_ENSURE_INCLUSIVE_RANGE(a, b, c); + return S2N_SUCCESS; +} + +static int POSIX_ENSURE_INCLUSIVE_RANGE_harness_int32(int32_t a, int32_t b, int32_t c) +{ + POSIX_ENSURE_INCLUSIVE_RANGE(a, b, c); + return S2N_SUCCESS; +} + +static int POSIX_ENSURE_EXCLUSIVE_RANGE_harness_uint32(uint32_t a, uint32_t b, uint32_t c) +{ + POSIX_ENSURE_EXCLUSIVE_RANGE(a, b, c); + return S2N_SUCCESS; +} + +static int POSIX_ENSURE_EXCLUSIVE_RANGE_harness_int32(int32_t a, int32_t b, int32_t c) +{ + POSIX_ENSURE_EXCLUSIVE_RANGE(a, b, c); + return S2N_SUCCESS; +} + +static int POSIX_ENSURE_REF_harness(const char* str) +{ + POSIX_ENSURE_REF(str); + return S2N_SUCCESS; +} + +static int POSIX_ENSURE_MUT_harness(uint32_t* v) +{ + POSIX_ENSURE_MUT(v); + return S2N_SUCCESS; +} + +static S2N_RESULT POSIX_PRECONDITION_harness_check(bool is_ok) +{ + RESULT_ENSURE(is_ok, S2N_ERR_SAFETY); + return S2N_RESULT_OK; +} + +static int POSIX_PRECONDITION_harness(s2n_result result) +{ + POSIX_PRECONDITION(result); + return S2N_SUCCESS; +} + +static S2N_RESULT POSIX_POSTCONDITION_harness_check(bool is_ok) +{ + RESULT_ENSURE(is_ok, S2N_ERR_SAFETY); + return S2N_RESULT_OK; +} + +static int POSIX_POSTCONDITION_harness(s2n_result result) +{ + POSIX_POSTCONDITION(result); + return S2N_SUCCESS; +} + +static int POSIX_CHECKED_MEMCPY_harness(uint32_t* dest, uint32_t* source, size_t len) +{ + POSIX_CHECKED_MEMCPY(dest, source, len); + return S2N_SUCCESS; +} + +static int POSIX_CHECKED_MEMSET_harness(uint32_t* dest, uint8_t value, size_t len) +{ + POSIX_CHECKED_MEMSET(dest, value, len); + return S2N_SUCCESS; +} + +static int POSIX_GUARD_harness(int result) +{ + POSIX_GUARD(result); + return S2N_SUCCESS; +} + +static int POSIX_GUARD_OSSL_harness(int result, int error) +{ + POSIX_GUARD_OSSL(result, error); + return S2N_SUCCESS; +} + +static const char* PTR_BAIL_harness() +{ + PTR_BAIL(S2N_ERR_SAFETY); + return "ok"; +} + +static const char* PTR_ENSURE_harness(bool is_ok) +{ + PTR_ENSURE(is_ok, S2N_ERR_SAFETY); + return "ok"; +} + +static const char* PTR_DEBUG_ENSURE_harness(bool is_ok) +{ + PTR_DEBUG_ENSURE(is_ok, S2N_ERR_SAFETY); + return "ok"; +} + +static const char* PTR_ENSURE_OK_harness(bool is_ok) +{ + PTR_ENSURE_OK(PTR_ENSURE_harness(is_ok), S2N_ERR_IO); + return "ok"; +} + +static const char* PTR_ENSURE_GTE_harness_uint32(uint32_t a, uint32_t b) +{ + PTR_ENSURE_GTE(a, b); + /* test the inverse */ + PTR_ENSURE_LTE(b, a); + return "ok"; +} + +static const char* PTR_ENSURE_GTE_harness_int32(int32_t a, int32_t b) +{ + PTR_ENSURE_GTE(a, b); + /* test the inverse */ + PTR_ENSURE_LTE(b, a); + return "ok"; +} + +static const char* PTR_ENSURE_LTE_harness_uint32(uint32_t a, uint32_t b) +{ + PTR_ENSURE_LTE(a, b); + /* test the inverse */ + PTR_ENSURE_GTE(b, a); + return "ok"; +} + +static const char* PTR_ENSURE_LTE_harness_int32(int32_t a, int32_t b) +{ + PTR_ENSURE_LTE(a, b); + /* test the inverse */ + PTR_ENSURE_GTE(b, a); + return "ok"; +} + +static const char* PTR_ENSURE_GT_harness_uint32(uint32_t a, uint32_t b) +{ + PTR_ENSURE_GT(a, b); + /* test the inverse */ + PTR_ENSURE_LT(b, a); + return "ok"; +} + +static const char* PTR_ENSURE_GT_harness_int32(int32_t a, int32_t b) +{ + PTR_ENSURE_GT(a, b); + /* test the inverse */ + PTR_ENSURE_LT(b, a); + return "ok"; +} + +static const char* PTR_ENSURE_LT_harness_uint32(uint32_t a, uint32_t b) +{ + PTR_ENSURE_LT(a, b); + /* test the inverse */ + PTR_ENSURE_GT(b, a); + return "ok"; +} + +static const char* PTR_ENSURE_LT_harness_int32(int32_t a, int32_t b) +{ + PTR_ENSURE_LT(a, b); + /* test the inverse */ + PTR_ENSURE_GT(b, a); + return "ok"; +} + +static const char* PTR_ENSURE_EQ_harness_uint32(uint32_t a, uint32_t b) +{ + PTR_ENSURE_EQ(a, b); + PTR_ENSURE_EQ(b, a); + return "ok"; +} + +static const char* PTR_ENSURE_EQ_harness_int32(int32_t a, int32_t b) +{ + PTR_ENSURE_EQ(a, b); + PTR_ENSURE_EQ(b, a); + return "ok"; +} + +static const char* PTR_ENSURE_NE_harness_uint32(uint32_t a, uint32_t b) +{ + PTR_ENSURE_NE(a, b); + PTR_ENSURE_NE(b, a); + return "ok"; +} + +static const char* PTR_ENSURE_NE_harness_int32(int32_t a, int32_t b) +{ + PTR_ENSURE_NE(a, b); + PTR_ENSURE_NE(b, a); + return "ok"; +} + +static const char* PTR_ENSURE_INCLUSIVE_RANGE_harness_uint32(uint32_t a, uint32_t b, uint32_t c) +{ + PTR_ENSURE_INCLUSIVE_RANGE(a, b, c); + return "ok"; +} + +static const char* PTR_ENSURE_INCLUSIVE_RANGE_harness_int32(int32_t a, int32_t b, int32_t c) +{ + PTR_ENSURE_INCLUSIVE_RANGE(a, b, c); + return "ok"; +} + +static const char* PTR_ENSURE_EXCLUSIVE_RANGE_harness_uint32(uint32_t a, uint32_t b, uint32_t c) +{ + PTR_ENSURE_EXCLUSIVE_RANGE(a, b, c); + return "ok"; +} + +static const char* PTR_ENSURE_EXCLUSIVE_RANGE_harness_int32(int32_t a, int32_t b, int32_t c) +{ + PTR_ENSURE_EXCLUSIVE_RANGE(a, b, c); + return "ok"; +} + +static const char* PTR_ENSURE_REF_harness(const char* str) +{ + PTR_ENSURE_REF(str); + return "ok"; +} + +static const char* PTR_ENSURE_MUT_harness(uint32_t* v) +{ + PTR_ENSURE_MUT(v); + return "ok"; +} + +static S2N_RESULT PTR_PRECONDITION_harness_check(bool is_ok) +{ + RESULT_ENSURE(is_ok, S2N_ERR_SAFETY); + return S2N_RESULT_OK; +} + +static const char* PTR_PRECONDITION_harness(s2n_result result) +{ + PTR_PRECONDITION(result); + return "ok"; +} + +static S2N_RESULT PTR_POSTCONDITION_harness_check(bool is_ok) +{ + RESULT_ENSURE(is_ok, S2N_ERR_SAFETY); + return S2N_RESULT_OK; +} + +static const char* PTR_POSTCONDITION_harness(s2n_result result) +{ + PTR_POSTCONDITION(result); + return "ok"; +} + +static const char* PTR_CHECKED_MEMCPY_harness(uint32_t* dest, uint32_t* source, size_t len) +{ + PTR_CHECKED_MEMCPY(dest, source, len); + return "ok"; +} + +static const char* PTR_CHECKED_MEMSET_harness(uint32_t* dest, uint8_t value, size_t len) +{ + PTR_CHECKED_MEMSET(dest, value, len); + return "ok"; +} + +static const char* PTR_GUARD_harness(const char* result) +{ + PTR_GUARD(result); + return "ok"; +} + +static const char* PTR_GUARD_OSSL_harness(int result, int error) +{ + PTR_GUARD_OSSL(result, error); + return "ok"; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* RESULT_BAIL(error) */ + EXPECT_ERROR_WITH_ERRNO(RESULT_BAIL_harness(), S2N_ERR_SAFETY); + + /* RESULT_ENSURE(condition, error) */ + EXPECT_OK(RESULT_ENSURE_harness(true)); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_harness(false), S2N_ERR_SAFETY); + + /* RESULT_DEBUG_ENSURE(condition, error) */ + EXPECT_OK(RESULT_DEBUG_ENSURE_harness(true)); + #ifdef NDEBUG + EXPECT_OK(RESULT_DEBUG_ENSURE_harness(false)); + #else + EXPECT_ERROR_WITH_ERRNO(RESULT_DEBUG_ENSURE_harness(false), S2N_ERR_SAFETY); + #endif + + /* RESULT_ENSURE_OK(result, error) */ + EXPECT_OK(RESULT_ENSURE_OK_harness(true)); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_OK_harness(false), S2N_ERR_IO); + + /* RESULT_ENSURE_GTE(a, b) */ + EXPECT_OK(RESULT_ENSURE_GTE_harness_uint32(0, 0)); + EXPECT_OK(RESULT_ENSURE_GTE_harness_uint32(1, 0)); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_GTE_harness_uint32(0, 1), S2N_ERR_SAFETY); + EXPECT_OK(RESULT_ENSURE_GTE_harness_int32(-1, -2)); + EXPECT_OK(RESULT_ENSURE_GTE_harness_int32(-1, -1)); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_GTE_harness_int32(-2, -1), S2N_ERR_SAFETY); + + /* RESULT_ENSURE_LTE(a, b) */ + EXPECT_OK(RESULT_ENSURE_LTE_harness_uint32(0, 0)); + EXPECT_OK(RESULT_ENSURE_LTE_harness_uint32(0, 1)); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_LTE_harness_uint32(1, 0), S2N_ERR_SAFETY); + EXPECT_OK(RESULT_ENSURE_LTE_harness_int32(-2, -1)); + EXPECT_OK(RESULT_ENSURE_LTE_harness_int32(-1, -1)); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_LTE_harness_int32(-1, -2), S2N_ERR_SAFETY); + + /* RESULT_ENSURE_GT(a, b) */ + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_GT_harness_uint32(0, 0), S2N_ERR_SAFETY); + EXPECT_OK(RESULT_ENSURE_GT_harness_uint32(1, 0)); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_GT_harness_uint32(0, 1), S2N_ERR_SAFETY); + EXPECT_OK(RESULT_ENSURE_GT_harness_int32(-1, -2)); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_GT_harness_int32(-1, -1), S2N_ERR_SAFETY); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_GT_harness_int32(-2, -1), S2N_ERR_SAFETY); + + /* RESULT_ENSURE_LT(a, b) */ + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_LT_harness_uint32(0, 0), S2N_ERR_SAFETY); + EXPECT_OK(RESULT_ENSURE_LT_harness_uint32(0, 1)); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_LT_harness_uint32(1, 0), S2N_ERR_SAFETY); + EXPECT_OK(RESULT_ENSURE_LT_harness_int32(-2, -1)); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_LT_harness_int32(-1, -1), S2N_ERR_SAFETY); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_LT_harness_int32(-1, -2), S2N_ERR_SAFETY); + + /* RESULT_ENSURE_EQ(a, b) */ + EXPECT_OK(RESULT_ENSURE_EQ_harness_uint32(0, 0)); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_EQ_harness_uint32(1, 0), S2N_ERR_SAFETY); + EXPECT_OK(RESULT_ENSURE_EQ_harness_int32(-1, -1)); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_EQ_harness_int32(-2, -1), S2N_ERR_SAFETY); + + /* RESULT_ENSURE_NE(a, b) */ + EXPECT_OK(RESULT_ENSURE_NE_harness_uint32(1, 0)); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_NE_harness_uint32(0, 0), S2N_ERR_SAFETY); + EXPECT_OK(RESULT_ENSURE_NE_harness_int32(-2, -1)); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_NE_harness_int32(-1, -1), S2N_ERR_SAFETY); + + /* RESULT_ENSURE_INCLUSIVE_RANGE(min, n, max) */ + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_INCLUSIVE_RANGE_harness_uint32(1, 0, 2), S2N_ERR_SAFETY); + EXPECT_OK(RESULT_ENSURE_INCLUSIVE_RANGE_harness_uint32(1, 1, 2)); + EXPECT_OK(RESULT_ENSURE_INCLUSIVE_RANGE_harness_uint32(1, 2, 2)); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_INCLUSIVE_RANGE_harness_uint32(1, 3, 2), S2N_ERR_SAFETY); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_INCLUSIVE_RANGE_harness_int32(-2, -3, -1), S2N_ERR_SAFETY); + EXPECT_OK(RESULT_ENSURE_INCLUSIVE_RANGE_harness_int32(-2, -2, -1)); + EXPECT_OK(RESULT_ENSURE_INCLUSIVE_RANGE_harness_int32(-2, -1, -1)); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_INCLUSIVE_RANGE_harness_int32(-2, 0, -1), S2N_ERR_SAFETY); + + /* RESULT_ENSURE_EXCLUSIVE_RANGE(min, n, max) */ + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_EXCLUSIVE_RANGE_harness_uint32(1, 0, 3), S2N_ERR_SAFETY); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_EXCLUSIVE_RANGE_harness_uint32(1, 1, 3), S2N_ERR_SAFETY); + EXPECT_OK(RESULT_ENSURE_EXCLUSIVE_RANGE_harness_uint32(1, 2, 3)); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_EXCLUSIVE_RANGE_harness_uint32(1, 3, 3), S2N_ERR_SAFETY); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_EXCLUSIVE_RANGE_harness_uint32(1, 4, 3), S2N_ERR_SAFETY); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_EXCLUSIVE_RANGE_harness_int32(-3, -4, -1), S2N_ERR_SAFETY); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_EXCLUSIVE_RANGE_harness_int32(-3, -3, -1), S2N_ERR_SAFETY); + EXPECT_OK(RESULT_ENSURE_EXCLUSIVE_RANGE_harness_int32(-3, -2, -1)); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_EXCLUSIVE_RANGE_harness_int32(-3, -1, -1), S2N_ERR_SAFETY); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_EXCLUSIVE_RANGE_harness_int32(-3, 0, -1), S2N_ERR_SAFETY); + + /* RESULT_ENSURE_REF(x) */ + EXPECT_OK(RESULT_ENSURE_REF_harness("")); + EXPECT_OK(RESULT_ENSURE_REF_harness("ok")); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_REF_harness(NULL), S2N_ERR_NULL); + + /* RESULT_ENSURE_MUT(x) */ + uint32_t RESULT_ensure_mut_test = 0; + EXPECT_OK(RESULT_ENSURE_MUT_harness(&RESULT_ensure_mut_test)); + RESULT_ensure_mut_test = 1; + EXPECT_OK(RESULT_ENSURE_MUT_harness(&RESULT_ensure_mut_test)); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_MUT_harness(NULL), S2N_ERR_NULL); + + /* RESULT_PRECONDITION(result) */ + EXPECT_OK(RESULT_PRECONDITION_harness(RESULT_PRECONDITION_harness_check(true))); + EXPECT_ERROR_WITH_ERRNO(RESULT_PRECONDITION_harness(RESULT_PRECONDITION_harness_check(false)), S2N_ERR_SAFETY); + + /* RESULT_POSTCONDITION(result) */ + EXPECT_OK(RESULT_POSTCONDITION_harness(RESULT_POSTCONDITION_harness_check(true))); + #ifdef NDEBUG + EXPECT_OK(RESULT_POSTCONDITION_harness(RESULT_POSTCONDITION_harness_check(false))); + #else + EXPECT_ERROR_WITH_ERRNO(RESULT_POSTCONDITION_harness(RESULT_POSTCONDITION_harness_check(false)), S2N_ERR_SAFETY); + #endif + + /* RESULT_CHECKED_MEMCPY(destination, source, len) */ + uint32_t RESULT__checked_memcpy_dest = 1; + uint32_t RESULT__checked_memcpy_source = 2; + EXPECT_OK(RESULT_CHECKED_MEMCPY_harness(&RESULT__checked_memcpy_dest, &RESULT__checked_memcpy_source, 0)); + EXPECT_EQUAL(RESULT__checked_memcpy_dest, 1); + EXPECT_ERROR_WITH_ERRNO(RESULT_CHECKED_MEMCPY_harness(NULL, &RESULT__checked_memcpy_source, 4), S2N_ERR_NULL); + EXPECT_ERROR_WITH_ERRNO(RESULT_CHECKED_MEMCPY_harness(&RESULT__checked_memcpy_dest, NULL, 4), S2N_ERR_NULL); + EXPECT_OK(RESULT_CHECKED_MEMCPY_harness(&RESULT__checked_memcpy_dest, &RESULT__checked_memcpy_source, 4)); + EXPECT_EQUAL(RESULT__checked_memcpy_dest, RESULT__checked_memcpy_source); + + /* RESULT_CHECKED_MEMSET(destination, value, len) */ + uint32_t RESULT__checked_memset_dest = 1; + EXPECT_OK(RESULT_CHECKED_MEMSET_harness(&RESULT__checked_memset_dest, 0x42, 0)); + EXPECT_EQUAL(RESULT__checked_memset_dest, 1); + EXPECT_ERROR_WITH_ERRNO(RESULT_CHECKED_MEMSET_harness(NULL, 0x42, 1), S2N_ERR_NULL); + EXPECT_OK(RESULT_CHECKED_MEMSET_harness(&RESULT__checked_memset_dest, 0x42, 4)); + EXPECT_EQUAL(RESULT__checked_memset_dest, 0x42424242); + + /* RESULT_GUARD(result) */ + EXPECT_OK(RESULT_GUARD_harness(RESULT_ENSURE_harness(true))); + EXPECT_ERROR_WITH_ERRNO(RESULT_GUARD_harness(RESULT_ENSURE_harness(false)), S2N_ERR_SAFETY); + + /* RESULT_GUARD_OSSL(result, error) */ + EXPECT_OK(RESULT_GUARD_OSSL_harness(1, S2N_ERR_SAFETY)); + EXPECT_ERROR_WITH_ERRNO(RESULT_GUARD_OSSL_harness(0, S2N_ERR_SAFETY), S2N_ERR_SAFETY); + + /* POSIX_BAIL(error) */ + EXPECT_FAILURE_WITH_ERRNO(POSIX_BAIL_harness(), S2N_ERR_SAFETY); + + /* POSIX_ENSURE(condition, error) */ + EXPECT_SUCCESS(POSIX_ENSURE_harness(true)); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_harness(false), S2N_ERR_SAFETY); + + /* POSIX_DEBUG_ENSURE(condition, error) */ + EXPECT_SUCCESS(POSIX_DEBUG_ENSURE_harness(true)); + #ifdef NDEBUG + EXPECT_SUCCESS(POSIX_DEBUG_ENSURE_harness(false)); + #else + EXPECT_FAILURE_WITH_ERRNO(POSIX_DEBUG_ENSURE_harness(false), S2N_ERR_SAFETY); + #endif + + /* POSIX_ENSURE_OK(result, error) */ + EXPECT_SUCCESS(POSIX_ENSURE_OK_harness(true)); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_OK_harness(false), S2N_ERR_IO); + + /* POSIX_ENSURE_GTE(a, b) */ + EXPECT_SUCCESS(POSIX_ENSURE_GTE_harness_uint32(0, 0)); + EXPECT_SUCCESS(POSIX_ENSURE_GTE_harness_uint32(1, 0)); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_GTE_harness_uint32(0, 1), S2N_ERR_SAFETY); + EXPECT_SUCCESS(POSIX_ENSURE_GTE_harness_int32(-1, -2)); + EXPECT_SUCCESS(POSIX_ENSURE_GTE_harness_int32(-1, -1)); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_GTE_harness_int32(-2, -1), S2N_ERR_SAFETY); + + /* POSIX_ENSURE_LTE(a, b) */ + EXPECT_SUCCESS(POSIX_ENSURE_LTE_harness_uint32(0, 0)); + EXPECT_SUCCESS(POSIX_ENSURE_LTE_harness_uint32(0, 1)); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_LTE_harness_uint32(1, 0), S2N_ERR_SAFETY); + EXPECT_SUCCESS(POSIX_ENSURE_LTE_harness_int32(-2, -1)); + EXPECT_SUCCESS(POSIX_ENSURE_LTE_harness_int32(-1, -1)); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_LTE_harness_int32(-1, -2), S2N_ERR_SAFETY); + + /* POSIX_ENSURE_GT(a, b) */ + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_GT_harness_uint32(0, 0), S2N_ERR_SAFETY); + EXPECT_SUCCESS(POSIX_ENSURE_GT_harness_uint32(1, 0)); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_GT_harness_uint32(0, 1), S2N_ERR_SAFETY); + EXPECT_SUCCESS(POSIX_ENSURE_GT_harness_int32(-1, -2)); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_GT_harness_int32(-1, -1), S2N_ERR_SAFETY); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_GT_harness_int32(-2, -1), S2N_ERR_SAFETY); + + /* POSIX_ENSURE_LT(a, b) */ + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_LT_harness_uint32(0, 0), S2N_ERR_SAFETY); + EXPECT_SUCCESS(POSIX_ENSURE_LT_harness_uint32(0, 1)); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_LT_harness_uint32(1, 0), S2N_ERR_SAFETY); + EXPECT_SUCCESS(POSIX_ENSURE_LT_harness_int32(-2, -1)); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_LT_harness_int32(-1, -1), S2N_ERR_SAFETY); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_LT_harness_int32(-1, -2), S2N_ERR_SAFETY); + + /* POSIX_ENSURE_EQ(a, b) */ + EXPECT_SUCCESS(POSIX_ENSURE_EQ_harness_uint32(0, 0)); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_EQ_harness_uint32(1, 0), S2N_ERR_SAFETY); + EXPECT_SUCCESS(POSIX_ENSURE_EQ_harness_int32(-1, -1)); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_EQ_harness_int32(-2, -1), S2N_ERR_SAFETY); + + /* POSIX_ENSURE_NE(a, b) */ + EXPECT_SUCCESS(POSIX_ENSURE_NE_harness_uint32(1, 0)); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_NE_harness_uint32(0, 0), S2N_ERR_SAFETY); + EXPECT_SUCCESS(POSIX_ENSURE_NE_harness_int32(-2, -1)); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_NE_harness_int32(-1, -1), S2N_ERR_SAFETY); + + /* POSIX_ENSURE_INCLUSIVE_RANGE(min, n, max) */ + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_INCLUSIVE_RANGE_harness_uint32(1, 0, 2), S2N_ERR_SAFETY); + EXPECT_SUCCESS(POSIX_ENSURE_INCLUSIVE_RANGE_harness_uint32(1, 1, 2)); + EXPECT_SUCCESS(POSIX_ENSURE_INCLUSIVE_RANGE_harness_uint32(1, 2, 2)); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_INCLUSIVE_RANGE_harness_uint32(1, 3, 2), S2N_ERR_SAFETY); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_INCLUSIVE_RANGE_harness_int32(-2, -3, -1), S2N_ERR_SAFETY); + EXPECT_SUCCESS(POSIX_ENSURE_INCLUSIVE_RANGE_harness_int32(-2, -2, -1)); + EXPECT_SUCCESS(POSIX_ENSURE_INCLUSIVE_RANGE_harness_int32(-2, -1, -1)); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_INCLUSIVE_RANGE_harness_int32(-2, 0, -1), S2N_ERR_SAFETY); + + /* POSIX_ENSURE_EXCLUSIVE_RANGE(min, n, max) */ + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_EXCLUSIVE_RANGE_harness_uint32(1, 0, 3), S2N_ERR_SAFETY); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_EXCLUSIVE_RANGE_harness_uint32(1, 1, 3), S2N_ERR_SAFETY); + EXPECT_SUCCESS(POSIX_ENSURE_EXCLUSIVE_RANGE_harness_uint32(1, 2, 3)); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_EXCLUSIVE_RANGE_harness_uint32(1, 3, 3), S2N_ERR_SAFETY); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_EXCLUSIVE_RANGE_harness_uint32(1, 4, 3), S2N_ERR_SAFETY); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_EXCLUSIVE_RANGE_harness_int32(-3, -4, -1), S2N_ERR_SAFETY); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_EXCLUSIVE_RANGE_harness_int32(-3, -3, -1), S2N_ERR_SAFETY); + EXPECT_SUCCESS(POSIX_ENSURE_EXCLUSIVE_RANGE_harness_int32(-3, -2, -1)); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_EXCLUSIVE_RANGE_harness_int32(-3, -1, -1), S2N_ERR_SAFETY); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_EXCLUSIVE_RANGE_harness_int32(-3, 0, -1), S2N_ERR_SAFETY); + + /* POSIX_ENSURE_REF(x) */ + EXPECT_SUCCESS(POSIX_ENSURE_REF_harness("")); + EXPECT_SUCCESS(POSIX_ENSURE_REF_harness("ok")); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_REF_harness(NULL), S2N_ERR_NULL); + + /* POSIX_ENSURE_MUT(x) */ + uint32_t POSIX_ensure_mut_test = 0; + EXPECT_SUCCESS(POSIX_ENSURE_MUT_harness(&POSIX_ensure_mut_test)); + POSIX_ensure_mut_test = 1; + EXPECT_SUCCESS(POSIX_ENSURE_MUT_harness(&POSIX_ensure_mut_test)); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_MUT_harness(NULL), S2N_ERR_NULL); + + /* POSIX_PRECONDITION(result) */ + EXPECT_SUCCESS(POSIX_PRECONDITION_harness(POSIX_PRECONDITION_harness_check(true))); + EXPECT_FAILURE_WITH_ERRNO(POSIX_PRECONDITION_harness(POSIX_PRECONDITION_harness_check(false)), S2N_ERR_SAFETY); + + /* POSIX_POSTCONDITION(result) */ + EXPECT_SUCCESS(POSIX_POSTCONDITION_harness(POSIX_POSTCONDITION_harness_check(true))); + #ifdef NDEBUG + EXPECT_SUCCESS(POSIX_POSTCONDITION_harness(POSIX_POSTCONDITION_harness_check(false))); + #else + EXPECT_FAILURE_WITH_ERRNO(POSIX_POSTCONDITION_harness(POSIX_POSTCONDITION_harness_check(false)), S2N_ERR_SAFETY); + #endif + + /* POSIX_CHECKED_MEMCPY(destination, source, len) */ + uint32_t POSIX__checked_memcpy_dest = 1; + uint32_t POSIX__checked_memcpy_source = 2; + EXPECT_SUCCESS(POSIX_CHECKED_MEMCPY_harness(&POSIX__checked_memcpy_dest, &POSIX__checked_memcpy_source, 0)); + EXPECT_EQUAL(POSIX__checked_memcpy_dest, 1); + EXPECT_FAILURE_WITH_ERRNO(POSIX_CHECKED_MEMCPY_harness(NULL, &POSIX__checked_memcpy_source, 4), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(POSIX_CHECKED_MEMCPY_harness(&POSIX__checked_memcpy_dest, NULL, 4), S2N_ERR_NULL); + EXPECT_SUCCESS(POSIX_CHECKED_MEMCPY_harness(&POSIX__checked_memcpy_dest, &POSIX__checked_memcpy_source, 4)); + EXPECT_EQUAL(POSIX__checked_memcpy_dest, POSIX__checked_memcpy_source); + + /* POSIX_CHECKED_MEMSET(destination, value, len) */ + uint32_t POSIX__checked_memset_dest = 1; + EXPECT_SUCCESS(POSIX_CHECKED_MEMSET_harness(&POSIX__checked_memset_dest, 0x42, 0)); + EXPECT_EQUAL(POSIX__checked_memset_dest, 1); + EXPECT_FAILURE_WITH_ERRNO(POSIX_CHECKED_MEMSET_harness(NULL, 0x42, 1), S2N_ERR_NULL); + EXPECT_SUCCESS(POSIX_CHECKED_MEMSET_harness(&POSIX__checked_memset_dest, 0x42, 4)); + EXPECT_EQUAL(POSIX__checked_memset_dest, 0x42424242); + + /* POSIX_GUARD(result) */ + EXPECT_SUCCESS(POSIX_GUARD_harness(POSIX_ENSURE_harness(true))); + EXPECT_FAILURE_WITH_ERRNO(POSIX_GUARD_harness(POSIX_ENSURE_harness(false)), S2N_ERR_SAFETY); + + /* POSIX_GUARD_OSSL(result, error) */ + EXPECT_SUCCESS(POSIX_GUARD_OSSL_harness(1, S2N_ERR_SAFETY)); + EXPECT_FAILURE_WITH_ERRNO(POSIX_GUARD_OSSL_harness(0, S2N_ERR_SAFETY), S2N_ERR_SAFETY); + + /* PTR_BAIL(error) */ + EXPECT_NULL_WITH_ERRNO(PTR_BAIL_harness(), S2N_ERR_SAFETY); + + /* PTR_ENSURE(condition, error) */ + EXPECT_NOT_NULL(PTR_ENSURE_harness(true)); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_harness(false), S2N_ERR_SAFETY); + + /* PTR_DEBUG_ENSURE(condition, error) */ + EXPECT_NOT_NULL(PTR_DEBUG_ENSURE_harness(true)); + #ifdef NDEBUG + EXPECT_NOT_NULL(PTR_DEBUG_ENSURE_harness(false)); + #else + EXPECT_NULL_WITH_ERRNO(PTR_DEBUG_ENSURE_harness(false), S2N_ERR_SAFETY); + #endif + + /* PTR_ENSURE_OK(result, error) */ + EXPECT_NOT_NULL(PTR_ENSURE_OK_harness(true)); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_OK_harness(false), S2N_ERR_IO); + + /* PTR_ENSURE_GTE(a, b) */ + EXPECT_NOT_NULL(PTR_ENSURE_GTE_harness_uint32(0, 0)); + EXPECT_NOT_NULL(PTR_ENSURE_GTE_harness_uint32(1, 0)); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_GTE_harness_uint32(0, 1), S2N_ERR_SAFETY); + EXPECT_NOT_NULL(PTR_ENSURE_GTE_harness_int32(-1, -2)); + EXPECT_NOT_NULL(PTR_ENSURE_GTE_harness_int32(-1, -1)); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_GTE_harness_int32(-2, -1), S2N_ERR_SAFETY); + + /* PTR_ENSURE_LTE(a, b) */ + EXPECT_NOT_NULL(PTR_ENSURE_LTE_harness_uint32(0, 0)); + EXPECT_NOT_NULL(PTR_ENSURE_LTE_harness_uint32(0, 1)); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_LTE_harness_uint32(1, 0), S2N_ERR_SAFETY); + EXPECT_NOT_NULL(PTR_ENSURE_LTE_harness_int32(-2, -1)); + EXPECT_NOT_NULL(PTR_ENSURE_LTE_harness_int32(-1, -1)); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_LTE_harness_int32(-1, -2), S2N_ERR_SAFETY); + + /* PTR_ENSURE_GT(a, b) */ + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_GT_harness_uint32(0, 0), S2N_ERR_SAFETY); + EXPECT_NOT_NULL(PTR_ENSURE_GT_harness_uint32(1, 0)); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_GT_harness_uint32(0, 1), S2N_ERR_SAFETY); + EXPECT_NOT_NULL(PTR_ENSURE_GT_harness_int32(-1, -2)); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_GT_harness_int32(-1, -1), S2N_ERR_SAFETY); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_GT_harness_int32(-2, -1), S2N_ERR_SAFETY); + + /* PTR_ENSURE_LT(a, b) */ + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_LT_harness_uint32(0, 0), S2N_ERR_SAFETY); + EXPECT_NOT_NULL(PTR_ENSURE_LT_harness_uint32(0, 1)); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_LT_harness_uint32(1, 0), S2N_ERR_SAFETY); + EXPECT_NOT_NULL(PTR_ENSURE_LT_harness_int32(-2, -1)); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_LT_harness_int32(-1, -1), S2N_ERR_SAFETY); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_LT_harness_int32(-1, -2), S2N_ERR_SAFETY); + + /* PTR_ENSURE_EQ(a, b) */ + EXPECT_NOT_NULL(PTR_ENSURE_EQ_harness_uint32(0, 0)); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_EQ_harness_uint32(1, 0), S2N_ERR_SAFETY); + EXPECT_NOT_NULL(PTR_ENSURE_EQ_harness_int32(-1, -1)); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_EQ_harness_int32(-2, -1), S2N_ERR_SAFETY); + + /* PTR_ENSURE_NE(a, b) */ + EXPECT_NOT_NULL(PTR_ENSURE_NE_harness_uint32(1, 0)); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_NE_harness_uint32(0, 0), S2N_ERR_SAFETY); + EXPECT_NOT_NULL(PTR_ENSURE_NE_harness_int32(-2, -1)); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_NE_harness_int32(-1, -1), S2N_ERR_SAFETY); + + /* PTR_ENSURE_INCLUSIVE_RANGE(min, n, max) */ + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_INCLUSIVE_RANGE_harness_uint32(1, 0, 2), S2N_ERR_SAFETY); + EXPECT_NOT_NULL(PTR_ENSURE_INCLUSIVE_RANGE_harness_uint32(1, 1, 2)); + EXPECT_NOT_NULL(PTR_ENSURE_INCLUSIVE_RANGE_harness_uint32(1, 2, 2)); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_INCLUSIVE_RANGE_harness_uint32(1, 3, 2), S2N_ERR_SAFETY); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_INCLUSIVE_RANGE_harness_int32(-2, -3, -1), S2N_ERR_SAFETY); + EXPECT_NOT_NULL(PTR_ENSURE_INCLUSIVE_RANGE_harness_int32(-2, -2, -1)); + EXPECT_NOT_NULL(PTR_ENSURE_INCLUSIVE_RANGE_harness_int32(-2, -1, -1)); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_INCLUSIVE_RANGE_harness_int32(-2, 0, -1), S2N_ERR_SAFETY); + + /* PTR_ENSURE_EXCLUSIVE_RANGE(min, n, max) */ + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_EXCLUSIVE_RANGE_harness_uint32(1, 0, 3), S2N_ERR_SAFETY); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_EXCLUSIVE_RANGE_harness_uint32(1, 1, 3), S2N_ERR_SAFETY); + EXPECT_NOT_NULL(PTR_ENSURE_EXCLUSIVE_RANGE_harness_uint32(1, 2, 3)); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_EXCLUSIVE_RANGE_harness_uint32(1, 3, 3), S2N_ERR_SAFETY); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_EXCLUSIVE_RANGE_harness_uint32(1, 4, 3), S2N_ERR_SAFETY); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_EXCLUSIVE_RANGE_harness_int32(-3, -4, -1), S2N_ERR_SAFETY); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_EXCLUSIVE_RANGE_harness_int32(-3, -3, -1), S2N_ERR_SAFETY); + EXPECT_NOT_NULL(PTR_ENSURE_EXCLUSIVE_RANGE_harness_int32(-3, -2, -1)); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_EXCLUSIVE_RANGE_harness_int32(-3, -1, -1), S2N_ERR_SAFETY); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_EXCLUSIVE_RANGE_harness_int32(-3, 0, -1), S2N_ERR_SAFETY); + + /* PTR_ENSURE_REF(x) */ + EXPECT_NOT_NULL(PTR_ENSURE_REF_harness("")); + EXPECT_NOT_NULL(PTR_ENSURE_REF_harness("ok")); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_REF_harness(NULL), S2N_ERR_NULL); + + /* PTR_ENSURE_MUT(x) */ + uint32_t PTR_ensure_mut_test = 0; + EXPECT_NOT_NULL(PTR_ENSURE_MUT_harness(&PTR_ensure_mut_test)); + PTR_ensure_mut_test = 1; + EXPECT_NOT_NULL(PTR_ENSURE_MUT_harness(&PTR_ensure_mut_test)); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_MUT_harness(NULL), S2N_ERR_NULL); + + /* PTR_PRECONDITION(result) */ + EXPECT_NOT_NULL(PTR_PRECONDITION_harness(PTR_PRECONDITION_harness_check(true))); + EXPECT_NULL_WITH_ERRNO(PTR_PRECONDITION_harness(PTR_PRECONDITION_harness_check(false)), S2N_ERR_SAFETY); + + /* PTR_POSTCONDITION(result) */ + EXPECT_NOT_NULL(PTR_POSTCONDITION_harness(PTR_POSTCONDITION_harness_check(true))); + #ifdef NDEBUG + EXPECT_NOT_NULL(PTR_POSTCONDITION_harness(PTR_POSTCONDITION_harness_check(false))); + #else + EXPECT_NULL_WITH_ERRNO(PTR_POSTCONDITION_harness(PTR_POSTCONDITION_harness_check(false)), S2N_ERR_SAFETY); + #endif + + /* PTR_CHECKED_MEMCPY(destination, source, len) */ + uint32_t PTR__checked_memcpy_dest = 1; + uint32_t PTR__checked_memcpy_source = 2; + EXPECT_NOT_NULL(PTR_CHECKED_MEMCPY_harness(&PTR__checked_memcpy_dest, &PTR__checked_memcpy_source, 0)); + EXPECT_EQUAL(PTR__checked_memcpy_dest, 1); + EXPECT_NULL_WITH_ERRNO(PTR_CHECKED_MEMCPY_harness(NULL, &PTR__checked_memcpy_source, 4), S2N_ERR_NULL); + EXPECT_NULL_WITH_ERRNO(PTR_CHECKED_MEMCPY_harness(&PTR__checked_memcpy_dest, NULL, 4), S2N_ERR_NULL); + EXPECT_NOT_NULL(PTR_CHECKED_MEMCPY_harness(&PTR__checked_memcpy_dest, &PTR__checked_memcpy_source, 4)); + EXPECT_EQUAL(PTR__checked_memcpy_dest, PTR__checked_memcpy_source); + + /* PTR_CHECKED_MEMSET(destination, value, len) */ + uint32_t PTR__checked_memset_dest = 1; + EXPECT_NOT_NULL(PTR_CHECKED_MEMSET_harness(&PTR__checked_memset_dest, 0x42, 0)); + EXPECT_EQUAL(PTR__checked_memset_dest, 1); + EXPECT_NULL_WITH_ERRNO(PTR_CHECKED_MEMSET_harness(NULL, 0x42, 1), S2N_ERR_NULL); + EXPECT_NOT_NULL(PTR_CHECKED_MEMSET_harness(&PTR__checked_memset_dest, 0x42, 4)); + EXPECT_EQUAL(PTR__checked_memset_dest, 0x42424242); + + /* PTR_GUARD(result) */ + EXPECT_NOT_NULL(PTR_GUARD_harness(PTR_ENSURE_harness(true))); + EXPECT_NULL_WITH_ERRNO(PTR_GUARD_harness(PTR_ENSURE_harness(false)), S2N_ERR_SAFETY); + + /* PTR_GUARD_OSSL(result, error) */ + EXPECT_NOT_NULL(PTR_GUARD_OSSL_harness(1, S2N_ERR_SAFETY)); + EXPECT_NULL_WITH_ERRNO(PTR_GUARD_OSSL_harness(0, S2N_ERR_SAFETY), S2N_ERR_SAFETY); + + + END_TEST(); + return S2N_SUCCESS; +} diff --git a/utils/s2n_ensure.c b/utils/s2n_ensure.c index 4cfe7e40c9f..910844ae9e9 100644 --- a/utils/s2n_ensure.c +++ b/utils/s2n_ensure.c @@ -23,5 +23,6 @@ void* s2n_ensure_memcpy_trace(void *restrict to, const void *restrict from, size return NULL; } - return memcpy(to, from, size); + /* use memmove instead of memcpy since it'll handle overlapping regions and not result in UB */ + return memmove(to, from, size); } diff --git a/utils/s2n_safety.h b/utils/s2n_safety.h index 0cebbdaae1b..b950c2dec10 100644 --- a/utils/s2n_safety.h +++ b/utils/s2n_safety.h @@ -24,394 +24,344 @@ #include "error/s2n_errno.h" #include "utils/s2n_ensure.h" #include "utils/s2n_result.h" - -/* Success signal value for OpenSSL functions */ -#define _OSSL_SUCCESS 1 +#include "utils/s2n_safety_macros.h" /** * The goal of s2n_safety is to provide helpers to perform common * checks, which help with code readability. */ +/** + * Marks a case of a switch statement as able to fall through to the next case + */ +#if defined(S2N_FALL_THROUGH_SUPPORTED) +# define FALL_THROUGH __attribute__((fallthrough)) +#else +# define FALL_THROUGH ((void)0) +#endif + +/* Returns `true` if s2n is in unit test mode, `false` otherwise */ +bool s2n_in_unit_test(); + +/* Sets whether s2n is in unit test mode */ +int s2n_in_unit_test_set(bool newval); + +#define S2N_IN_INTEG_TEST ( getenv("S2N_INTEG_TEST") != NULL ) +#define S2N_IN_TEST ( s2n_in_unit_test() || S2N_IN_INTEG_TEST ) + +/** + * Get the process id + * + * Returns: + * The process ID of the current process + */ +extern pid_t s2n_actual_getpid(); + +/* Returns 1 if a and b are equal, in constant time */ +extern bool s2n_constant_time_equals(const uint8_t * a, const uint8_t * b, const uint32_t len); + +/* Copy src to dst, or don't copy it, in constant time */ +extern int s2n_constant_time_copy_or_dont(uint8_t * dst, const uint8_t * src, uint32_t len, uint8_t dont); + +/* If src contains valid PKCS#1 v1.5 padding of exactly expectlen bytes, decode + * it into dst, otherwise leave dst alone, in constant time. + * Always returns zero. */ +extern int s2n_constant_time_pkcs1_unpad_or_dont(uint8_t * dst, const uint8_t * src, uint32_t srclen, uint32_t expectlen); + +/** + * Runs _thecleanup function on _thealloc once _thealloc went out of scope + */ +#define DEFER_CLEANUP(_thealloc, _thecleanup) \ + __attribute__((cleanup(_thecleanup))) _thealloc +/** + * Often we want to free memory on an error, but not on a success. + * We do this by declaring a variable with DEFER_CLEANUP, then zeroing + * that variable after success to prevent DEFER_CLEANUP from accessing + * and freeing any memory it allocated. + * + * This pattern is not intuitive, so a named macro makes it more readable. + */ +#define ZERO_TO_DISABLE_DEFER_CLEANUP(_thealloc) memset(&_thealloc, 0, sizeof(_thealloc)) + +/* Creates cleanup function for pointers from function func which accepts a pointer. + * This is useful for DEFER_CLEANUP as it passes &_thealloc into _thecleanup function, + * so if _thealloc is a pointer _thecleanup will receive a pointer to a pointer.*/ +#define DEFINE_POINTER_CLEANUP_FUNC(type, func) \ + static inline void func##_pointer(type *p) { \ + if (p && *p) \ + func(*p); \ + } \ + struct __useless_struct_to_allow_trailing_semicolon__ + +#define s2n_array_len(array) ((array != NULL) ? (sizeof(array) / sizeof(array[0])) : 0) + +extern int s2n_mul_overflow(uint32_t a, uint32_t b, uint32_t* out); + +/** + * Rounds "initial" up to a multiple of "alignment", and stores the result in "out". + * Raises an error if overflow would occur. + * NOT CONSTANT TIME. + */ +extern int s2n_align_to(uint32_t initial, uint32_t alignment, uint32_t* out); +extern int s2n_add_overflow(uint32_t a, uint32_t b, uint32_t* out); +extern int s2n_sub_overflow(uint32_t a, uint32_t b, uint32_t* out); + + +/* START COMPATIBILITY LAYER */ + +/** + * NOTE: This will be removed once everything is using the new safety macro + * naming conventions + */ + /** * Sets the global `errno` and returns with a `S2N_RESULT_ERROR` */ -#define BAIL( x ) do { _S2N_ERROR( ( x ) ); return S2N_RESULT_ERROR; } while (0) +#define BAIL( x ) RESULT_BAIL(x) /** * Sets the global `errno` and returns with a POSIX error (`-1`) */ -#define BAIL_POSIX( x ) do { _S2N_ERROR( ( x ) ); return S2N_FAILURE; } while (0) +#define BAIL_POSIX( x ) POSIX_BAIL(x) /** * Sets the global `errno` and returns with a `NULL` pointer value */ -#define BAIL_PTR( x ) do { _S2N_ERROR( ( x ) ); return NULL; } while (0) +#define BAIL_PTR( x ) PTR_BAIL(x) /** * Ensures the `condition` is `true`, otherwise the function will `BAIL` with an `error` */ -#define ENSURE( condition , error ) __S2N_ENSURE((condition), BAIL(error)) +#define ENSURE( condition , error ) RESULT_ENSURE((condition), (error)) /** * Ensures the `result` is OK, otherwise the function will `BAIL` with an `error` */ -#define ENSURE_OK( result , error ) __S2N_ENSURE(s2n_result_is_ok(result), BAIL(error)) +#define ENSURE_OK( result , error ) RESULT_ENSURE_OK((result), (error)) /** * Ensures `n` is greater than or equal to `min`, otherwise the function will `BAIL` with a `S2N_ERR_SAFETY` error */ -#define ENSURE_GTE( n , min ) ENSURE((n) >= (min), S2N_ERR_SAFETY) +#define ENSURE_GTE( n , min ) RESULT_ENSURE_GTE((n), (min)) /** * Ensures `n` is less than or equal to `max`, otherwise the function will `BAIL` with a `S2N_ERR_SAFETY` error */ -#define ENSURE_LTE( n , max ) ENSURE((n) <= (max), S2N_ERR_SAFETY) +#define ENSURE_LTE( n , max ) RESULT_ENSURE_LTE((n), (max)) /** * Ensures `n` is greater than `min`, otherwise the function will `BAIL` with a `S2N_ERR_SAFETY` error */ -#define ENSURE_GT( n , min ) ENSURE((n) > (min), S2N_ERR_SAFETY) +#define ENSURE_GT( n , min ) RESULT_ENSURE_GT((n), (min)) /** * Ensures `n` is less than `min`, otherwise the function will `BAIL` with a `S2N_ERR_SAFETY` error */ -#define ENSURE_LT( n , max ) ENSURE((n) < (max), S2N_ERR_SAFETY) +#define ENSURE_LT( n , max ) RESULT_ENSURE_LT((n), (max)) /** * Ensures `a` is equal to `b`, otherwise the function will `BAIL` with a `S2N_ERR_SAFETY` error */ -#define ENSURE_EQ( a , b ) ENSURE((a) == (b), S2N_ERR_SAFETY) +#define ENSURE_EQ( a , b ) RESULT_ENSURE_EQ((a), (b)) /** * Ensures `a` is not equal to `b`, otherwise the function will `BAIL` with a `S2N_ERR_SAFETY` error */ -#define ENSURE_NE( a , b ) ENSURE((a) != (b), S2N_ERR_SAFETY) +#define ENSURE_NE( a , b ) RESULT_ENSURE_NE((a), (b)) /** * Ensures the `condition` is `true`, otherwise the function will `BAIL_POSIX` with an `error` */ -#define ENSURE_POSIX( condition , error ) __S2N_ENSURE((condition), BAIL_POSIX(error)) +#define ENSURE_POSIX( condition , error ) POSIX_ENSURE((condition), (error)) /** * Ensures the `condition` is `true`, otherwise the function will `BAIL_PTR` with an `error` */ -#define ENSURE_PTR( condition , error ) __S2N_ENSURE((condition), BAIL_PTR(error)) +#define ENSURE_PTR( condition , error ) PTR_ENSURE((condition), (error)) /** * Ensures `x` is not `NULL`, otherwise the function will `BAIL_PTR` with an `error` */ -#define ENSURE_REF_PTR( x ) ENSURE_PTR(S2N_OBJECT_PTR_IS_READABLE(x), S2N_ERR_NULL) +#define ENSURE_REF_PTR( x ) PTR_ENSURE_REF(x) /** * Ensures `x` is a readable reference, otherwise the function will `BAIL` with `S2N_ERR_NULL` */ -#define ENSURE_REF( x ) ENSURE(S2N_OBJECT_PTR_IS_READABLE(x), S2N_ERR_NULL) +#define ENSURE_REF( x ) RESULT_ENSURE_REF(x) /** * Ensures `x` is a readable reference, otherwise the function will `BAIL_POSIX` with `S2N_ERR_NULL` */ -#define ENSURE_POSIX_REF( x ) ENSURE_POSIX(S2N_OBJECT_PTR_IS_READABLE(x), S2N_ERR_NULL) +#define ENSURE_POSIX_REF( x ) POSIX_ENSURE_REF(x) /** * Ensures `x` is a mutable reference, otherwise the function will `BAIL` with `S2N_ERR_NULL` */ -#define ENSURE_MUT( x ) ENSURE(S2N_OBJECT_PTR_IS_WRITABLE(x), S2N_ERR_NULL) +#define ENSURE_MUT( x ) RESULT_ENSURE_MUT(x) /** * Ensures `x` is a mutable reference, otherwise the function will `BAIL_POSIX` with `S2N_ERR_NULL` */ -#define ENSURE_POSIX_MUT( x ) ENSURE_POSIX(S2N_OBJECT_PTR_IS_WRITABLE(x), S2N_ERR_NULL) +#define ENSURE_POSIX_MUT( x ) POSIX_ENSURE_MUT(x) /** * Ensures `min <= n <= max` */ -#define ENSURE_INCLUSIVE_RANGE( min , n , max ) \ - do { \ - __typeof( n ) __tmp_n = ( n ); \ - ENSURE_GTE(__tmp_n, min); \ - ENSURE_LTE(__tmp_n, max); \ - } while(0) +#define ENSURE_INCLUSIVE_RANGE( min , n , max ) RESULT_ENSURE_INCLUSIVE_RANGE((min), (n), (max)) /** * Ensures `min < n < max` */ -#define ENSURE_EXCLUSIVE_RANGE( min , n , max ) \ - do { \ - __typeof( n ) __tmp_n = ( n ); \ - ENSURE_GT(__tmp_n, min); \ - ENSURE_LT(__tmp_n, max); \ - } while(0) +#define ENSURE_EXCLUSIVE_RANGE( min , n , max ) RESULT_ENSURE_EXCLUSIVE_RANGE((min), (n), (max)) /** * Ensures the `result` is `S2N_RESULT_OK`, otherwise the function will return an error signal */ -#define PRECONDITION( result ) GUARD_RESULT(__S2N_ENSURE_PRECONDITION(result)) +#define PRECONDITION( result ) RESULT_PRECONDITION(result) /** * Ensures the `result` is `S2N_RESULT_OK`, otherwise the function will return an error signal */ -#define POSTCONDITION( result ) GUARD_RESULT(__S2N_ENSURE_POSTCONDITION(result)) +#define POSTCONDITION( result ) RESULT_POSTCONDITION(result) /** * Ensures the `result` is `S2N_RESULT_OK`, otherwise the function will return an error signal */ -#define PRECONDITION_POSIX( result ) GUARD_AS_POSIX(__S2N_ENSURE_PRECONDITION(result)) +#define PRECONDITION_POSIX( result ) POSIX_PRECONDITION(result) /** * Ensures the `result` is `S2N_RESULT_OK`, otherwise the function will return an error signal */ -#define POSTCONDITION_POSIX( result ) GUARD_AS_POSIX(__S2N_ENSURE_POSTCONDITION(result)) +#define POSTCONDITION_POSIX( result ) POSIX_POSTCONDITION(result) /** * Ensures the `condition` is `true`, otherwise the function will `BAIL` with an `error`. * When the code is built in debug mode, they are checked. * When the code is built in production mode, they are ignored. */ -#define DEBUG_ENSURE( condition, error ) __S2N_ENSURE_DEBUG((condition), BAIL(error)) +#define DEBUG_ENSURE( condition, error ) RESULT_DEBUG_ENSURE((condition), (error)) /** * Ensures the `condition` is `true`, otherwise the function will `BAIL_POSIX` with an `error`. * When the code is built in debug mode, they are checked. * When the code is built in production mode, they are ignored. */ -#define DEBUG_ENSURE_POSIX( condition, error ) __S2N_ENSURE_DEBUG((condition), BAIL_POSIX(error)) +#define DEBUG_ENSURE_POSIX( condition, error ) POSIX_DEBUG_ENSURE((condition), (error)) /** * Ensures `x` is not an error, otherwise the function will return an error signal * * Note: this currently accepts POSIX error signals but will transition to accept s2n_result */ -#define GUARD( x ) GUARD_POSIX(x) - -/** - * Ensures `x` is not an error, otherwise goto `label` - * - * Note: this currently accepts POSIX error signals but will transition to accept s2n_result - */ -#define GUARD_GOTO( x , label ) GUARD_POSIX_GOTO((x), (label)) +#define GUARD( x ) POSIX_GUARD(x) /** * Ensures `x` is not an error, otherwise the function will return `NULL` * * Note: this currently accepts POSIX error signals but will transition to accept s2n_result */ -#define GUARD_PTR( x ) GUARD_POSIX_PTR(x) +#define GUARD_PTR( x ) PTR_GUARD_POSIX(x) /** * Ensures `x` is not `NULL`, otherwise the function will return an error signal * * Note: this currently accepts POSIX error signals but will transition to accept s2n_result */ -#define GUARD_NONNULL( x ) GUARD_POSIX_NONNULL(x) - -/** - * Ensures `x` is not `NULL`, otherwise goto `label` - * - * Note: this currently accepts POSIX error signals but will transition to accept s2n_result - */ -#define GUARD_NONNULL_GOTO( x , label ) __S2N_ENSURE((x) != NULL, goto label) +#define GUARD_NONNULL( x ) POSIX_GUARD_PTR(x) /** * Ensures `x` is not `NULL`, otherwise the function will return `NULL` - * - * Note: this currently accepts POSIX error signals but will transition to accept s2n_result */ -#define GUARD_NONNULL_PTR( x ) __S2N_ENSURE((x) != NULL, return NULL) +#define GUARD_NONNULL_PTR( x ) PTR_GUARD(x) /** * Ensures `x` is not a OpenSSL error, otherwise the function will return an error signal * * Note: this currently accepts POSIX error signals but will transition to accept s2n_result */ -#define GUARD_OSSL( x, error ) GUARD_POSIX_OSSL((x), (error)) +#define GUARD_OSSL( x, error ) POSIX_GUARD_OSSL((x), (error)) /** * Ensures `x` is ok, otherwise the function will return an `S2N_RESULT_ERROR` */ -#define GUARD_RESULT( x ) __S2N_ENSURE(s2n_result_is_ok(x), return S2N_RESULT_ERROR) - -/** - * Ensures `x` is ok, otherwise goto `label` - */ -#define GUARD_RESULT_GOTO( x, label ) __S2N_ENSURE(s2n_result_is_ok(x), goto label) +#define GUARD_RESULT( x ) RESULT_GUARD(x) /** * Ensures `x` is ok, otherwise the function will return `NULL` */ -#define GUARD_RESULT_PTR( x ) __S2N_ENSURE(s2n_result_is_ok(x), return NULL) +#define GUARD_RESULT_PTR( x ) PTR_GUARD_RESULT(x) /** * Ensures `x` is not `NULL`, otherwise the function will return an `S2N_RESULT_ERROR` */ -#define GUARD_RESULT_NONNULL( x ) __S2N_ENSURE((x) != NULL, return S2N_RESULT_ERROR) +#define GUARD_RESULT_NONNULL( x ) RESULT_GUARD_PTR(x) /** * Ensures `x` is not a OpenSSL error, otherwise the function will `BAIL` with `error` */ /* TODO: use the OSSL error code in error reporting https://github.com/awslabs/s2n/issues/705 */ -#define GUARD_RESULT_OSSL( x , error ) ENSURE((x) == _OSSL_SUCCESS, error) +#define GUARD_RESULT_OSSL( x , error ) RESULT_GUARD_OSSL((x), (error)) /** * Ensures `x` is not a POSIX error, otherwise return a POSIX error */ -#define GUARD_POSIX( x ) __S2N_ENSURE((x) >= S2N_SUCCESS, return S2N_FAILURE) - -/** - * Ensures `x` is strictly not a POSIX error (`-1`), otherwise goto `label` - */ -#define GUARD_POSIX_STRICT( x ) __S2N_ENSURE((x) == S2N_SUCCESS, return S2N_FAILURE) - -/** - * Ensures `x` is not a POSIX error, otherwise goto `label` - */ -#define GUARD_POSIX_GOTO( x , label ) __S2N_ENSURE((x) >= S2N_SUCCESS, goto label) +#define GUARD_POSIX( x ) POSIX_GUARD(x) /** * Ensures `x` is not a POSIX error, otherwise the function will return `NULL` */ -#define GUARD_POSIX_PTR( x ) __S2N_ENSURE((x) >= S2N_SUCCESS, return NULL) +#define GUARD_POSIX_PTR( x ) PTR_GUARD_POSIX(x) /** * Ensures `x` is not `NULL`, otherwise the function will return a POSIX error (`-1`) */ -#define GUARD_POSIX_NONNULL( x ) __S2N_ENSURE((x) != NULL, return S2N_FAILURE) +#define GUARD_POSIX_NONNULL( x ) POSIX_GUARD_PTR(x) /** * Ensures `x` is not a OpenSSL error, otherwise the function will `BAIL` with `error` */ /* TODO: use the OSSL error code in error reporting https://github.com/awslabs/s2n/issues/705 */ -#define GUARD_POSIX_OSSL( x , error ) ENSURE_POSIX((x) == _OSSL_SUCCESS, error) +#define GUARD_POSIX_OSSL( x , error ) POSIX_GUARD_OSSL((x), (error)) /** * Ensures `x` is not a POSIX error, otherwise the function will return a `S2N_RESULT_ERROR` */ -#define GUARD_AS_RESULT( x ) __S2N_ENSURE((x) >= S2N_SUCCESS, return S2N_RESULT_ERROR) +#define GUARD_AS_RESULT( x ) RESULT_GUARD_POSIX(x) /** * Ensures `x` is OK (S2N_RESULT), otherwise the function will return a POSIX error (`-1`) */ -#define GUARD_AS_POSIX( x ) __S2N_ENSURE(s2n_result_is_ok(x), return S2N_FAILURE) +#define GUARD_AS_POSIX( x ) POSIX_GUARD_RESULT(x) /** * Performs a safe memcpy */ -#define CHECKED_MEMCPY( d , s , n ) __S2N_ENSURE_SAFE_MEMCPY((d), (s), (n), GUARD_RESULT_NONNULL) +#define CHECKED_MEMCPY( d , s , n ) RESULT_CHECKED_MEMCPY((d), (s), (n)) /** * Performs a safe memset */ -#define CHECKED_MEMSET( d , c , n ) __S2N_ENSURE_SAFE_MEMSET((d), (c), (n), ENSURE_REF) - -/** - * Marks a case of a switch statement as able to fall through to the next case - */ -#if defined(S2N_FALL_THROUGH_SUPPORTED) -# define FALL_THROUGH __attribute__((fallthrough)) -#else -# define FALL_THROUGH ((void)0) -#endif - -/* Returns `true` if s2n is in unit test mode, `false` otherwise */ -bool s2n_in_unit_test(); - -/* Sets whether s2n is in unit test mode */ -int s2n_in_unit_test_set(bool newval); - -#define S2N_IN_INTEG_TEST ( getenv("S2N_INTEG_TEST") != NULL ) -#define S2N_IN_TEST ( s2n_in_unit_test() || S2N_IN_INTEG_TEST ) - -/** - * Get the process id - * - * Returns: - * The process ID of the current process - */ -extern pid_t s2n_actual_getpid(); - -/* Returns 1 if a and b are equal, in constant time */ -extern bool s2n_constant_time_equals(const uint8_t * a, const uint8_t * b, const uint32_t len); - -/* Copy src to dst, or don't copy it, in constant time */ -extern int s2n_constant_time_copy_or_dont(uint8_t * dst, const uint8_t * src, uint32_t len, uint8_t dont); - -/* If src contains valid PKCS#1 v1.5 padding of exactly expectlen bytes, decode - * it into dst, otherwise leave dst alone, in constant time. - * Always returns zero. */ -extern int s2n_constant_time_pkcs1_unpad_or_dont(uint8_t * dst, const uint8_t * src, uint32_t srclen, uint32_t expectlen); - -/** - * Runs _thecleanup function on _thealloc once _thealloc went out of scope - */ -#define DEFER_CLEANUP(_thealloc, _thecleanup) \ - __attribute__((cleanup(_thecleanup))) _thealloc -/** - * Often we want to free memory on an error, but not on a success. - * We do this by declaring a variable with DEFER_CLEANUP, then zeroing - * that variable after success to prevent DEFER_CLEANUP from accessing - * and freeing any memory it allocated. - * - * This pattern is not intuitive, so a named macro makes it more readable. - */ -#define ZERO_TO_DISABLE_DEFER_CLEANUP(_thealloc) memset(&_thealloc, 0, sizeof(_thealloc)) - -/* Creates cleanup function for pointers from function func which accepts a pointer. - * This is useful for DEFER_CLEANUP as it passes &_thealloc into _thecleanup function, - * so if _thealloc is a pointer _thecleanup will receive a pointer to a pointer.*/ -#define DEFINE_POINTER_CLEANUP_FUNC(type, func) \ - static inline void func##_pointer(type *p) { \ - if (p && *p) \ - func(*p); \ - } \ - struct __useless_struct_to_allow_trailing_semicolon__ - -#define s2n_array_len(array) ((array != NULL) ? (sizeof(array) / sizeof(array[0])) : 0) - -extern int s2n_mul_overflow(uint32_t a, uint32_t b, uint32_t* out); - -/** - * Rounds "initial" up to a multiple of "alignment", and stores the result in "out". - * Raises an error if overflow would occur. - * NOT CONSTANT TIME. - */ -extern int s2n_align_to(uint32_t initial, uint32_t alignment, uint32_t* out); -extern int s2n_add_overflow(uint32_t a, uint32_t b, uint32_t* out); -extern int s2n_sub_overflow(uint32_t a, uint32_t b, uint32_t* out); -/* START COMPATIBILITY LAYER */ - -/** - * NOTE: This will be removed once everything is using s2n_result - */ +#define CHECKED_MEMSET( d , c , n ) RESULT_CHECKED_MEMSET((d), (c), (n)) /* `NULL` check a pointer */ -/* Note: this macro is replaced by ENSURE_POSIX_REF */ -#define notnull_check(ptr) ENSURE_POSIX_REF(ptr) -/* Note: this macro is replaced by ENSURE_REF_PTR */ -#define notnull_check_ptr(ptr) ENSURE_REF_PTR(ptr) +/* Note: this macro is replaced by POSIX_ENSURE_REF */ +#define notnull_check(ptr) POSIX_ENSURE_REF(ptr) +/* Note: this macro is replaced by PTR_ENSURE_REF */ +#define notnull_check_ptr(ptr) PTR_ENSURE_REF(ptr) /* Range check a number */ -#define gte_check( n , min ) ENSURE_POSIX((n) >= (min), S2N_ERR_SAFETY) -#define lte_check( n , max ) ENSURE_POSIX((n) <= (max), S2N_ERR_SAFETY) -#define gt_check( n , min ) ENSURE_POSIX((n) > (min), S2N_ERR_SAFETY) -#define lt_check( n , max ) ENSURE_POSIX((n) < (max), S2N_ERR_SAFETY) -#define eq_check( a , b ) ENSURE_POSIX((a) == (b), S2N_ERR_SAFETY) -#define ne_check( a , b ) ENSURE_POSIX((a) != (b), S2N_ERR_SAFETY) -#define inclusive_range_check( low, n, high ) \ - do { \ - __typeof( n ) __tmp_n = ( n ); \ - gte_check(__tmp_n, low); \ - lte_check(__tmp_n, high); \ - } while (0) -#define exclusive_range_check( low, n, high ) \ - do { \ - __typeof( n ) __tmp_n = ( n ); \ - gt_check(__tmp_n, low); \ - lt_check(__tmp_n, high); \ - } while (0) - -#define memcpy_check( d , s , n ) __S2N_ENSURE_SAFE_MEMCPY((d), (s), (n), GUARD_POSIX_NONNULL) +#define gte_check( n , min ) POSIX_ENSURE_GTE((n), (min)) +#define lte_check( n , max ) POSIX_ENSURE_LTE((n), (max)) +#define gt_check( n , min ) POSIX_ENSURE_GT((n), (min)) +#define lt_check( n , max ) POSIX_ENSURE_LT((n), (max)) +#define eq_check( a , b ) POSIX_ENSURE_EQ((a), (b)) +#define ne_check( a , b ) POSIX_ENSURE_NE((a), (b)) +#define inclusive_range_check( low, n, high ) POSIX_ENSURE_INCLUSIVE_RANGE((low), (n), (high)) +#define exclusive_range_check( low, n, high ) POSIX_ENSURE_EXCLUSIVE_RANGE((low), (n), (high)) + +#define memcpy_check( d , s , n ) POSIX_CHECKED_MEMCPY((d), (s), (n)) /* This will fail to build if d is an array. Cast the array to a pointer first! */ -#define memset_check( d , c , n ) __S2N_ENSURE_SAFE_MEMSET((d), (c), (n), ENSURE_POSIX_REF) +#define memset_check( d , c , n ) POSIX_CHECKED_MEMSET((d), (c), (n)) /* END COMPATIBILITY LAYER */ diff --git a/utils/s2n_safety_macros.h b/utils/s2n_safety_macros.h new file mode 100644 index 00000000000..42adbb11d8f --- /dev/null +++ b/utils/s2n_safety_macros.h @@ -0,0 +1,532 @@ + +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#pragma once + +/** + * DO NOT DIRECTLY MODIFY THIS FILE: + * + * The code in this file is generated from scripts/s2n_safety_macros.py and any modifications + * should be in there. + */ + +#include "error/s2n_errno.h" +#include "utils/s2n_ensure.h" +#include "utils/s2n_result.h" + +/** + * The goal of s2n_safety is to provide helpers to perform common + * checks, which help with code readability. + */ + +/* Success signal value for OpenSSL functions */ +#define _OSSL_SUCCESS 1 + +/** + * Sets the global `s2n_errno` to `error` and returns with an `S2N_RESULT_ERROR` + */ +#define RESULT_BAIL(error) do { _S2N_ERROR((error)); return S2N_RESULT_ERROR; } while (0) + +/** + * Ensures the `condition` is `true`, otherwise the function will `RESULT_BAIL` with `error` + */ +#define RESULT_ENSURE(condition, error) __S2N_ENSURE((condition), RESULT_BAIL(error)) + +/** + * Ensures the `condition` is `true`, otherwise the function will `RESULT_BAIL` with `error` + * + * NOTE: The condition will _only_ be checked when the code is compiled in debug mode. + * In release mode, the check is removed. + */ +#define RESULT_DEBUG_ENSURE(condition, error) __S2N_ENSURE_DEBUG((condition), RESULT_BAIL(error)) + +/** + * Ensures `s2n_result_is_ok(result)`, otherwise the function will `RESULT_BAIL` with `error` + * + * This can be useful for overriding the global `s2n_errno` + */ +#define RESULT_ENSURE_OK(result, error) __S2N_ENSURE(s2n_result_is_ok(result), RESULT_BAIL(error)) + +/** + * Ensures `a` is greater than or equal to `b`, otherwise the function will `RESULT_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define RESULT_ENSURE_GTE(a, b) __S2N_ENSURE((a) >= (b), RESULT_BAIL(S2N_ERR_SAFETY)) + +/** + * Ensures `a` is less than or equal to `b`, otherwise the function will `RESULT_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define RESULT_ENSURE_LTE(a, b) __S2N_ENSURE((a) <= (b), RESULT_BAIL(S2N_ERR_SAFETY)) + +/** + * Ensures `a` is greater than `b`, otherwise the function will `RESULT_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define RESULT_ENSURE_GT(a, b) __S2N_ENSURE((a) > (b), RESULT_BAIL(S2N_ERR_SAFETY)) + +/** + * Ensures `a` is less than `b`, otherwise the function will `RESULT_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define RESULT_ENSURE_LT(a, b) __S2N_ENSURE((a) < (b), RESULT_BAIL(S2N_ERR_SAFETY)) + +/** + * Ensures `a` is equal to `b`, otherwise the function will `RESULT_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define RESULT_ENSURE_EQ(a, b) __S2N_ENSURE((a) == (b), RESULT_BAIL(S2N_ERR_SAFETY)) + +/** + * Ensures `a` is not equal to `b`, otherwise the function will `RESULT_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define RESULT_ENSURE_NE(a, b) __S2N_ENSURE((a) != (b), RESULT_BAIL(S2N_ERR_SAFETY)) + +/** + * Ensures `min <= n <= max`, otherwise the function will `RESULT_BAIL` with `S2N_ERR_SAFETY` + */ +#define RESULT_ENSURE_INCLUSIVE_RANGE(min, n, max) \ + do { \ + __typeof(n) __tmp_n = ( n ); \ + __typeof(n) __tmp_min = ( min ); \ + __typeof(n) __tmp_max = ( max ); \ + RESULT_ENSURE_GTE(__tmp_n, __tmp_min); \ + RESULT_ENSURE_LTE(__tmp_n, __tmp_max); \ + } while(0) + +/** + * Ensures `min < n < max`, otherwise the function will `RESULT_BAIL` with `S2N_ERR_SAFETY` + */ +#define RESULT_ENSURE_EXCLUSIVE_RANGE(min, n, max) \ + do { \ + __typeof(n) __tmp_n = ( n ); \ + __typeof(n) __tmp_min = ( min ); \ + __typeof(n) __tmp_max = ( max ); \ + RESULT_ENSURE_GT(__tmp_n, __tmp_min); \ + RESULT_ENSURE_LT(__tmp_n, __tmp_max); \ + } while(0) + +/** + * Ensures `x` is a readable reference, otherwise the function will `RESULT_BAIL` with `S2N_ERR_NULL` + */ +#define RESULT_ENSURE_REF(x) __S2N_ENSURE(S2N_OBJECT_PTR_IS_READABLE(x), RESULT_BAIL(S2N_ERR_NULL)) + +/** + * Ensures `x` is a mutable reference, otherwise the function will `RESULT_BAIL` with `S2N_ERR_NULL` + */ +#define RESULT_ENSURE_MUT(x) __S2N_ENSURE(S2N_OBJECT_PTR_IS_WRITABLE(x), RESULT_BAIL(S2N_ERR_NULL)) + +/** + * Ensures the `result` is `S2N_RESULT_OK`, otherwise the function will return an error signal + * + * `RESULT_PRECONDITION` should be used at the beginning of a function to make assertions about + * the provided arguments. By default, it is functionally equivalent to `RESULT_GUARD_RESULT(result)` + * but can be altered by a testing environment to provide additional guarantees. + */ +#define RESULT_PRECONDITION(result) RESULT_GUARD_RESULT(__S2N_ENSURE_PRECONDITION((result))) + +/** + * Ensures the `result` is `S2N_RESULT_OK`, otherwise the function will return an error signal + * + * NOTE: The condition will _only_ be checked when the code is compiled in debug mode. + * In release mode, the check is removed. + * + * `RESULT_POSTCONDITION` should be used at the end of a function to make assertions about + * the resulting state. In debug mode, it is functionally equivalent to `RESULT_GUARD_RESULT(result)`. + * In production builds, it becomes a no-op. This can also be altered by a testing environment + * to provide additional guarantees. + */ +#define RESULT_POSTCONDITION(result) RESULT_GUARD_RESULT(__S2N_ENSURE_POSTCONDITION((result))) + +/** + * Performs a safer memcpy. + * + * The following checks are performed: + * + * * `destination` is non-null + * * `source` is non-null + * + * Callers will still need to ensure the following: + * + * * The size of the data pointed to by both the `destination` and `source` parameters, + * shall be at least `len` bytes. + */ +#define RESULT_CHECKED_MEMCPY(destination, source, len) __S2N_ENSURE_SAFE_MEMCPY((destination), (source), (len), RESULT_GUARD_PTR) + +/** + * Performs a safer memset + * + * The following checks are performed: + * + * * `destination` is non-null + * + * Callers will still need to ensure the following: + * + * * The size of the data pointed to by the `destination` parameter shall be at least + * `len` bytes. + */ +#define RESULT_CHECKED_MEMSET(destination, value, len) __S2N_ENSURE_SAFE_MEMSET((destination), (value), (len), RESULT_ENSURE_REF) + +/** + * Ensures `s2n_result_is_ok(result)`, otherwise the function will return `S2N_RESULT_ERROR` + */ +#define RESULT_GUARD(result) __S2N_ENSURE(s2n_result_is_ok(result), return S2N_RESULT_ERROR) + +/** + * Ensures `result == _OSSL_SUCCESS`, otherwise the function will `RESULT_BAIL` with `error` + */ +#define RESULT_GUARD_OSSL(result, error) __S2N_ENSURE((result) == _OSSL_SUCCESS, RESULT_BAIL(error)) + +/** + * Ensures `s2n_result_is_ok(result)`, otherwise the function will return `S2N_RESULT_ERROR` + */ +#define RESULT_GUARD_RESULT(result) __S2N_ENSURE(s2n_result_is_ok(result), return S2N_RESULT_ERROR) + +/** + * Ensures `(result) >= S2N_SUCCESS`, otherwise the function will return `S2N_RESULT_ERROR` + */ +#define RESULT_GUARD_POSIX(result) __S2N_ENSURE((result) >= S2N_SUCCESS, return S2N_RESULT_ERROR) + +/** + * Ensures `(result) != NULL`, otherwise the function will return `S2N_RESULT_ERROR` + */ +#define RESULT_GUARD_PTR(result) __S2N_ENSURE((result) != NULL, return S2N_RESULT_ERROR) + +/** + * Sets the global `s2n_errno` to `error` and returns with an `S2N_FAILURE` + */ +#define POSIX_BAIL(error) do { _S2N_ERROR((error)); return S2N_FAILURE; } while (0) + +/** + * Ensures the `condition` is `true`, otherwise the function will `POSIX_BAIL` with `error` + */ +#define POSIX_ENSURE(condition, error) __S2N_ENSURE((condition), POSIX_BAIL(error)) + +/** + * Ensures the `condition` is `true`, otherwise the function will `POSIX_BAIL` with `error` + * + * NOTE: The condition will _only_ be checked when the code is compiled in debug mode. + * In release mode, the check is removed. + */ +#define POSIX_DEBUG_ENSURE(condition, error) __S2N_ENSURE_DEBUG((condition), POSIX_BAIL(error)) + +/** + * Ensures `(result) >= S2N_SUCCESS`, otherwise the function will `POSIX_BAIL` with `error` + * + * This can be useful for overriding the global `s2n_errno` + */ +#define POSIX_ENSURE_OK(result, error) __S2N_ENSURE((result) >= S2N_SUCCESS, POSIX_BAIL(error)) + +/** + * Ensures `a` is greater than or equal to `b`, otherwise the function will `POSIX_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define POSIX_ENSURE_GTE(a, b) __S2N_ENSURE((a) >= (b), POSIX_BAIL(S2N_ERR_SAFETY)) + +/** + * Ensures `a` is less than or equal to `b`, otherwise the function will `POSIX_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define POSIX_ENSURE_LTE(a, b) __S2N_ENSURE((a) <= (b), POSIX_BAIL(S2N_ERR_SAFETY)) + +/** + * Ensures `a` is greater than `b`, otherwise the function will `POSIX_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define POSIX_ENSURE_GT(a, b) __S2N_ENSURE((a) > (b), POSIX_BAIL(S2N_ERR_SAFETY)) + +/** + * Ensures `a` is less than `b`, otherwise the function will `POSIX_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define POSIX_ENSURE_LT(a, b) __S2N_ENSURE((a) < (b), POSIX_BAIL(S2N_ERR_SAFETY)) + +/** + * Ensures `a` is equal to `b`, otherwise the function will `POSIX_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define POSIX_ENSURE_EQ(a, b) __S2N_ENSURE((a) == (b), POSIX_BAIL(S2N_ERR_SAFETY)) + +/** + * Ensures `a` is not equal to `b`, otherwise the function will `POSIX_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define POSIX_ENSURE_NE(a, b) __S2N_ENSURE((a) != (b), POSIX_BAIL(S2N_ERR_SAFETY)) + +/** + * Ensures `min <= n <= max`, otherwise the function will `POSIX_BAIL` with `S2N_ERR_SAFETY` + */ +#define POSIX_ENSURE_INCLUSIVE_RANGE(min, n, max) \ + do { \ + __typeof(n) __tmp_n = ( n ); \ + __typeof(n) __tmp_min = ( min ); \ + __typeof(n) __tmp_max = ( max ); \ + POSIX_ENSURE_GTE(__tmp_n, __tmp_min); \ + POSIX_ENSURE_LTE(__tmp_n, __tmp_max); \ + } while(0) + +/** + * Ensures `min < n < max`, otherwise the function will `POSIX_BAIL` with `S2N_ERR_SAFETY` + */ +#define POSIX_ENSURE_EXCLUSIVE_RANGE(min, n, max) \ + do { \ + __typeof(n) __tmp_n = ( n ); \ + __typeof(n) __tmp_min = ( min ); \ + __typeof(n) __tmp_max = ( max ); \ + POSIX_ENSURE_GT(__tmp_n, __tmp_min); \ + POSIX_ENSURE_LT(__tmp_n, __tmp_max); \ + } while(0) + +/** + * Ensures `x` is a readable reference, otherwise the function will `POSIX_BAIL` with `S2N_ERR_NULL` + */ +#define POSIX_ENSURE_REF(x) __S2N_ENSURE(S2N_OBJECT_PTR_IS_READABLE(x), POSIX_BAIL(S2N_ERR_NULL)) + +/** + * Ensures `x` is a mutable reference, otherwise the function will `POSIX_BAIL` with `S2N_ERR_NULL` + */ +#define POSIX_ENSURE_MUT(x) __S2N_ENSURE(S2N_OBJECT_PTR_IS_WRITABLE(x), POSIX_BAIL(S2N_ERR_NULL)) + +/** + * Ensures the `result` is `S2N_RESULT_OK`, otherwise the function will return an error signal + * + * `POSIX_PRECONDITION` should be used at the beginning of a function to make assertions about + * the provided arguments. By default, it is functionally equivalent to `POSIX_GUARD_RESULT(result)` + * but can be altered by a testing environment to provide additional guarantees. + */ +#define POSIX_PRECONDITION(result) POSIX_GUARD_RESULT(__S2N_ENSURE_PRECONDITION((result))) + +/** + * Ensures the `result` is `S2N_RESULT_OK`, otherwise the function will return an error signal + * + * NOTE: The condition will _only_ be checked when the code is compiled in debug mode. + * In release mode, the check is removed. + * + * `POSIX_POSTCONDITION` should be used at the end of a function to make assertions about + * the resulting state. In debug mode, it is functionally equivalent to `POSIX_GUARD_RESULT(result)`. + * In production builds, it becomes a no-op. This can also be altered by a testing environment + * to provide additional guarantees. + */ +#define POSIX_POSTCONDITION(result) POSIX_GUARD_RESULT(__S2N_ENSURE_POSTCONDITION((result))) + +/** + * Performs a safer memcpy. + * + * The following checks are performed: + * + * * `destination` is non-null + * * `source` is non-null + * + * Callers will still need to ensure the following: + * + * * The size of the data pointed to by both the `destination` and `source` parameters, + * shall be at least `len` bytes. + */ +#define POSIX_CHECKED_MEMCPY(destination, source, len) __S2N_ENSURE_SAFE_MEMCPY((destination), (source), (len), POSIX_GUARD_PTR) + +/** + * Performs a safer memset + * + * The following checks are performed: + * + * * `destination` is non-null + * + * Callers will still need to ensure the following: + * + * * The size of the data pointed to by the `destination` parameter shall be at least + * `len` bytes. + */ +#define POSIX_CHECKED_MEMSET(destination, value, len) __S2N_ENSURE_SAFE_MEMSET((destination), (value), (len), POSIX_ENSURE_REF) + +/** + * Ensures `(result) >= S2N_SUCCESS`, otherwise the function will return `S2N_FAILURE` + */ +#define POSIX_GUARD(result) __S2N_ENSURE((result) >= S2N_SUCCESS, return S2N_FAILURE) + +/** + * Ensures `result == _OSSL_SUCCESS`, otherwise the function will `POSIX_BAIL` with `error` + */ +#define POSIX_GUARD_OSSL(result, error) __S2N_ENSURE((result) == _OSSL_SUCCESS, POSIX_BAIL(error)) + +/** + * Ensures `s2n_result_is_ok(result)`, otherwise the function will return `S2N_FAILURE` + */ +#define POSIX_GUARD_RESULT(result) __S2N_ENSURE(s2n_result_is_ok(result), return S2N_FAILURE) + +/** + * Ensures `(result) >= S2N_SUCCESS`, otherwise the function will return `S2N_FAILURE` + */ +#define POSIX_GUARD_POSIX(result) __S2N_ENSURE((result) >= S2N_SUCCESS, return S2N_FAILURE) + +/** + * Ensures `(result) != NULL`, otherwise the function will return `S2N_FAILURE` + */ +#define POSIX_GUARD_PTR(result) __S2N_ENSURE((result) != NULL, return S2N_FAILURE) + +/** + * Sets the global `s2n_errno` to `error` and returns with an `NULL` + */ +#define PTR_BAIL(error) do { _S2N_ERROR((error)); return NULL; } while (0) + +/** + * Ensures the `condition` is `true`, otherwise the function will `PTR_BAIL` with `error` + */ +#define PTR_ENSURE(condition, error) __S2N_ENSURE((condition), PTR_BAIL(error)) + +/** + * Ensures the `condition` is `true`, otherwise the function will `PTR_BAIL` with `error` + * + * NOTE: The condition will _only_ be checked when the code is compiled in debug mode. + * In release mode, the check is removed. + */ +#define PTR_DEBUG_ENSURE(condition, error) __S2N_ENSURE_DEBUG((condition), PTR_BAIL(error)) + +/** + * Ensures `(result) != NULL`, otherwise the function will `PTR_BAIL` with `error` + * + * This can be useful for overriding the global `s2n_errno` + */ +#define PTR_ENSURE_OK(result, error) __S2N_ENSURE((result) != NULL, PTR_BAIL(error)) + +/** + * Ensures `a` is greater than or equal to `b`, otherwise the function will `PTR_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define PTR_ENSURE_GTE(a, b) __S2N_ENSURE((a) >= (b), PTR_BAIL(S2N_ERR_SAFETY)) + +/** + * Ensures `a` is less than or equal to `b`, otherwise the function will `PTR_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define PTR_ENSURE_LTE(a, b) __S2N_ENSURE((a) <= (b), PTR_BAIL(S2N_ERR_SAFETY)) + +/** + * Ensures `a` is greater than `b`, otherwise the function will `PTR_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define PTR_ENSURE_GT(a, b) __S2N_ENSURE((a) > (b), PTR_BAIL(S2N_ERR_SAFETY)) + +/** + * Ensures `a` is less than `b`, otherwise the function will `PTR_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define PTR_ENSURE_LT(a, b) __S2N_ENSURE((a) < (b), PTR_BAIL(S2N_ERR_SAFETY)) + +/** + * Ensures `a` is equal to `b`, otherwise the function will `PTR_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define PTR_ENSURE_EQ(a, b) __S2N_ENSURE((a) == (b), PTR_BAIL(S2N_ERR_SAFETY)) + +/** + * Ensures `a` is not equal to `b`, otherwise the function will `PTR_BAIL` with a `S2N_ERR_SAFETY` error + */ +#define PTR_ENSURE_NE(a, b) __S2N_ENSURE((a) != (b), PTR_BAIL(S2N_ERR_SAFETY)) + +/** + * Ensures `min <= n <= max`, otherwise the function will `PTR_BAIL` with `S2N_ERR_SAFETY` + */ +#define PTR_ENSURE_INCLUSIVE_RANGE(min, n, max) \ + do { \ + __typeof(n) __tmp_n = ( n ); \ + __typeof(n) __tmp_min = ( min ); \ + __typeof(n) __tmp_max = ( max ); \ + PTR_ENSURE_GTE(__tmp_n, __tmp_min); \ + PTR_ENSURE_LTE(__tmp_n, __tmp_max); \ + } while(0) + +/** + * Ensures `min < n < max`, otherwise the function will `PTR_BAIL` with `S2N_ERR_SAFETY` + */ +#define PTR_ENSURE_EXCLUSIVE_RANGE(min, n, max) \ + do { \ + __typeof(n) __tmp_n = ( n ); \ + __typeof(n) __tmp_min = ( min ); \ + __typeof(n) __tmp_max = ( max ); \ + PTR_ENSURE_GT(__tmp_n, __tmp_min); \ + PTR_ENSURE_LT(__tmp_n, __tmp_max); \ + } while(0) + +/** + * Ensures `x` is a readable reference, otherwise the function will `PTR_BAIL` with `S2N_ERR_NULL` + */ +#define PTR_ENSURE_REF(x) __S2N_ENSURE(S2N_OBJECT_PTR_IS_READABLE(x), PTR_BAIL(S2N_ERR_NULL)) + +/** + * Ensures `x` is a mutable reference, otherwise the function will `PTR_BAIL` with `S2N_ERR_NULL` + */ +#define PTR_ENSURE_MUT(x) __S2N_ENSURE(S2N_OBJECT_PTR_IS_WRITABLE(x), PTR_BAIL(S2N_ERR_NULL)) + +/** + * Ensures the `result` is `S2N_RESULT_OK`, otherwise the function will return an error signal + * + * `PTR_PRECONDITION` should be used at the beginning of a function to make assertions about + * the provided arguments. By default, it is functionally equivalent to `PTR_GUARD_RESULT(result)` + * but can be altered by a testing environment to provide additional guarantees. + */ +#define PTR_PRECONDITION(result) PTR_GUARD_RESULT(__S2N_ENSURE_PRECONDITION((result))) + +/** + * Ensures the `result` is `S2N_RESULT_OK`, otherwise the function will return an error signal + * + * NOTE: The condition will _only_ be checked when the code is compiled in debug mode. + * In release mode, the check is removed. + * + * `PTR_POSTCONDITION` should be used at the end of a function to make assertions about + * the resulting state. In debug mode, it is functionally equivalent to `PTR_GUARD_RESULT(result)`. + * In production builds, it becomes a no-op. This can also be altered by a testing environment + * to provide additional guarantees. + */ +#define PTR_POSTCONDITION(result) PTR_GUARD_RESULT(__S2N_ENSURE_POSTCONDITION((result))) + +/** + * Performs a safer memcpy. + * + * The following checks are performed: + * + * * `destination` is non-null + * * `source` is non-null + * + * Callers will still need to ensure the following: + * + * * The size of the data pointed to by both the `destination` and `source` parameters, + * shall be at least `len` bytes. + */ +#define PTR_CHECKED_MEMCPY(destination, source, len) __S2N_ENSURE_SAFE_MEMCPY((destination), (source), (len), PTR_GUARD_PTR) + +/** + * Performs a safer memset + * + * The following checks are performed: + * + * * `destination` is non-null + * + * Callers will still need to ensure the following: + * + * * The size of the data pointed to by the `destination` parameter shall be at least + * `len` bytes. + */ +#define PTR_CHECKED_MEMSET(destination, value, len) __S2N_ENSURE_SAFE_MEMSET((destination), (value), (len), PTR_ENSURE_REF) + +/** + * Ensures `(result) != NULL`, otherwise the function will return `NULL` + */ +#define PTR_GUARD(result) __S2N_ENSURE((result) != NULL, return NULL) + +/** + * Ensures `result == _OSSL_SUCCESS`, otherwise the function will `PTR_BAIL` with `error` + */ +#define PTR_GUARD_OSSL(result, error) __S2N_ENSURE((result) == _OSSL_SUCCESS, PTR_BAIL(error)) + +/** + * Ensures `s2n_result_is_ok(result)`, otherwise the function will return `NULL` + */ +#define PTR_GUARD_RESULT(result) __S2N_ENSURE(s2n_result_is_ok(result), return NULL) + +/** + * Ensures `(result) >= S2N_SUCCESS`, otherwise the function will return `NULL` + */ +#define PTR_GUARD_POSIX(result) __S2N_ENSURE((result) >= S2N_SUCCESS, return NULL) + +/** + * Ensures `(result) != NULL`, otherwise the function will return `NULL` + */ +#define PTR_GUARD_PTR(result) __S2N_ENSURE((result) != NULL, return NULL) +