Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 27 additions & 2 deletions Source/UnitTest/cmake_core.cmake
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: Apache-2.0
# ----------------------------------------------------------------------------
# Copyright 2020-2025 Arm Limited
# Copyright 2020-2026 Arm Limited
#
# 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
Expand Down Expand Up @@ -43,7 +43,8 @@ target_sources(${ASTCENC_TEST}
PRIVATE
test_simd.cpp
test_softfloat.cpp
test_decode.cpp)
test_decode.cpp
test_encode.cpp)

target_include_directories(${ASTCENC_TEST}
PRIVATE
Expand Down Expand Up @@ -190,6 +191,30 @@ elseif(${ASTCENC_ISA_SIMD} MATCHES "avx2")

endif()

if(${ASTCENC_ASAN})
target_compile_options(${ASTCENC_TEST}
PRIVATE
$<${is_gnu_fe}:-fsanitize=address>
$<${is_gnu_fe}:-fno-sanitize-recover=all>)

target_link_options(${ASTCENC_TEST}
PRIVATE
$<${is_gnu_fe}:-fsanitize=address>
$<${is_clang}:-fuse-ld=lld>)
endif()

if(${ASTCENC_UBSAN})
target_compile_options(${ASTCENC_TEST}
PRIVATE
$<${is_gnu_fe}:-fsanitize=undefined>
$<${is_gnu_fe}:-fno-sanitize-recover=all>)

target_link_options(${ASTCENC_TEST}
PRIVATE
$<${is_gnu_fe}:-fsanitize=undefined>
$<${is_clang}:-fuse-ld=lld>)
endif()

target_link_libraries(${ASTCENC_TEST}
PRIVATE
gtest_main)
Expand Down
103 changes: 0 additions & 103 deletions Source/UnitTest/test_decode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,109 +28,6 @@
namespace astcenc
{

/** @brief Input overflow tests for xy * z. */
TEST(compress, overflow_in_z)
{
astcenc_error status;
astcenc_config config;
astcenc_context* context;

static const astcenc_swizzle swizzle {
ASTCENC_SWZ_R, ASTCENC_SWZ_G, ASTCENC_SWZ_B, ASTCENC_SWZ_A
};

astcenc_config_init(ASTCENC_PRF_LDR, 4, 4, 1, ASTCENC_PRE_MEDIUM, 0, &config);
status = astcenc_context_alloc(&config, 1, &context, nullptr);
EXPECT_EQ(status, ASTCENC_SUCCESS);

// Arrays are too short, but should never be touched
uint8_t data[1];
uint8_t input[1];

astcenc_image image;
// x * y always fits in 64-bit size_t, but xy * z will overflow
image.dim_x = 0xFFFFFFFFu;
image.dim_y = 0xFFFFFFFFu;
image.dim_z = 0xFFFFFFFFu;
image.data_type = ASTCENC_TYPE_U8;
uint8_t* slices = input;
image.data = reinterpret_cast<void**>(&slices);

status = astcenc_compress_image(context, &image, &swizzle, data, -1, 0);
EXPECT_EQ(status, ASTCENC_ERR_BAD_PARAM);

astcenc_context_free(context);
}

/** @brief Input overflow tests for xyz * 16. */
TEST(compress, overflow_in_16)
{
astcenc_error status;
astcenc_config config;
astcenc_context* context;

static const astcenc_swizzle swizzle {
ASTCENC_SWZ_R, ASTCENC_SWZ_G, ASTCENC_SWZ_B, ASTCENC_SWZ_A
};

astcenc_config_init(ASTCENC_PRF_LDR, 4, 4, 1, ASTCENC_PRE_MEDIUM, 0, &config);
status = astcenc_context_alloc(&config, 1, &context, nullptr);
EXPECT_EQ(status, ASTCENC_SUCCESS);

// Arrays are too short, but should never be touched
uint8_t data[1];
uint8_t input[1];

astcenc_image image;
// xyz (in blocks) always fits in 64-bit size_t, but xyz * 16 will overflow
image.dim_x = 0x80000000u;
image.dim_y = 0x80000000u;
image.dim_z = 0x00000010u;
image.data_type = ASTCENC_TYPE_U8;
uint8_t* slices = input;
image.data = reinterpret_cast<void**>(&slices);

status = astcenc_compress_image(context, &image, &swizzle, data, -1, 0);
EXPECT_EQ(status, ASTCENC_ERR_BAD_PARAM);

astcenc_context_free(context);
}


/** @brief Input data buffer overrun tests. */
TEST(compress, data_buffer_exceeded)
{
astcenc_error status;
astcenc_config config;
astcenc_context* context;

static const astcenc_swizzle swizzle {
ASTCENC_SWZ_R, ASTCENC_SWZ_G, ASTCENC_SWZ_B, ASTCENC_SWZ_A
};

astcenc_config_init(ASTCENC_PRF_LDR, 4, 4, 1, ASTCENC_PRE_MEDIUM, 0, &config);
status = astcenc_context_alloc(&config, 1, &context, nullptr);
EXPECT_EQ(status, ASTCENC_SUCCESS);

// Arrays are too short, but should never be touched
uint8_t data[1];
uint8_t input[1];

astcenc_image image;
image.dim_x = 0x4u;
image.dim_y = 0x4u;
image.dim_z = 0x1u;
image.data_type = ASTCENC_TYPE_U8;
uint8_t* slices = input;
image.data = reinterpret_cast<void**>(&slices);

// Data size is 1 byte too short so this should error
status = astcenc_compress_image(context, &image, &swizzle, data, 15, 0);
EXPECT_EQ(status, ASTCENC_ERR_OUT_OF_MEM);

astcenc_context_free(context);
}

/** @brief Input overflow tests for xy * z. */
TEST(decompress, overflow_in_z)
{
Expand Down
134 changes: 134 additions & 0 deletions Source/UnitTest/test_encode.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// SPDX-License-Identifier: Apache-2.0
// ----------------------------------------------------------------------------
// Copyright 2025-2026 Arm Limited
//
// 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.
// ----------------------------------------------------------------------------

/**
* @brief Unit tests for the vectorized SIMD functionality.
*/

#include <limits>

#include "gtest/gtest.h"

#include "../astcenc.h"

namespace astcenc
{

/** @brief Input overflow tests for xy * z. */
TEST(compress, overflow_in_z)
{
astcenc_error status;
astcenc_config config;
astcenc_context* context;

static const astcenc_swizzle swizzle {
ASTCENC_SWZ_R, ASTCENC_SWZ_G, ASTCENC_SWZ_B, ASTCENC_SWZ_A
};

astcenc_config_init(ASTCENC_PRF_LDR, 4, 4, 1, ASTCENC_PRE_MEDIUM, 0, &config);
status = astcenc_context_alloc(&config, 1, &context, nullptr);
EXPECT_EQ(status, ASTCENC_SUCCESS);

// Arrays are too short, but should never be touched
uint8_t data[1];
uint8_t input[1];

astcenc_image image;
// x * y always fits in 64-bit size_t, but xy * z will overflow
image.dim_x = 0xFFFFFFFFu;
image.dim_y = 0xFFFFFFFFu;
image.dim_z = 0xFFFFFFFFu;
image.data_type = ASTCENC_TYPE_U8;
uint8_t* slices = input;
image.data = reinterpret_cast<void**>(&slices);

status = astcenc_compress_image(context, &image, &swizzle, data, -1, 0);
EXPECT_EQ(status, ASTCENC_ERR_BAD_PARAM);

astcenc_context_free(context);
}

/** @brief Input overflow tests for xyz * 16. */
TEST(compress, overflow_in_16)
{
astcenc_error status;
astcenc_config config;
astcenc_context* context;

static const astcenc_swizzle swizzle {
ASTCENC_SWZ_R, ASTCENC_SWZ_G, ASTCENC_SWZ_B, ASTCENC_SWZ_A
};

astcenc_config_init(ASTCENC_PRF_LDR, 4, 4, 1, ASTCENC_PRE_MEDIUM, 0, &config);
status = astcenc_context_alloc(&config, 1, &context, nullptr);
EXPECT_EQ(status, ASTCENC_SUCCESS);

// Arrays are too short, but should never be touched
uint8_t data[1];
uint8_t input[1];

astcenc_image image;
// xyz (in blocks) always fits in 64-bit size_t, but xyz * 16 will overflow
image.dim_x = 0x80000000u;
image.dim_y = 0x80000000u;
image.dim_z = 0x00000010u;
image.data_type = ASTCENC_TYPE_U8;
uint8_t* slices = input;
image.data = reinterpret_cast<void**>(&slices);

status = astcenc_compress_image(context, &image, &swizzle, data, -1, 0);
EXPECT_EQ(status, ASTCENC_ERR_BAD_PARAM);

astcenc_context_free(context);
}


/** @brief Input data buffer overrun tests. */
TEST(compress, data_buffer_exceeded)
{
astcenc_error status;
astcenc_config config;
astcenc_context* context;

static const astcenc_swizzle swizzle {
ASTCENC_SWZ_R, ASTCENC_SWZ_G, ASTCENC_SWZ_B, ASTCENC_SWZ_A
};

astcenc_config_init(ASTCENC_PRF_LDR, 4, 4, 1, ASTCENC_PRE_MEDIUM, 0, &config);
status = astcenc_context_alloc(&config, 1, &context, nullptr);
EXPECT_EQ(status, ASTCENC_SUCCESS);

// Arrays are too short, but should never be touched
uint8_t data[1];
uint8_t input[1];

astcenc_image image;
image.dim_x = 0x4u;
image.dim_y = 0x4u;
image.dim_z = 0x1u;
image.data_type = ASTCENC_TYPE_U8;
uint8_t* slices = input;
image.data = reinterpret_cast<void**>(&slices);

// Data size is 1 byte too short so this should error
status = astcenc_compress_image(context, &image, &swizzle, data, 15, 0);
EXPECT_EQ(status, ASTCENC_ERR_OUT_OF_MEM);

astcenc_context_free(context);
}

}
9 changes: 7 additions & 2 deletions Source/astcenc_vecmathlib_avx2_8.h
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,13 @@ struct vint8
*/
ASTCENC_SIMD_INLINE explicit vint8(const uint8_t *p)
{
// _mm_loadu_si64 would be nicer syntax, but missing on older GCC
m = _mm256_cvtepu8_epi32(_mm_cvtsi64_si128(*reinterpret_cast<const long long*>(p)));
// _mm_loadu_si64 would be nicer syntax, but missing on older GCC and
// generates broken code on GCC 11.x before 11.3, so use this to
// generate alignment-safe and aliasing-safe alternative
uint64_t tmp;
std::memcpy(&tmp, p, sizeof(tmp));

m = _mm256_cvtepu8_epi32(_mm_cvtsi64_si128(tmp));
}

/**
Expand Down
9 changes: 7 additions & 2 deletions Source/astcenc_vecmathlib_sse_4.h
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,13 @@ struct vint4
*/
ASTCENC_SIMD_INLINE explicit vint4(const uint8_t *p)
{
// _mm_loadu_si32 would be nicer syntax, but missing on older GCC
__m128i t = _mm_cvtsi32_si128(*reinterpret_cast<const int*>(p));
// _mm_loadu_si32 would be nicer syntax, but missing on older GCC and
// generates broken code on GCC 11.x before 11.3, so use this to
// generate alignment-safe and aliasing-safe alternative
int32_t tmp;
std::memcpy(&tmp, p, sizeof(tmp));

__m128i t = _mm_cvtsi32_si128(tmp);

#if ASTCENC_SSE >= 41
m = _mm_cvtepu8_epi32(t);
Expand Down
1 change: 0 additions & 1 deletion Source/cmake_core.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,6 @@ macro(astcenc_set_properties ASTCENC_TARGET_NAME ASTCENC_VENEER_TYPE)
PRIVATE
$<${is_gnu_fe}:-fsanitize=address>
$<${is_clang}:-fuse-ld=lld>)

endif()

if(${ASTCENC_UBSAN})
Expand Down
Loading