From: Eric Sesterhenn <eric.sesterhenn@xxxxxxxxxxx> Git's fuzz tests are run continuously as part of OSS-Fuzz [1]. Several additional fuzz tests have been contributed directly to OSS-Fuzz; however, these tests are vulnerable to bitrot because they are not built during Git's CI runs, and thus breaking changes are much less likely to be noticed by Git contributors. Port one of these tests back to the Git project: fuzz-parse-attr-line This test was originally written by Eric Sesterhenn as part of a security audit of Git [2]. It was then contributed to the OSS-Fuzz repo in commit c58ac4492 (Git fuzzing: uncomment the existing and add new targets. (#11486), 2024-02-21) by Jaroslav Lobačevski. I (Josh Steadmon) have verified with both Eric and Jaroslav that they're OK with moving this test to the Git project. [1] https://github.com/google/oss-fuzz [2] https://ostif.org/wp-content/uploads/2023/01/X41-OSTIF-Gitlab-Git-Security-Audit-20230117-public.pdf Co-authored-by: Jaroslav Lobačevski <jarlob@xxxxxxxxx> Co-authored-by: Josh Steadmon <steadmon@xxxxxxxxxx> Signed-off-by: Josh Steadmon <steadmon@xxxxxxxxxx> --- Makefile | 1 + attr.c | 38 +------------------------ attr.h | 43 +++++++++++++++++++++++++++++ ci/run-build-and-minimal-fuzzers.sh | 1 + oss-fuzz/.gitignore | 1 + oss-fuzz/fuzz-parse-attr-line.c | 39 ++++++++++++++++++++++++++ 6 files changed, 86 insertions(+), 37 deletions(-) create mode 100644 oss-fuzz/fuzz-parse-attr-line.c diff --git a/Makefile b/Makefile index 3ce391062f..141e194bf5 100644 --- a/Makefile +++ b/Makefile @@ -2382,6 +2382,7 @@ FUZZ_OBJS += oss-fuzz/fuzz-credential-from-url-gently.o FUZZ_OBJS += oss-fuzz/fuzz-date.o FUZZ_OBJS += oss-fuzz/fuzz-pack-headers.o FUZZ_OBJS += oss-fuzz/fuzz-pack-idx.o +FUZZ_OBJS += oss-fuzz/fuzz-parse-attr-line.o .PHONY: fuzz-objs fuzz-objs: $(FUZZ_OBJS) diff --git a/attr.c b/attr.c index 06b5b5e55e..f7898285c5 100644 --- a/attr.c +++ b/attr.c @@ -259,42 +259,6 @@ const struct git_attr *git_attr(const char *name) return git_attr_internal(name, strlen(name)); } -/* What does a matched pattern decide? */ -struct attr_state { - const struct git_attr *attr; - const char *setto; -}; - -struct pattern { - const char *pattern; - int patternlen; - int nowildcardlen; - unsigned flags; /* PATTERN_FLAG_* */ -}; - -/* - * One rule, as from a .gitattributes file. - * - * If is_macro is true, then u.attr is a pointer to the git_attr being - * defined. - * - * If is_macro is false, then u.pat is the filename pattern to which the - * rule applies. - * - * In either case, num_attr is the number of attributes affected by - * this rule, and state is an array listing them. The attributes are - * listed as they appear in the file (macros unexpanded). - */ -struct match_attr { - union { - struct pattern pat; - const struct git_attr *attr; - } u; - char is_macro; - size_t num_attr; - struct attr_state state[FLEX_ARRAY]; -}; - static const char blank[] = " \t\r\n"; /* Flags usable in read_attr() and parse_attr_line() family of functions. */ @@ -353,7 +317,7 @@ static const char *parse_attr(const char *src, int lineno, const char *cp, return ep + strspn(ep, blank); } -static struct match_attr *parse_attr_line(const char *line, const char *src, +struct match_attr *parse_attr_line(const char *line, const char *src, int lineno, unsigned flags) { size_t namelen, num_attr, i; diff --git a/attr.h b/attr.h index bb33b60880..2319ef31e3 100644 --- a/attr.h +++ b/attr.h @@ -240,4 +240,47 @@ int git_attr_system_is_enabled(void); extern char *git_attr_tree; +/* + * Exposed for fuzz-testing only. + */ + +/* What does a matched pattern decide? */ +struct attr_state { + const struct git_attr *attr; + const char *setto; +}; + +struct pattern { + const char *pattern; + int patternlen; + int nowildcardlen; + unsigned flags; /* PATTERN_FLAG_* */ +}; + +/* + * One rule, as from a .gitattributes file. + * + * If is_macro is true, then u.attr is a pointer to the git_attr being + * defined. + * + * If is_macro is false, then u.pat is the filename pattern to which the + * rule applies. + * + * In either case, num_attr is the number of attributes affected by + * this rule, and state is an array listing them. The attributes are + * listed as they appear in the file (macros unexpanded). + */ +struct match_attr { + union { + struct pattern pat; + const struct git_attr *attr; + } u; + char is_macro; + size_t num_attr; + struct attr_state state[FLEX_ARRAY]; +}; + +struct match_attr *parse_attr_line(const char *line, const char *src, + int lineno, unsigned flags); + #endif /* ATTR_H */ diff --git a/ci/run-build-and-minimal-fuzzers.sh b/ci/run-build-and-minimal-fuzzers.sh index d9d3ad23c7..60fe8b0dfc 100755 --- a/ci/run-build-and-minimal-fuzzers.sh +++ b/ci/run-build-and-minimal-fuzzers.sh @@ -20,6 +20,7 @@ credential-from-url-gently \ date \ pack-headers \ pack-idx \ +parse-attr-line \ " for fuzzer in $fuzzers ; do diff --git a/oss-fuzz/.gitignore b/oss-fuzz/.gitignore index 2cfc845b20..ec185f061c 100644 --- a/oss-fuzz/.gitignore +++ b/oss-fuzz/.gitignore @@ -4,3 +4,4 @@ fuzz-credential-from-url-gently fuzz-date fuzz-pack-headers fuzz-pack-idx +fuzz-parse-attr-line diff --git a/oss-fuzz/fuzz-parse-attr-line.c b/oss-fuzz/fuzz-parse-attr-line.c new file mode 100644 index 0000000000..45a4c4e53c --- /dev/null +++ b/oss-fuzz/fuzz-parse-attr-line.c @@ -0,0 +1,39 @@ +#include "git-compat-util.h" +#include <stddef.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include "attr.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + struct match_attr *res; + char *buf; + + buf = malloc(size + 1); + if (!buf) + return 0; + + memcpy(buf, data, size); + buf[size] = 0; + + res = parse_attr_line(buf, "dummy", 0, 0); + + if (res) { + int j; + for (j = 0; j < res->num_attr; j++) { + const char *setto = res->state[j].setto; + if (ATTR_TRUE(setto) || ATTR_FALSE(setto) || + ATTR_UNSET(setto)) + ; + else + free((char *)setto); + } + free(res); + } + free(buf); + + return 0; +} -- 2.47.0.rc1.288.g06298d1525-goog