]> git.ipfire.org Git - thirdparty/git.git/commitdiff
fuzz: port fuzz-parse-attr-line from OSS-Fuzz
authorEric Sesterhenn <eric.sesterhenn@x41-dsec.de>
Mon, 14 Oct 2024 21:04:09 +0000 (14:04 -0700)
committerTaylor Blau <me@ttaylorr.com>
Wed, 16 Oct 2024 22:14:11 +0000 (18:14 -0400)
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@gmail.com>
Co-authored-by: Josh Steadmon <steadmon@google.com>
Signed-off-by: Josh Steadmon <steadmon@google.com>
Signed-off-by: Taylor Blau <me@ttaylorr.com>
Makefile
attr.c
attr.h
ci/run-build-and-minimal-fuzzers.sh
oss-fuzz/.gitignore
oss-fuzz/fuzz-parse-attr-line.c [new file with mode: 0644]

index 22f7585f74e5bda7923e6f4e497e3758db600acd..cfbbd70fdc7dc0ed3cbdfd5bdbca492f418f13d5 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -2426,6 +2426,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 c605d2c1703808668830947331fd4bc9c2a8fb54..192936c4bc8fc623d6a602ea80e53ba5cd6aa05c 100644 (file)
--- 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,8 +317,8 @@ 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,
-                                         int lineno, unsigned flags)
+struct match_attr *parse_attr_line(const char *line, const char *src,
+                                  int lineno, unsigned flags)
 {
        size_t namelen, num_attr, i;
        const char *cp, *name, *states;
diff --git a/attr.h b/attr.h
index bb33b608805a0e4e0664f0b4e42dca1462d4fc19..a04a5210921e222551a48398a7b098d2c449d368 100644 (file)
--- 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 */
index 631796ab8bf8ab4dd89990f44adeffd18c025704..13c85320d6b22ebcf2626f34ca2af311b9512705 100755 (executable)
@@ -20,6 +20,7 @@ credential-from-url-gently
 date
 pack-headers
 pack-idx
+parse-attr-line
 "
 
 for fuzzer in $fuzzers; do
index 2cfc845b202d668328c4776b7d47533f86f6b3b9..ec185f061c78a135447803ddb3f09afda9ec9e3e 100644 (file)
@@ -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 (file)
index 0000000..45a4c4e
--- /dev/null
@@ -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;
+}