diff --git a/programs/test/selftest.c b/programs/test/selftest.c index 727054ee6..bd28e9a36 100644 --- a/programs/test/selftest.c +++ b/programs/test/selftest.c @@ -66,6 +66,8 @@ #else #include #include +#define mbedtls_calloc calloc +#define mbedtls_free free #define mbedtls_printf printf #define mbedtls_snprintf snprintf #define mbedtls_exit exit @@ -78,6 +80,86 @@ #endif +#if defined MBEDTLS_SELF_TEST +/* Sanity check for malloc. This is not expected to fail, and is rather + * intended to display potentially useful information about the platform, + * in particular the behavior of malloc(0). */ +static int calloc_self_test( int verbose ) +{ + int failures = 0; + void *empty1 = mbedtls_calloc( 0, 1 ); + void *empty2 = mbedtls_calloc( 0, 1 ); + void *buffer1 = mbedtls_calloc( 1, 1 ); + void *buffer2 = mbedtls_calloc( 1, 1 ); + uintptr_t old_buffer1; + + if( empty1 == NULL && empty2 == NULL ) + { + if( verbose ) + mbedtls_printf( " CALLOC(0): passed (NULL)\n" ); + } + else if( empty1 == NULL || empty2 == NULL ) + { + if( verbose ) + mbedtls_printf( " CALLOC(0): failed (mix of NULL and non-NULL)\n" ); + ++failures; + } + else if( empty1 == empty2 ) + { + if( verbose ) + mbedtls_printf( " CALLOC(0): passed (same non-null)\n" ); + } + else + { + if( verbose ) + mbedtls_printf( " CALLOC(0): passed (distinct non-null)\n" ); + } + + if( buffer1 == NULL || buffer2 == NULL ) + { + if( verbose ) + mbedtls_printf( " CALLOC(1): failed (NULL)\n" ); + ++failures; + } + else if( buffer1 == buffer2 ) + { + if( verbose ) + mbedtls_printf( " CALLOC(1): failed (same buffer twice)\n" ); + ++failures; + } + else + { + if( verbose ) + mbedtls_printf( " CALLOC(1): passed\n" ); + } + + old_buffer1 = (uintptr_t) buffer1; + mbedtls_free( buffer1 ); + buffer1 = mbedtls_calloc( 1, 1 ); + if( buffer1 == NULL ) + { + if( verbose ) + mbedtls_printf( " CALLOC(1 again): failed (NULL)\n" ); + ++failures; + } + else + { + if( verbose ) + mbedtls_printf( " CALLOC(1 again): passed (%s address)\n", + (uintptr_t) old_buffer1 == (uintptr_t) buffer1 ? + "same" : "different" ); + } + + if( verbose ) + mbedtls_printf( "\n" ); + mbedtls_free( empty1 ); + mbedtls_free( empty2 ); + mbedtls_free( buffer1 ); + mbedtls_free( buffer2 ); + return( failures ); +} +#endif /* MBEDTLS_SELF_TEST */ + static int test_snprintf( size_t n, const char ref_buf[10], int ref_ret ) { int ret; @@ -174,6 +256,7 @@ typedef struct const selftest_t selftests[] = { + {"calloc", calloc_self_test}, #if defined(MBEDTLS_MD2_C) {"md2", mbedtls_md2_self_test}, #endif diff --git a/tests/configs/config-wrapper-malloc-0-null.h b/tests/configs/config-wrapper-malloc-0-null.h new file mode 100644 index 000000000..ed74eda63 --- /dev/null +++ b/tests/configs/config-wrapper-malloc-0-null.h @@ -0,0 +1,39 @@ +/* config.h wrapper that forces calloc(0) to return NULL. + * Used for testing. + */ +/* + * Copyright (C) 2019, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License 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. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ + +#ifndef MBEDTLS_CONFIG_H +/* Don't #define MBEDTLS_CONFIG_H, let config.h do it. */ + +#include "mbedtls/config.h" + +#include +static inline void *custom_calloc( size_t nmemb, size_t size ) +{ + if( nmemb == 0 || size == 0 ) + return( NULL ); + return( calloc( nmemb, size ) ); +} + +#define MBEDTLS_PLATFORM_MEMORY +#define MBEDTLS_PLATFORM_STD_CALLOC custom_calloc + +#endif /* MBEDTLS_CONFIG_H */ diff --git a/tests/scripts/all.sh b/tests/scripts/all.sh index 3c4681afb..e1be1a4d4 100755 --- a/tests/scripts/all.sh +++ b/tests/scripts/all.sh @@ -992,6 +992,21 @@ component_test_platform_calloc_macro () { make test } +component_test_malloc_0_null () { + msg "build: malloc(0) returns NULL (ASan+UBSan build)" + scripts/config.pl full + scripts/config.pl unset MBEDTLS_MEMORY_BUFFER_ALLOC_C + make CC=gcc CFLAGS="'-DMBEDTLS_CONFIG_FILE=\"$PWD/tests/configs/config-wrapper-malloc-0-null.h\"' -O -Werror -Wall -Wextra -fsanitize=address,undefined" LDFLAGS='-fsanitize=address,undefined' + + msg "test: malloc(0) returns NULL (ASan+UBSan build)" + make test + + msg "selftest: malloc(0) returns NULL (ASan+UBSan build)" + # Just the calloc selftest. "make test" ran the others as part of the + # test suites. + if_build_succeeded programs/test/selftest calloc +} + component_test_aes_fewer_tables () { msg "build: default config with AES_FEWER_TABLES enabled" scripts/config.pl set MBEDTLS_AES_FEWER_TABLES