Skip to content

Commit f611fac

Browse files
committed
Ensure correct behaviour of printf formatting
* Add configuration check to ensure that compiler and runtime support C99 length modifier 'z' * Add emv_debug_test to ensure that debugging mechanism correctly applies C99 length modifier 'z' * Let emv_debug_internal() and emv_str_list_add() specify format checking according to the compiler being used The above is intended to address the paradoxes created by MinGW's use of MSVCRT. MSYS2's MinGW provides its own printf() family of functions to compensate for MSVCRT's lack of C99 compatibility, but unfortunately still issues a warning for length modifiers that would otherwise work with its own printf() family of functions.
1 parent 2136eeb commit f611fac

File tree

5 files changed

+110
-3
lines changed

5 files changed

+110
-3
lines changed

src/CMakeLists.txt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,38 @@
77

88
cmake_minimum_required(VERSION 3.16)
99

10+
# Check whether C99 length modifier 'z' is supported
11+
include(CheckCSourceRuns)
12+
check_c_source_runs("
13+
#include <stdarg.h>
14+
#include <stdio.h>
15+
#include <string.h>
16+
#if defined(__clang__)
17+
__attribute__((format(printf, 1, 4)))
18+
#elif defined(__GNUC__)
19+
__attribute__((format(gnu_printf, 1, 4)))
20+
#endif
21+
static int test_formatting(const char* fmt, char* s, size_t s_len, ...)
22+
{
23+
int r;
24+
va_list ap;
25+
va_start(ap, s_len);
26+
r = vsnprintf(s, s_len, fmt, ap);
27+
va_end(ap);
28+
return r;
29+
}
30+
int main(void)
31+
{
32+
char s[16] = { 0 };
33+
size_t x = 1337;
34+
return test_formatting(\"s=%zu\", s, sizeof(s), x) != 6 || strcmp(s, \"s=1337\") != 0;
35+
}"
36+
C99_length_modifier_z_is_supported
37+
)
38+
if(NOT C99_length_modifier_z_is_supported)
39+
message(FATAL_ERROR "C99 length modifier 'z' not supported")
40+
endif()
41+
1042
# Check for time.h and clock_gettime()
1143
include(CheckIncludeFile)
1244
check_include_file(time.h HAVE_TIME_H)

src/emv_debug.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,17 @@ void emv_debug_internal(
108108
const void* buf,
109109
size_t buf_len,
110110
...
111-
) __attribute__((format(printf, 4, 7)));
111+
)
112+
#if defined(__clang__)
113+
// Check for Clang first because it also defines __GNUC__
114+
__attribute__((format(printf, 4, 7)));
115+
#elif defined(__GNUC__)
116+
// Use gnu_printf for GCC and MSYS2's MinGW
117+
__attribute__((format(gnu_printf, 4, 7)));
118+
#else
119+
// Otherwise no format string checks
120+
;
121+
#endif
112122

113123
#ifdef EMV_DEBUG_ENABLED
114124

src/emv_strings.c

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,17 @@
3131
#include <stdio.h> // for vsnprintf() and snprintf()
3232
#include <ctype.h>
3333

34+
#if defined(__clang__)
35+
// Check for Clang first because it also defines __GNUC__
36+
#define ATTRIBUTE_FORMAT_PRINTF(str_idx, va_idx) __attribute__((format(printf, str_idx, va_idx)))
37+
#elif defined(__GNUC__)
38+
// Use gnu_printf for GCC and MSYS2's MinGW
39+
#define ATTRIBUTE_FORMAT_PRINTF(str_idx, va_idx) __attribute__((format(gnu_printf, str_idx, va_idx)))
40+
#else
41+
// Otherwise no format string checks
42+
#define ATTRIBUTE_FORMAT_PRINTF(str_idx, va_idx)
43+
#endif
44+
3445
struct str_itr_t {
3546
char* ptr;
3647
size_t len;
@@ -40,7 +51,7 @@ struct str_itr_t {
4051
static int emv_tlv_value_get_string(const struct emv_tlv_t* tlv, enum emv_format_t format, size_t max_format_len, char* value_str, size_t value_str_len);
4152
static int emv_uint_to_str(uint32_t value, char* str, size_t str_len);
4253
static void emv_str_list_init(struct str_itr_t* itr, char* buf, size_t len);
43-
static void emv_str_list_add(struct str_itr_t* itr, const char* fmt, ...) __attribute__((format(printf, 2, 3)));
54+
static void emv_str_list_add(struct str_itr_t* itr, const char* fmt, ...) ATTRIBUTE_FORMAT_PRINTF(2, 3);
4455
static int emv_country_alpha2_code_get_string(const uint8_t* buf, size_t buf_len, char* str, size_t str_len);
4556
static int emv_country_alpha3_code_get_string(const uint8_t* buf, size_t buf_len, char* str, size_t str_len);
4657
static int emv_country_numeric_code_get_string(const uint8_t* buf, size_t buf_len, char* str, size_t str_len);
@@ -1559,7 +1570,7 @@ static void emv_str_list_init(struct str_itr_t* itr, char* buf, size_t len)
15591570
itr->len = len;
15601571
}
15611572

1562-
__attribute__((format(printf, 2, 3)))
1573+
ATTRIBUTE_FORMAT_PRINTF(2, 3)
15631574
static void emv_str_list_add(struct str_itr_t* itr, const char* fmt, ...)
15641575
{
15651576
int r;

tests/CMakeLists.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,17 @@
88
cmake_minimum_required(VERSION 3.16)
99

1010
if (BUILD_TESTING)
11+
add_executable(emv_debug_test emv_debug_test.c)
12+
target_link_libraries(emv_debug_test PRIVATE print_helpers emv)
13+
add_test(emv_debug_test emv_debug_test)
14+
string(CONCAT emv_debug_test_regex
15+
"\\[APP\\] asdf=1337[\r\n]"
16+
)
17+
set_tests_properties(emv_debug_test
18+
PROPERTIES
19+
PASS_REGULAR_EXPRESSION ${emv_debug_test_regex}
20+
)
21+
1122
add_executable(emv_atr_parse_test emv_atr_parse_test.c)
1223
target_link_libraries(emv_atr_parse_test PRIVATE print_helpers emv)
1324
add_test(emv_atr_parse_test emv_atr_parse_test)

tests/emv_debug_test.c

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/**
2+
* @file emv_debug_test.c
3+
* @brief Unit tests for EMV debug implementation
4+
*
5+
* Copyright (c) 2023 Leon Lynch
6+
*
7+
* This program is free software; you can redistribute it and/or
8+
* modify it under the terms of the GNU Lesser General Public
9+
* License as published by the Free Software Foundation; either
10+
* version 2.1 of the License, or (at your option) any later version.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15+
* Lesser General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Lesser General Public
18+
* License along with this program. If not, see
19+
* <https://www.gnu.org/licenses/>.
20+
*/
21+
22+
#define EMV_DEBUG_SOURCE EMV_DEBUG_SOURCE_APP
23+
#include "emv_debug.h"
24+
#include "print_helpers.h"
25+
26+
#include <stdint.h>
27+
#include <stdio.h>
28+
29+
int main(void)
30+
{
31+
int r;
32+
size_t asdf = 1337;
33+
34+
// Enable debug output
35+
r = emv_debug_init(EMV_DEBUG_SOURCE_ALL, EMV_DEBUG_ALL, &print_emv_debug);
36+
if (r) {
37+
fprintf(stderr, "emv_debug_init() failed; r=%d\n", r);
38+
return 1;
39+
}
40+
41+
// Test whether compiler supports length modifier z both at compile-time and runtime
42+
emv_debug_error("asdf=%zu", asdf);
43+
}

0 commit comments

Comments
 (0)