Skip to content

Commit ef35af9

Browse files
authored
Add ASAN/UBSAN support to unit tests (#650)
1 parent 8a537ca commit ef35af9

6 files changed

Lines changed: 175 additions & 110 deletions

File tree

Source/UnitTest/cmake_core.cmake

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# SPDX-License-Identifier: Apache-2.0
22
# ----------------------------------------------------------------------------
3-
# Copyright 2020-2025 Arm Limited
3+
# Copyright 2020-2026 Arm Limited
44
#
55
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
66
# use this file except in compliance with the License. You may obtain a copy
@@ -43,7 +43,8 @@ target_sources(${ASTCENC_TEST}
4343
PRIVATE
4444
test_simd.cpp
4545
test_softfloat.cpp
46-
test_decode.cpp)
46+
test_decode.cpp
47+
test_encode.cpp)
4748

4849
target_include_directories(${ASTCENC_TEST}
4950
PRIVATE
@@ -190,6 +191,30 @@ elseif(${ASTCENC_ISA_SIMD} MATCHES "avx2")
190191

191192
endif()
192193

194+
if(${ASTCENC_ASAN})
195+
target_compile_options(${ASTCENC_TEST}
196+
PRIVATE
197+
$<${is_gnu_fe}:-fsanitize=address>
198+
$<${is_gnu_fe}:-fno-sanitize-recover=all>)
199+
200+
target_link_options(${ASTCENC_TEST}
201+
PRIVATE
202+
$<${is_gnu_fe}:-fsanitize=address>
203+
$<${is_clang}:-fuse-ld=lld>)
204+
endif()
205+
206+
if(${ASTCENC_UBSAN})
207+
target_compile_options(${ASTCENC_TEST}
208+
PRIVATE
209+
$<${is_gnu_fe}:-fsanitize=undefined>
210+
$<${is_gnu_fe}:-fno-sanitize-recover=all>)
211+
212+
target_link_options(${ASTCENC_TEST}
213+
PRIVATE
214+
$<${is_gnu_fe}:-fsanitize=undefined>
215+
$<${is_clang}:-fuse-ld=lld>)
216+
endif()
217+
193218
target_link_libraries(${ASTCENC_TEST}
194219
PRIVATE
195220
gtest_main)

Source/UnitTest/test_decode.cpp

Lines changed: 0 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -28,109 +28,6 @@
2828
namespace astcenc
2929
{
3030

31-
/** @brief Input overflow tests for xy * z. */
32-
TEST(compress, overflow_in_z)
33-
{
34-
astcenc_error status;
35-
astcenc_config config;
36-
astcenc_context* context;
37-
38-
static const astcenc_swizzle swizzle {
39-
ASTCENC_SWZ_R, ASTCENC_SWZ_G, ASTCENC_SWZ_B, ASTCENC_SWZ_A
40-
};
41-
42-
astcenc_config_init(ASTCENC_PRF_LDR, 4, 4, 1, ASTCENC_PRE_MEDIUM, 0, &config);
43-
status = astcenc_context_alloc(&config, 1, &context, nullptr);
44-
EXPECT_EQ(status, ASTCENC_SUCCESS);
45-
46-
// Arrays are too short, but should never be touched
47-
uint8_t data[1];
48-
uint8_t input[1];
49-
50-
astcenc_image image;
51-
// x * y always fits in 64-bit size_t, but xy * z will overflow
52-
image.dim_x = 0xFFFFFFFFu;
53-
image.dim_y = 0xFFFFFFFFu;
54-
image.dim_z = 0xFFFFFFFFu;
55-
image.data_type = ASTCENC_TYPE_U8;
56-
uint8_t* slices = input;
57-
image.data = reinterpret_cast<void**>(&slices);
58-
59-
status = astcenc_compress_image(context, &image, &swizzle, data, -1, 0);
60-
EXPECT_EQ(status, ASTCENC_ERR_BAD_PARAM);
61-
62-
astcenc_context_free(context);
63-
}
64-
65-
/** @brief Input overflow tests for xyz * 16. */
66-
TEST(compress, overflow_in_16)
67-
{
68-
astcenc_error status;
69-
astcenc_config config;
70-
astcenc_context* context;
71-
72-
static const astcenc_swizzle swizzle {
73-
ASTCENC_SWZ_R, ASTCENC_SWZ_G, ASTCENC_SWZ_B, ASTCENC_SWZ_A
74-
};
75-
76-
astcenc_config_init(ASTCENC_PRF_LDR, 4, 4, 1, ASTCENC_PRE_MEDIUM, 0, &config);
77-
status = astcenc_context_alloc(&config, 1, &context, nullptr);
78-
EXPECT_EQ(status, ASTCENC_SUCCESS);
79-
80-
// Arrays are too short, but should never be touched
81-
uint8_t data[1];
82-
uint8_t input[1];
83-
84-
astcenc_image image;
85-
// xyz (in blocks) always fits in 64-bit size_t, but xyz * 16 will overflow
86-
image.dim_x = 0x80000000u;
87-
image.dim_y = 0x80000000u;
88-
image.dim_z = 0x00000010u;
89-
image.data_type = ASTCENC_TYPE_U8;
90-
uint8_t* slices = input;
91-
image.data = reinterpret_cast<void**>(&slices);
92-
93-
status = astcenc_compress_image(context, &image, &swizzle, data, -1, 0);
94-
EXPECT_EQ(status, ASTCENC_ERR_BAD_PARAM);
95-
96-
astcenc_context_free(context);
97-
}
98-
99-
100-
/** @brief Input data buffer overrun tests. */
101-
TEST(compress, data_buffer_exceeded)
102-
{
103-
astcenc_error status;
104-
astcenc_config config;
105-
astcenc_context* context;
106-
107-
static const astcenc_swizzle swizzle {
108-
ASTCENC_SWZ_R, ASTCENC_SWZ_G, ASTCENC_SWZ_B, ASTCENC_SWZ_A
109-
};
110-
111-
astcenc_config_init(ASTCENC_PRF_LDR, 4, 4, 1, ASTCENC_PRE_MEDIUM, 0, &config);
112-
status = astcenc_context_alloc(&config, 1, &context, nullptr);
113-
EXPECT_EQ(status, ASTCENC_SUCCESS);
114-
115-
// Arrays are too short, but should never be touched
116-
uint8_t data[1];
117-
uint8_t input[1];
118-
119-
astcenc_image image;
120-
image.dim_x = 0x4u;
121-
image.dim_y = 0x4u;
122-
image.dim_z = 0x1u;
123-
image.data_type = ASTCENC_TYPE_U8;
124-
uint8_t* slices = input;
125-
image.data = reinterpret_cast<void**>(&slices);
126-
127-
// Data size is 1 byte too short so this should error
128-
status = astcenc_compress_image(context, &image, &swizzle, data, 15, 0);
129-
EXPECT_EQ(status, ASTCENC_ERR_OUT_OF_MEM);
130-
131-
astcenc_context_free(context);
132-
}
133-
13431
/** @brief Input overflow tests for xy * z. */
13532
TEST(decompress, overflow_in_z)
13633
{

Source/UnitTest/test_encode.cpp

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// ----------------------------------------------------------------------------
3+
// Copyright 2025-2026 Arm Limited
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
6+
// use this file except in compliance with the License. You may obtain a copy
7+
// of the License at:
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13+
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14+
// License for the specific language governing permissions and limitations
15+
// under the License.
16+
// ----------------------------------------------------------------------------
17+
18+
/**
19+
* @brief Unit tests for the vectorized SIMD functionality.
20+
*/
21+
22+
#include <limits>
23+
24+
#include "gtest/gtest.h"
25+
26+
#include "../astcenc.h"
27+
28+
namespace astcenc
29+
{
30+
31+
/** @brief Input overflow tests for xy * z. */
32+
TEST(compress, overflow_in_z)
33+
{
34+
astcenc_error status;
35+
astcenc_config config;
36+
astcenc_context* context;
37+
38+
static const astcenc_swizzle swizzle {
39+
ASTCENC_SWZ_R, ASTCENC_SWZ_G, ASTCENC_SWZ_B, ASTCENC_SWZ_A
40+
};
41+
42+
astcenc_config_init(ASTCENC_PRF_LDR, 4, 4, 1, ASTCENC_PRE_MEDIUM, 0, &config);
43+
status = astcenc_context_alloc(&config, 1, &context, nullptr);
44+
EXPECT_EQ(status, ASTCENC_SUCCESS);
45+
46+
// Arrays are too short, but should never be touched
47+
uint8_t data[1];
48+
uint8_t input[1];
49+
50+
astcenc_image image;
51+
// x * y always fits in 64-bit size_t, but xy * z will overflow
52+
image.dim_x = 0xFFFFFFFFu;
53+
image.dim_y = 0xFFFFFFFFu;
54+
image.dim_z = 0xFFFFFFFFu;
55+
image.data_type = ASTCENC_TYPE_U8;
56+
uint8_t* slices = input;
57+
image.data = reinterpret_cast<void**>(&slices);
58+
59+
status = astcenc_compress_image(context, &image, &swizzle, data, -1, 0);
60+
EXPECT_EQ(status, ASTCENC_ERR_BAD_PARAM);
61+
62+
astcenc_context_free(context);
63+
}
64+
65+
/** @brief Input overflow tests for xyz * 16. */
66+
TEST(compress, overflow_in_16)
67+
{
68+
astcenc_error status;
69+
astcenc_config config;
70+
astcenc_context* context;
71+
72+
static const astcenc_swizzle swizzle {
73+
ASTCENC_SWZ_R, ASTCENC_SWZ_G, ASTCENC_SWZ_B, ASTCENC_SWZ_A
74+
};
75+
76+
astcenc_config_init(ASTCENC_PRF_LDR, 4, 4, 1, ASTCENC_PRE_MEDIUM, 0, &config);
77+
status = astcenc_context_alloc(&config, 1, &context, nullptr);
78+
EXPECT_EQ(status, ASTCENC_SUCCESS);
79+
80+
// Arrays are too short, but should never be touched
81+
uint8_t data[1];
82+
uint8_t input[1];
83+
84+
astcenc_image image;
85+
// xyz (in blocks) always fits in 64-bit size_t, but xyz * 16 will overflow
86+
image.dim_x = 0x80000000u;
87+
image.dim_y = 0x80000000u;
88+
image.dim_z = 0x00000010u;
89+
image.data_type = ASTCENC_TYPE_U8;
90+
uint8_t* slices = input;
91+
image.data = reinterpret_cast<void**>(&slices);
92+
93+
status = astcenc_compress_image(context, &image, &swizzle, data, -1, 0);
94+
EXPECT_EQ(status, ASTCENC_ERR_BAD_PARAM);
95+
96+
astcenc_context_free(context);
97+
}
98+
99+
100+
/** @brief Input data buffer overrun tests. */
101+
TEST(compress, data_buffer_exceeded)
102+
{
103+
astcenc_error status;
104+
astcenc_config config;
105+
astcenc_context* context;
106+
107+
static const astcenc_swizzle swizzle {
108+
ASTCENC_SWZ_R, ASTCENC_SWZ_G, ASTCENC_SWZ_B, ASTCENC_SWZ_A
109+
};
110+
111+
astcenc_config_init(ASTCENC_PRF_LDR, 4, 4, 1, ASTCENC_PRE_MEDIUM, 0, &config);
112+
status = astcenc_context_alloc(&config, 1, &context, nullptr);
113+
EXPECT_EQ(status, ASTCENC_SUCCESS);
114+
115+
// Arrays are too short, but should never be touched
116+
uint8_t data[1];
117+
uint8_t input[1];
118+
119+
astcenc_image image;
120+
image.dim_x = 0x4u;
121+
image.dim_y = 0x4u;
122+
image.dim_z = 0x1u;
123+
image.data_type = ASTCENC_TYPE_U8;
124+
uint8_t* slices = input;
125+
image.data = reinterpret_cast<void**>(&slices);
126+
127+
// Data size is 1 byte too short so this should error
128+
status = astcenc_compress_image(context, &image, &swizzle, data, 15, 0);
129+
EXPECT_EQ(status, ASTCENC_ERR_OUT_OF_MEM);
130+
131+
astcenc_context_free(context);
132+
}
133+
134+
}

Source/astcenc_vecmathlib_avx2_8.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,13 @@ struct vint8
142142
*/
143143
ASTCENC_SIMD_INLINE explicit vint8(const uint8_t *p)
144144
{
145-
// _mm_loadu_si64 would be nicer syntax, but missing on older GCC
146-
m = _mm256_cvtepu8_epi32(_mm_cvtsi64_si128(*reinterpret_cast<const long long*>(p)));
145+
// _mm_loadu_si64 would be nicer syntax, but missing on older GCC and
146+
// generates broken code on GCC 11.x before 11.3, so use this to
147+
// generate alignment-safe and aliasing-safe alternative
148+
uint64_t tmp;
149+
std::memcpy(&tmp, p, sizeof(tmp));
150+
151+
m = _mm256_cvtepu8_epi32(_mm_cvtsi64_si128(tmp));
147152
}
148153

149154
/**

Source/astcenc_vecmathlib_sse_4.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,8 +207,13 @@ struct vint4
207207
*/
208208
ASTCENC_SIMD_INLINE explicit vint4(const uint8_t *p)
209209
{
210-
// _mm_loadu_si32 would be nicer syntax, but missing on older GCC
211-
__m128i t = _mm_cvtsi32_si128(*reinterpret_cast<const int*>(p));
210+
// _mm_loadu_si32 would be nicer syntax, but missing on older GCC and
211+
// generates broken code on GCC 11.x before 11.3, so use this to
212+
// generate alignment-safe and aliasing-safe alternative
213+
int32_t tmp;
214+
std::memcpy(&tmp, p, sizeof(tmp));
215+
216+
__m128i t = _mm_cvtsi32_si128(tmp);
212217

213218
#if ASTCENC_SSE >= 41
214219
m = _mm_cvtepu8_epi32(t);

Source/cmake_core.cmake

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,6 @@ macro(astcenc_set_properties ASTCENC_TARGET_NAME ASTCENC_VENEER_TYPE)
196196
PRIVATE
197197
$<${is_gnu_fe}:-fsanitize=address>
198198
$<${is_clang}:-fuse-ld=lld>)
199-
200199
endif()
201200

202201
if(${ASTCENC_UBSAN})

0 commit comments

Comments
 (0)