/src/git/oss-fuzz/fuzz-cmd-base.c
Line  | Count  | Source (jump to first uncovered line)  | 
1  |  | /* Copyright 2022 Google LLC  | 
2  |  | Licensed under the Apache License, Version 2.0 (the "License");  | 
3  |  | you may not use this file except in compliance with the License.  | 
4  |  | You may obtain a copy of the License at  | 
5  |  |       http://www.apache.org/licenses/LICENSE-2.0  | 
6  |  | Unless required by applicable law or agreed to in writing, software  | 
7  |  | distributed under the License is distributed on an "AS IS" BASIS,  | 
8  |  | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  | 
9  |  | See the License for the specific language governing permissions and  | 
10  |  | limitations under the License.  | 
11  |  | */  | 
12  |  | #include "builtin.h"  | 
13  |  | #include "hex.h"  | 
14  |  | #include "strbuf.h"  | 
15  |  | #include "fuzz-cmd-base.h"  | 
16  |  |  | 
17  |  |  | 
18  |  | /*  | 
19  |  |  * This function is used to randomize the content of a file with the  | 
20  |  |  * random data. The random data normally come from the fuzzing engine  | 
21  |  |  * LibFuzzer in order to create randomization of the git file worktree  | 
22  |  |  * and possibly messing up of certain git config file to fuzz different  | 
23  |  |  * git command execution logic. Return -1 if it fails to create the file.  | 
24  |  |  */  | 
25  |  | int randomize_git_file(char *dir, char *name, const unsigned char *data, int size)  | 
26  | 188  | { | 
27  | 188  |   FILE *fp;  | 
28  | 188  |   int ret = 0;  | 
29  | 188  |   struct strbuf fname = STRBUF_INIT;  | 
30  |  |  | 
31  | 188  |   strbuf_addf(&fname, "%s/%s", dir, name);  | 
32  |  |  | 
33  | 188  |   fp = fopen(fname.buf, "wb");  | 
34  | 188  |   if (fp)  | 
35  | 188  |   { | 
36  | 188  |     fwrite(data, 1, size, fp);  | 
37  | 188  |   }  | 
38  | 0  |   else  | 
39  | 0  |   { | 
40  | 0  |     ret = -1;  | 
41  | 0  |   }  | 
42  |  |  | 
43  | 188  |   fclose(fp);  | 
44  | 188  |   strbuf_release(&fname);  | 
45  |  |  | 
46  | 188  |   return ret;  | 
47  | 188  | }  | 
48  |  |  | 
49  |  | /*  | 
50  |  |  * This function is a variant of the above function which takes  | 
51  |  |  * a set of target files to be processed. These target file are  | 
52  |  |  * passing to the above function one by one for content rewrite.  | 
53  |  |  * The data is equally divided for each of the files, and the  | 
54  |  |  * remaining bytes (if not divisible) will be ignored.  | 
55  |  |  */  | 
56  |  | void randomize_git_files(char *dir, char *name_set[],  | 
57  |  |   int files_count, const unsigned char *data, int size)  | 
58  | 0  | { | 
59  | 0  |   int i;  | 
60  | 0  |   int data_size = size / files_count;  | 
61  |  | 
  | 
62  | 0  |   for (i = 0; i < files_count; i++)  | 
63  | 0  |   { | 
64  | 0  |     randomize_git_file(dir, name_set[i], data + (i * data_size), data_size);  | 
65  | 0  |   }  | 
66  | 0  | }  | 
67  |  |  | 
68  |  | /*  | 
69  |  |  * Instead of randomizing the content of existing files. This helper  | 
70  |  |  * function helps generate a temp file with random file name before  | 
71  |  |  * passing to the above functions to get randomized content for later  | 
72  |  |  * fuzzing of git command.  | 
73  |  |  */  | 
74  |  | void generate_random_file(const unsigned char *data, int size)  | 
75  | 0  | { | 
76  | 0  |   unsigned char *hash = xmallocz_gently(size);  | 
77  | 0  |   const unsigned char *data_chunk = data + size;  | 
78  | 0  |   struct strbuf fname = STRBUF_INIT;  | 
79  |  | 
  | 
80  | 0  |   if (!hash || !data_chunk)  | 
81  | 0  |   { | 
82  | 0  |     return;  | 
83  | 0  |   }  | 
84  |  |  | 
85  | 0  |   memcpy(hash, data, size);  | 
86  |  | 
  | 
87  | 0  |   strbuf_addf(&fname, "TEMP-%s-TEMP", hash_to_hex(hash));  | 
88  | 0  |   randomize_git_file(".", fname.buf, data_chunk, size); | 
89  |  | 
  | 
90  | 0  |   free(hash);  | 
91  | 0  |   strbuf_release(&fname);  | 
92  | 0  | }  | 
93  |  |  | 
94  |  | /*  | 
95  |  |  * This function provides a shorthand for generate commit in master  | 
96  |  |  * branch.  | 
97  |  |  */  | 
98  |  | int generate_commit(const unsigned char *data, int size)  | 
99  | 0  | { | 
100  | 0  |   return generate_commit_in_branch(data, size, "master");  | 
101  | 0  | }  | 
102  |  |  | 
103  |  | /*  | 
104  |  |  * This function helps to generate random commit and build up a  | 
105  |  |  * worktree with randomization to provide a target for the fuzzing  | 
106  |  |  * of git command under specific branch.  | 
107  |  |  */  | 
108  |  | int generate_commit_in_branch(const unsigned char *data, int size, char *branch_name)  | 
109  | 0  | { | 
110  | 0  |   char *argv[4];  | 
111  |  | 
  | 
112  | 0  |   generate_random_file(data, size);  | 
113  |  | 
  | 
114  | 0  |   argv[0] = "add";  | 
115  | 0  |   argv[1] = "TEMP-*-TEMP";  | 
116  | 0  |   argv[2] = NULL;  | 
117  | 0  |   if (cmd_add(2, (const char **)argv, (const char *)""))  | 
118  | 0  |   { | 
119  | 0  |     return -1;  | 
120  | 0  |   }  | 
121  |  |  | 
122  | 0  |   argv[0] = "commit";  | 
123  | 0  |   argv[1] = "-m\"New Commit\"";  | 
124  | 0  |   argv[2] = NULL;  | 
125  | 0  |   if (cmd_commit(2, (const char **)argv, (const char *)""))  | 
126  | 0  |   { | 
127  | 0  |     return -2;  | 
128  | 0  |   }  | 
129  | 0  |     return 0;  | 
130  | 0  | }  | 
131  |  |  | 
132  |  | /*  | 
133  |  |  * In some cases, there maybe some fuzzing logic that will mess  | 
134  |  |  * up with the git repository and its configuration and settings.  | 
135  |  |  * This function integrates into the fuzzing processing and  | 
136  |  |  * reset the git repository into the default  | 
137  |  |  * base settings before each round of fuzzing.  | 
138  |  |  * Return 0 for success.  | 
139  |  |  */  | 
140  |  | int reset_git_folder(void)  | 
141  | 0  | { | 
142  | 0  |   char *argv[6];  | 
143  | 0  |   argv[0] = "init";  | 
144  | 0  |   argv[1] = "--quiet";  | 
145  | 0  |   argv[2] = NULL;  | 
146  | 0  |   if (cmd_init_db(2, (const char **)argv, (const char *)""))  | 
147  | 0  |   { | 
148  | 0  |     return -1;  | 
149  | 0  |   }  | 
150  |  |  | 
151  |  |   /*  | 
152  |  |   printf("R2\n"); | 
153  |  |   argv[0] = "config";  | 
154  |  |   argv[1] = "--global";  | 
155  |  |   argv[2] = "user.name";  | 
156  |  |   argv[3] = "\"FUZZ\"";  | 
157  |  |   argv[4] = NULL;  | 
158  |  |   if (cmd_config(4, (const char **)argv, (const char *)""))  | 
159  |  |   { | 
160  |  |     return -2;  | 
161  |  |   }  | 
162  |  |  | 
163  |  |   printf("R3\n"); | 
164  |  |   argv[0] = "config";  | 
165  |  |   argv[1] = "--global";  | 
166  |  |   argv[2] = "user.email";  | 
167  |  |   argv[3] = "\"FUZZ@LOCALHOST\"";  | 
168  |  |   argv[4] = NULL;  | 
169  |  |   if (cmd_config(4, (const char **)argv, (const char *)""))  | 
170  |  |   { | 
171  |  |     return -3;  | 
172  |  |   }  | 
173  |  |  | 
174  |  |   printf("R4\n"); | 
175  |  |   argv[0] = "config";  | 
176  |  |   argv[1] = "--global";  | 
177  |  |   argv[2] = "safe.directory";  | 
178  |  |   argv[3] = "\"*\"";  | 
179  |  |   argv[4] = NULL;  | 
180  |  |   if (cmd_config(4, (const char **)argv, (const char *)""))  | 
181  |  |   { | 
182  |  |     return -4;  | 
183  |  |   }  | 
184  |  |   */  | 
185  | 0  |   argv[0] = "add";  | 
186  | 0  |   argv[1] = "TEMP_1";  | 
187  | 0  |   argv[2] = "TEMP_2";  | 
188  | 0  |   argv[3] = NULL;  | 
189  | 0  |   if (cmd_add(3, (const char **)argv, (const char *)""))  | 
190  | 0  |   { | 
191  | 0  |     return -5;  | 
192  | 0  |   }  | 
193  |  |  | 
194  | 0  |   argv[0] = "commit";  | 
195  | 0  |   argv[1] = "-m\"First Commit\"";  | 
196  | 0  |   argv[2] = NULL;  | 
197  | 0  |   if (cmd_commit(2, (const char **)argv, (const char *)""))  | 
198  | 0  |   { | 
199  | 0  |     return -6;  | 
200  | 0  |   }  | 
201  |  |  | 
202  | 0  |   return 0;  | 
203  | 0  | }  | 
204  |  |  | 
205  |  | /*  | 
206  |  |  * This helper function returns the maximum number of commit can  | 
207  |  |  * be generated by the provided random data without reusing the  | 
208  |  |  * data to increase randomization of the fuzzing target and allow  | 
209  |  |  * more path of fuzzing to be covered.  | 
210  |  |  */  | 
211  |  | int get_max_commit_count(int data_size, int git_files_count, int reserve_size)  | 
212  | 0  | { | 
213  | 0  |   int count = (data_size - reserve_size  - git_files_count * HASH_SIZE) / (HASH_HEX_SIZE);  | 
214  |  | 
  | 
215  | 0  |   if (count > 20)  | 
216  | 0  |   { | 
217  | 0  |     count = 20;  | 
218  | 0  |   }  | 
219  |  | 
  | 
220  | 0  |   return count;  | 
221  | 0  | }  | 
222  |  |  | 
223  |  | static char template_directory_env[350];  | 
224  |  |  | 
225  |  | void create_templ_dir(void)  | 
226  | 1  | { | 
227  |  |   /*  | 
228  |  |   * Create an empty and accessible template directory.  | 
229  |  |   */  | 
230  | 1  |   char template_directory[250];  | 
231  | 1  |   snprintf(template_directory, 250, "/tmp/templatedir-%d", getpid());  | 
232  | 1  |   struct stat stats;  | 
233  | 1  |   if (stat(template_directory, &stats) != 0 || S_ISDIR(stats.st_mode) == 0)  | 
234  | 1  |   { | 
235  | 1  |     mkdir(template_directory, 0777);  | 
236  | 1  |   }  | 
237  | 1  |   snprintf(template_directory_env, 350,  | 
238  | 1  |       "GIT_TEMPLATE_DIR=%s", template_directory);  | 
239  | 1  |   putenv(template_directory_env);  | 
240  | 1  | }  | 
241  |  |  | 
242  |  | void put_envs(void)  | 
243  | 1  | { | 
244  | 1  |   putenv("GIT_CONFIG_NOSYSTEM=true"); | 
245  | 1  |   putenv("GIT_AUTHOR_EMAIL=FUZZ@LOCALHOST"); | 
246  | 1  |   putenv("GIT_AUTHOR_NAME=FUZZ"); | 
247  | 1  |   putenv("GIT_COMMITTER_NAME=FUZZ"); | 
248  | 1  |   putenv("GIT_COMMITTER_EMAIL=FUZZ@LOCALHOST"); | 
249  | 1  | }  |