summaryrefslogtreecommitdiffstats
path: root/src/lib/test-printf-format-fix.c
blob: c9d6203de30bb7c5081957eac4916e404786d070 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
/* Copyright (c) 2001-2018 Dovecot authors, see the included COPYING file */

/* Unit tests for printf-format-fix helper */

#include "test-lib.h"
#include "printf-format-fix.h"

#include <string.h>

struct format_fix_rewrites {
	const char *input;
	const char *output;
	size_t      length;
};

static void test_unchanged()
{
	static const char *tests[] = {
		"Hello world",
		"Embedded %%, %u, %f, %s, etc. are OK",
		"Allow %#0- +s flags",
		"duplicate flags in different args %0-123s %0-123s",
		"Minimum length %9999s",
		"Minimum length parameter %*s",
		"Precision %.9999s",
		"Precision %1.9999s",
		"Precision parameter %1.*s %.*s",
		"Floating precisions such as %.0f %0.4f %-4.0f",
		"Length modifiers %hd %hhd %ld %lld %Lg %jd %zd %td",
		"Specifiers %s %u %d %c %i %x %X %p %o %e %E %f %F %g %G %a %A",
		"%%doesn't cause confusion in %%m and %%n",
	};
	unsigned int i;

	test_begin("printf_format_fix(safe)");
	for (i = 0; i < N_ELEMENTS(tests); ++i) {
		size_t len;
		T_BEGIN {
			test_assert_idx(printf_format_fix(tests[i])
					== tests[i], i);
			test_assert_idx(printf_format_fix_get_len(tests[i], &len)
					== tests[i], i);
			test_assert_idx(len == strlen(tests[i]), i);
		} T_END;
	}
	test_end();
}

static void test_ok_changes()
{
	static const char *tests[] = {
		"OK to have a trailing %m",
		"%m can appear at the start too",
		"Even %m in the middle with a confusing %%m elsewhere is OK",
	};
	unsigned int i;
	const char *needle;
	unsigned int needlen;
	int old_errno = errno;

	test_begin("printf_format_fix(rewrites)");

	errno = EINVAL;
	needle = strerror(errno);
	i_assert(needle != NULL);
	needlen = strlen(needle);

	for (i = 0; i < N_ELEMENTS(tests); ++i) {
		size_t len;
		char const *chgd;
		char const *insert;
		unsigned int offs;

		T_BEGIN {
			chgd = printf_format_fix(tests[i]);
			test_assert_idx(chgd != tests[i], i);
			insert = strstr(chgd, needle);
			test_assert_idx(insert != NULL, i);
			offs = insert - chgd;
			test_assert_idx(memcmp(chgd, tests[i], offs) == 0, i);
			test_assert_idx(memcmp(chgd+offs, needle, needlen) == 0, i);
			test_assert_idx(strcmp(chgd+offs+needlen, tests[i]+offs+2) == 0, i);

			chgd = printf_format_fix_get_len(tests[i], &len);
			test_assert_idx(chgd != tests[i], i);
			test_assert_idx(len == strlen(chgd), i);
			insert = strstr(chgd, needle);
			test_assert_idx(insert != NULL, i);
			offs = insert - chgd;
			test_assert_idx(memcmp(chgd, tests[i], offs) == 0, i);
			test_assert_idx(memcmp(chgd+offs, needle, needlen) == 0, i);
			test_assert_idx(memcmp(chgd+offs+needlen, tests[i]+offs+2, len-needlen-offs) == 0, i);
		} T_END;
	}

	errno = old_errno;

	test_end();
}

void test_printf_format_fix()
{
	test_unchanged();
	test_ok_changes();
}

/* Want to test the panics too? go for it! */
enum fatal_test_state fatal_printf_format_fix(unsigned int stage)
{
	static const struct {
		const char *format;
		const char *expected_fatal;
	} fatals[] = {
		{ "no no no %n's", "%n modifier used" },
		{ "no no no %-1234567890123n's with extra stuff", "Too large minimum field width" },
		{ "%m allowed once, but not twice: %m", "%m used twice" },
		{ "%m must not obscure a later %n", "%n modifier used" },
		{ "definitely can't have a tailing %", "Missing % specifier" },
		{ "Evil %**%n", "Unsupported 0x2a specifier" },
		{ "Evil %*#%99999$s", "Unsupported 0x23 specifier" },
		{ "No weird %% with %0%", "Unsupported 0x25 specifier" },
		{ "No duplicate modifiers %00s", "Duplicate % flag '0'" },
		{ "Minimum length can't be too long %10000s", "Too large minimum field width" },
		{ "Minimum length doesn't support %*1$s", "Unsupported 0x31 specifier" },
		{ "Precision can't be too long %.10000s", "Too large precision" },
		{ "Precision can't be too long %1.10000s", "Too large precision" },
		{ "Precision doesn't support %1.-1s", "Unsupported 0x2d specifier" },
	};

	if(stage >= N_ELEMENTS(fatals)) {
		test_end();
		return FATAL_TEST_FINISHED;
	}

	if(stage == 0)
		test_begin("fatal_printf_format_fix");

	/* let's crash! */
	test_expect_fatal_string(fatals[stage].expected_fatal);
	(void)printf_format_fix(fatals[stage].format);
	return FATAL_TEST_FAILURE;
}