1 #include "clar_libgit2.h"
2 
3 #include "filebuf.h"
4 #include "futils.h"
5 #include "posix.h"
6 
7 #define TEST_CONFIG "git-test-config"
8 
9 static git_buf buf = GIT_BUF_INIT;
10 
test_config_stress__initialize(void)11 void test_config_stress__initialize(void)
12 {
13 	git_filebuf file = GIT_FILEBUF_INIT;
14 
15 	cl_git_pass(git_filebuf_open(&file, TEST_CONFIG, 0, 0666));
16 
17 	git_filebuf_printf(&file, "[color]\n\tui = auto\n");
18 	git_filebuf_printf(&file, "[core]\n\teditor = \n");
19 
20 	cl_git_pass(git_filebuf_commit(&file));
21 }
22 
test_config_stress__cleanup(void)23 void test_config_stress__cleanup(void)
24 {
25 	git_buf_dispose(&buf);
26 	p_unlink(TEST_CONFIG);
27 }
28 
test_config_stress__dont_break_on_invalid_input(void)29 void test_config_stress__dont_break_on_invalid_input(void)
30 {
31 	git_config *config;
32 
33 	cl_assert(git_path_exists(TEST_CONFIG));
34 	cl_git_pass(git_config_open_ondisk(&config, TEST_CONFIG));
35 
36 	cl_git_pass(git_config_get_string_buf(&buf, config, "color.ui"));
37 	cl_git_pass(git_config_get_string_buf(&buf, config, "core.editor"));
38 
39 	git_config_free(config);
40 }
41 
assert_config_value(git_config * config,const char * key,const char * value)42 void assert_config_value(git_config *config, const char *key, const char *value)
43 {
44 	git_buf_clear(&buf);
45 	cl_git_pass(git_config_get_string_buf(&buf, config, key));
46 	cl_assert_equal_s(value, git_buf_cstr(&buf));
47 }
48 
test_config_stress__comments(void)49 void test_config_stress__comments(void)
50 {
51 	git_config *config;
52 
53 	cl_git_pass(git_config_open_ondisk(&config, cl_fixture("config/config12")));
54 
55 	assert_config_value(config, "some.section.test2", "hello");
56 	assert_config_value(config, "some.section.test3", "welcome");
57 	assert_config_value(config, "some.section.other", "hello! \" ; ; ; ");
58 	assert_config_value(config, "some.section.other2", "cool! \" # # # ");
59 	assert_config_value(config, "some.section.multi", "hi, this is a ; multiline comment # with ;\n special chars and other stuff !@#");
60 	assert_config_value(config, "some.section.multi2", "good, this is a ; multiline comment # with ;\n special chars and other stuff !@#");
61 	assert_config_value(config, "some.section.back", "this is \ba phrase");
62 	assert_config_value(config, "some.section.dollar", "some $sign");
63 	assert_config_value(config, "some.section.multiquotes", "!ls  x                     ls  # comment2                     $HOME");
64 	assert_config_value(config, "some.section.multiquotes2", "!ls  x                      ls  \"# comment2                      $HOME\"");
65 	assert_config_value(config, "some.section.multiquotes3", "hi # ho there are # more quotes");
66 	assert_config_value(config, "some.section.quotecomment", "hi # ho there are # more");
67 
68 	git_config_free(config);
69 }
70 
test_config_stress__escape_subsection_names(void)71 void test_config_stress__escape_subsection_names(void)
72 {
73 	git_config *config;
74 
75 	cl_assert(git_path_exists("git-test-config"));
76 	cl_git_pass(git_config_open_ondisk(&config, TEST_CONFIG));
77 
78 	cl_git_pass(git_config_set_string(config, "some.sec\\tion.other", "foo"));
79 	git_config_free(config);
80 
81 	cl_git_pass(git_config_open_ondisk(&config, TEST_CONFIG));
82 
83 	assert_config_value(config, "some.sec\\tion.other", "foo");
84 
85 	git_config_free(config);
86 }
87 
test_config_stress__trailing_backslash(void)88 void test_config_stress__trailing_backslash(void)
89 {
90 	git_config *config;
91 	const char *path =  "C:\\iam\\some\\windows\\path\\";
92 
93 	cl_assert(git_path_exists("git-test-config"));
94 	cl_git_pass(git_config_open_ondisk(&config, TEST_CONFIG));
95 	cl_git_pass(git_config_set_string(config, "windows.path", path));
96 	git_config_free(config);
97 
98 	cl_git_pass(git_config_open_ondisk(&config, TEST_CONFIG));
99 	assert_config_value(config, "windows.path", path);
100 
101 	git_config_free(config);
102 }
103 
test_config_stress__complex(void)104 void test_config_stress__complex(void)
105 {
106 	git_config *config;
107 	const char *path = "./config-immediate-multiline";
108 
109 	cl_git_mkfile(path, "[imm]\n multi = \"\\\nfoo\"");
110 	cl_git_pass(git_config_open_ondisk(&config, path));
111 	assert_config_value(config, "imm.multi", "foo");
112 
113 	git_config_free(config);
114 }
115 
test_config_stress__quick_write(void)116 void test_config_stress__quick_write(void)
117 {
118 	git_config *config_w, *config_r;
119 	const char *path = "./config-quick-write";
120 	const char *key = "quick.write";
121 	int32_t i;
122 
123 	/* Create an external writer for one instance with the other one */
124 	cl_git_pass(git_config_open_ondisk(&config_w, path));
125 	cl_git_pass(git_config_open_ondisk(&config_r, path));
126 
127 	/* Write and read in the same second (repeat to increase the chance of it happening) */
128 	for (i = 0; i < 10; i++) {
129 		int32_t val;
130 		cl_git_pass(git_config_set_int32(config_w, key, i));
131 		cl_msleep(1);
132 		cl_git_pass(git_config_get_int32(&val, config_r, key));
133 		cl_assert_equal_i(i, val);
134 	}
135 
136 	git_config_free(config_r);
137 	git_config_free(config_w);
138 }
139 
foreach_cb(const git_config_entry * entry,void * payload)140 static int foreach_cb(const git_config_entry *entry, void *payload)
141 {
142 	if (!strcmp(entry->name, "key.value")) {
143 		*(char **)payload = git__strdup(entry->value);
144 		return 0;
145 	}
146 	return -1;
147 }
148 
test_config_stress__foreach_refreshes(void)149 void test_config_stress__foreach_refreshes(void)
150 {
151 	git_config *config_w, *config_r;
152 	char *value = NULL;
153 
154 	cl_git_pass(git_config_open_ondisk(&config_w, "./cfg"));
155 	cl_git_pass(git_config_open_ondisk(&config_r, "./cfg"));
156 
157 	cl_git_pass(git_config_set_string(config_w, "key.value", "1"));
158 	cl_git_pass(git_config_foreach_match(config_r, "key.value", foreach_cb, &value));
159 
160 	cl_assert_equal_s(value, "1");
161 
162 	git_config_free(config_r);
163 	git_config_free(config_w);
164 	git__free(value);
165 }
166 
test_config_stress__foreach_refreshes_snapshot(void)167 void test_config_stress__foreach_refreshes_snapshot(void)
168 {
169 	git_config *config, *snapshot;
170 	char *value = NULL;
171 
172 	cl_git_pass(git_config_open_ondisk(&config, "./cfg"));
173 
174 	cl_git_pass(git_config_set_string(config, "key.value", "1"));
175 	cl_git_pass(git_config_snapshot(&snapshot, config));
176 	cl_git_pass(git_config_foreach_match(snapshot, "key.value", foreach_cb, &value));
177 
178 	cl_assert_equal_s(value, "1");
179 
180 	git_config_free(snapshot);
181 	git_config_free(config);
182 	git__free(value);
183 }
184 
test_config_stress__huge_section_with_many_values(void)185 void test_config_stress__huge_section_with_many_values(void)
186 {
187 	git_config *config;
188 
189 	if (!cl_is_env_set("GITTEST_INVASIVE_SPEED"))
190 		cl_skip();
191 
192 	/*
193 	 * The config file is structured in such a way that is
194 	 * has a section header that is approximately 500kb of
195 	 * size followed by 40k entries. While the resulting
196 	 * configuration file itself is roughly 650kb in size and
197 	 * thus considered to be rather small, in the past we'd
198 	 * balloon to more than 20GB of memory (20000x500kb)
199 	 * while parsing the file. It thus was a trivial way to
200 	 * cause an out-of-memory situation and thus cause denial
201 	 * of service, e.g. via gitmodules.
202 	 */
203 	cl_git_pass(git_config_open_ondisk(&config, cl_fixture("config/config-oom")));
204 
205 	git_config_free(config);
206 }
207