From 39a4c95af355d4687e79fe0ca7d6849a1366c9a1 Mon Sep 17 00:00:00 2001 From: Arthur Chan Date: Fri, 23 Sep 2022 19:26:32 +0100 Subject: [PATCH] git: Combine multiple git command fuzzer to one (#8579) List of git commands covered GIT_ADD GIT_BRANCH GIT_COMMIT GIT_CONFIG GIT_DIFF GIT_DIFF_FILES GIT_DIFF_INDEX GIT_DIFF_TREE GIT_LS_FILES GIT_LS_TREE GIT_MV GIT_RERERE GIT_STATUS GIT_VERSION --- projects/git/Makefile.diff | 13 +- projects/git/build.sh | 1 + projects/git/fuzz-cmd-base.c | 3 +- projects/git/fuzz-cmd-base.h | 17 ++ projects/git/fuzz-command.c | 307 +++++++++++++++++++++++++++++++++++ 5 files changed, 334 insertions(+), 7 deletions(-) create mode 100644 projects/git/fuzz-command.c diff --git a/projects/git/Makefile.diff b/projects/git/Makefile.diff index 8f5f88980..910d676c5 100644 --- a/projects/git/Makefile.diff +++ b/projects/git/Makefile.diff @@ -1,24 +1,25 @@ diff --git a/Makefile b/Makefile -index 924b864ae8..bd68d0f05a 100644 +index 924b864ae8..3f0e004a5e 100644 --- a/Makefile +++ b/Makefile -@@ -689,9 +689,12 @@ SCRIPTS = $(SCRIPT_SH_GEN) \ - +@@ -689,9 +689,13 @@ SCRIPTS = $(SCRIPT_SH_GEN) \ + ETAGS_TARGET = TAGS - + -FUZZ_OBJS += fuzz-commit-graph.o -FUZZ_OBJS += fuzz-pack-headers.o -FUZZ_OBJS += fuzz-pack-idx.o +FUZZ_OBJS += oss-fuzz/fuzz-commit-graph.o +FUZZ_OBJS += oss-fuzz/fuzz-pack-headers.o +FUZZ_OBJS += oss-fuzz/fuzz-pack-idx.o ++FUZZ_OBJS += oss-fuzz/fuzz-command.o +FUZZ_OBJS += oss-fuzz/fuzz-cmd-status.o +FUZZ_OBJS += oss-fuzz/fuzz-cmd-version.o +FUZZ_OBJS += oss-fuzz/fuzz-cmd-diff.o .PHONY: fuzz-objs fuzz-objs: $(FUZZ_OBJS) - -@@ -1012,6 +1015,7 @@ LIB_OBJS += oid-array.o + +@@ -1012,6 +1016,7 @@ LIB_OBJS += oid-array.o LIB_OBJS += oidmap.o LIB_OBJS += oidset.o LIB_OBJS += oidtree.o diff --git a/projects/git/build.sh b/projects/git/build.sh index c7eb009e5..617a5d0f6 100755 --- a/projects/git/build.sh +++ b/projects/git/build.sh @@ -22,6 +22,7 @@ make -j$(nproc) CC=$CC CXX=$CXX CFLAGS="$CFLAGS" \ FUZZERS="fuzz-pack-headers fuzz-pack-idx fuzz-commit-graph" FUZZERS="$FUZZERS fuzz-cmd-status fuzz-cmd-version fuzz-cmd-diff" +FUZZERS="$FUZZERS fuzz-command" # copy fuzzers for fuzzer in $FUZZERS ; do diff --git a/projects/git/fuzz-cmd-base.c b/projects/git/fuzz-cmd-base.c index bdbdc189f..6aad8bd3b 100644 --- a/projects/git/fuzz-cmd-base.c +++ b/projects/git/fuzz-cmd-base.c @@ -131,7 +131,8 @@ void generate_commit_in_branch(char *data, int size, char *branch_name) strbuf_addf(&push_cmd, "git push origin %s", branch_name); if (system("git add TEMP-*-TEMP") || - system("git commit -m\"New Commit\"")) + system("git commit -m\"New Commit\"") || + system(push_cmd.buf)) { // Just skip the commit if fails strbuf_release(&push_cmd); diff --git a/projects/git/fuzz-cmd-base.h b/projects/git/fuzz-cmd-base.h index d9e12e22d..1ae3727e9 100644 --- a/projects/git/fuzz-cmd-base.h +++ b/projects/git/fuzz-cmd-base.h @@ -15,6 +15,23 @@ limitations under the License. #define HASH_SIZE 20 #define HASH_HEX_SIZE 40 #define INT_SIZE 4 +#define GIT_COMMAND_COUNT 12 + +typedef enum git_command { + GIT_STATUS = 0, + GIT_ADD_COMMIT = 1, + GIT_VERSION = 2, + GIT_CONFIG_RERERE = 3, + GIT_DIFF = 4, + GIT_DIFF_FILES = 5, + GIT_DIFF_TREE = 6, + GIT_DIFF_INDEX = 7, + GIT_BRANCH = 8, + GIT_MV = 9, + GIT_LS_FILES = 10, + GIT_LS_TREE = 11 +} git_command_t; + int randomize_git_file(char *dir, char *name, char *data, int size); void randomize_git_files(char *dir, char *name_set[], diff --git a/projects/git/fuzz-command.c b/projects/git/fuzz-command.c new file mode 100644 index 000000000..d602351db --- /dev/null +++ b/projects/git/fuzz-command.c @@ -0,0 +1,307 @@ +/* Copyright 2022 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +#include "builtin.h" +#include "repository.h" +#include "fuzz-cmd-base.h" + +int cmd_add(int argc, const char **argv, const char *prefix); +int cmd_branch(int argc, const char **argv, const char *prefix); +int cmd_commit(int argc, const char **argv, const char *prefix); +int cmd_config(int argc, const char **argv, const char *prefix); +int cmd_diff(int argc, const char **argv, const char *prefix); +int cmd_diff_files(int argc, const char **argv, const char *prefix); +int cmd_diff_index(int argc, const char **argv, const char *prefix); +int cmd_diff_tree(int argc, const char **argv, const char *prefix); +int cmd_ls_files(int argc, const char **argv, const char *prefix); +int cmd_ls_tree(int argc, const char **argv, const char *prefix); +int cmd_mv(int argc, const char **argv, const char *prefix); +int cmd_rerere(int argc, const char **argv, const char *prefix); +int cmd_status(int argc, const char **argv, const char *prefix); +int cmd_version(int argc, const char **argv, const char *prefix); + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + int i; + int argc; + int no_of_commit; + int max_commit_count; + char *argv[6]; + char *data_chunk; + char *basedir = "./.git"; + git_command_t choice; + struct strbuf name = STRBUF_INIT; + + /* + * Initialize the repository + */ + initialize_the_repository(); + + /* + * End this round of fuzzing if the data is not large enough + */ + if (size <= (HASH_HEX_SIZE * 4 + HASH_SIZE + INT_SIZE * 2)) + { + repo_clear(the_repository); + return 0; + } + + reset_git_folder(); + + /* + * Generate random commit + */ + max_commit_count = get_max_commit_count(size, 0, + HASH_HEX_SIZE * 2 + HASH_SIZE + INT_SIZE * 2); + no_of_commit = (*((int *)data)) % max_commit_count + 1; + data += 4; + size -= 4; + + choice = (*((int *)data)) % GIT_COMMAND_COUNT; + data += 4; + size -= 4; + + data_chunk = xmallocz_gently(HASH_HEX_SIZE); + + if (!data_chunk) + { + repo_clear(the_repository); + return 0; + } + + for (i = 0; i < no_of_commit; i++) + { + memcpy(data_chunk, data, HASH_HEX_SIZE); + generate_commit(data_chunk, HASH_SIZE); + data += HASH_HEX_SIZE; + size -= HASH_HEX_SIZE; + } + + free(data_chunk); + + /* + * Final preparing of the repository settings + */ + repo_clear(the_repository); + if (repo_init(the_repository, basedir, ".")) + { + return 0; + } + + /* + * Alter content of TEMP_1 and TEMP_2 + */ + data_chunk = xmallocz_gently(HASH_SIZE); + memcpy(data_chunk, data, HASH_SIZE); + randomize_git_file(".", "TEMP_1", data_chunk, HASH_SIZE); + memcpy(data_chunk, data + HASH_SIZE, HASH_SIZE); + randomize_git_file(".", "TEMP_2", data_chunk, HASH_SIZE); + data += (HASH_SIZE * 2); + size -= (HASH_SIZE * 2); + free(data_chunk); + + /* + * Generate random name for different usage + */ + data_chunk = xmallocz_gently(HASH_SIZE); + memcpy(data_chunk, data, HASH_SIZE); + strbuf_addf(&name, "name-%s-name", hash_to_hex((unsigned char *)data_chunk)); + data += HASH_SIZE; + size -= HASH_SIZE; + free(data_chunk); + + switch(choice) { + case GIT_STATUS: default: + argv[0] = "status"; + argv[1] = "-v"; + argv[2] = NULL; + cmd_status(2, (const char **)argv, (const char *)""); + + break; + + case GIT_ADD_COMMIT: + argv[0] = "add"; + argv[1] = "-u"; + argv[2] = NULL; + cmd_add(2, (const char **)argv, (const char *)""); + + argv[0] = "commit"; + argv[1] = "-m"; + argv[2] = "\"New Commit\""; + argv[3] = NULL; + cmd_commit(3, (const char **)argv, (const char *)""); + + break; + + case GIT_VERSION: + argv[0] = "version"; + argv[1] = NULL; + cmd_version(1, (const char **)argv, (const char *)""); + + argv[1] = "--build-options"; + argv[2] = NULL; + cmd_version(2, (const char **)argv, (const char *)""); + + break; + + case GIT_CONFIG_RERERE: + argv[0] = "config"; + argv[1] = "--global"; + argv[2] = "rerere.enabled"; + argv[3] = "true"; + argv[4] = NULL; + cmd_config(4, (const char **)argv, (const char *)""); + + argv[0] = "rerere"; + argv[1] = NULL; + cmd_rerere(1, (const char **)argv, (const char *)""); + + argv[1] = "clear"; + argv[2] = NULL; + cmd_rerere(2, (const char **)argv, (const char *)""); + + argv[1] = "diff"; + cmd_rerere(2, (const char **)argv, (const char *)""); + + argv[1] = "remaining"; + cmd_rerere(2, (const char **)argv, (const char *)""); + + argv[1] = "status"; + cmd_rerere(2, (const char **)argv, (const char *)""); + + argv[1] = "gc"; + cmd_rerere(2, (const char **)argv, (const char *)""); + + break; + + case GIT_DIFF: + argv[0] = "diff"; + argv[1] = NULL; + cmd_diff(1, (const char **)argv, (const char *)""); + + argv[1] = "TEMP_1"; + argv[2] = NULL; + cmd_diff(2, (const char **)argv, (const char *)""); + + argv[2] = "TEMP_2"; + argv[3] = NULL; + cmd_diff(3, (const char **)argv, (const char *)""); + + break; + + case GIT_DIFF_FILES: + argv[0] = "diff-files"; + argv[1] = NULL; + cmd_diff_files(1, (const char **)argv, (const char *)""); + + argv[1] = "TEMP_1"; + argv[2] = NULL; + cmd_diff_files(2, (const char **)argv, (const char *)""); + + argv[2] = "TEMP_2"; + argv[3] = NULL; + cmd_diff_files(3, (const char **)argv, (const char *)""); + + break; + + case GIT_DIFF_TREE: + argv[0] = "diff-tree"; + argv[1] = "master"; + argv[2] = "--"; + argv[3] = NULL; + cmd_diff_tree(3, (const char **)argv, (const char *)""); + + break; + + case GIT_DIFF_INDEX: + argv[0] = "diff-index"; + argv[1] = "master"; + argv[2] = "--"; + argv[3] = NULL; + cmd_diff_index(3, (const char **)argv, (const char *)""); + + argv[2] = "--"; + argv[3] = "TEMP_1"; + argv[4] = NULL; + cmd_diff_index(4, (const char **)argv, (const char *)""); + + argv[2] = "--"; + argv[3] = "TEMP_1"; + argv[4] = "TEMP_2"; + argv[5] = NULL; + cmd_diff_index(5, (const char **)argv, (const char *)""); + + break; + + case GIT_BRANCH: + argv[0] = "branch"; + argv[1] = name.buf; + argv[2] = NULL; + cmd_branch(2, (const char **)argv, (const char *)""); + + argv[1] = "-d"; + argv[2] = name.buf; + argv[3] = NULL; + cmd_branch(3, (const char **)argv, (const char *)""); + + break; + + case GIT_MV: + argv[0] = "mv"; + argv[1] = "-k"; + argv[2] = "TEMP_1"; + argv[3] = name.buf; + argv[4] = NULL; + cmd_mv(4, (const char **)argv, (const char *)""); + + argv[1] = "-k"; + argv[2] = name.buf; + argv[3] = "TEMP_1"; + argv[4] = NULL; + cmd_mv(4, (const char **)argv, (const char *)""); + + break; + + case GIT_LS_FILES: + argv[0] = "ls-files"; + argv[1] = NULL; + cmd_ls_files(1, (const char **)argv, (const char *)""); + + argv[1] = "TEMP"; + argv[2] = NULL; + cmd_ls_files(2, (const char **)argv, (const char *)""); + + argv[1] = "-m"; + argv[2] = NULL; + cmd_ls_files(2, (const char **)argv, (const char *)""); + + argv[1] = name.buf; + argv[2] = NULL; + cmd_ls_files(2, (const char **)argv, (const char *)""); + + break; + + case GIT_LS_TREE: + argv[0] = "ls-tree"; + argv[1] = "master"; + argv[2] = NULL; + cmd_ls_tree(2, (const char **)argv, (const char *)""); + + break; + } + + strbuf_release(&name); + repo_clear(the_repository); + + return 0; +}