Skip to content

Commit 72686d4

Browse files
EricSesterhennX41JarLobsteadmon
authored andcommitted
fuzz: port fuzz-parse-attr-line from OSS-Fuzz
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 <[email protected]> Co-authored-by: Josh Steadmon <[email protected]> Signed-off-by: Josh Steadmon <[email protected]> Signed-off-by: Taylor Blau <[email protected]>
1 parent 966253d commit 72686d4

File tree

6 files changed

+87
-38
lines changed

6 files changed

+87
-38
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2426,6 +2426,7 @@ FUZZ_OBJS += oss-fuzz/fuzz-credential-from-url-gently.o
24262426
FUZZ_OBJS += oss-fuzz/fuzz-date.o
24272427
FUZZ_OBJS += oss-fuzz/fuzz-pack-headers.o
24282428
FUZZ_OBJS += oss-fuzz/fuzz-pack-idx.o
2429+
FUZZ_OBJS += oss-fuzz/fuzz-parse-attr-line.o
24292430
.PHONY: fuzz-objs
24302431
fuzz-objs: $(FUZZ_OBJS)
24312432

attr.c

Lines changed: 2 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -259,42 +259,6 @@ const struct git_attr *git_attr(const char *name)
259259
return git_attr_internal(name, strlen(name));
260260
}
261261

262-
/* What does a matched pattern decide? */
263-
struct attr_state {
264-
const struct git_attr *attr;
265-
const char *setto;
266-
};
267-
268-
struct pattern {
269-
const char *pattern;
270-
int patternlen;
271-
int nowildcardlen;
272-
unsigned flags; /* PATTERN_FLAG_* */
273-
};
274-
275-
/*
276-
* One rule, as from a .gitattributes file.
277-
*
278-
* If is_macro is true, then u.attr is a pointer to the git_attr being
279-
* defined.
280-
*
281-
* If is_macro is false, then u.pat is the filename pattern to which the
282-
* rule applies.
283-
*
284-
* In either case, num_attr is the number of attributes affected by
285-
* this rule, and state is an array listing them. The attributes are
286-
* listed as they appear in the file (macros unexpanded).
287-
*/
288-
struct match_attr {
289-
union {
290-
struct pattern pat;
291-
const struct git_attr *attr;
292-
} u;
293-
char is_macro;
294-
size_t num_attr;
295-
struct attr_state state[FLEX_ARRAY];
296-
};
297-
298262
static const char blank[] = " \t\r\n";
299263

300264
/* 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,
353317
return ep + strspn(ep, blank);
354318
}
355319

356-
static struct match_attr *parse_attr_line(const char *line, const char *src,
357-
int lineno, unsigned flags)
320+
struct match_attr *parse_attr_line(const char *line, const char *src,
321+
int lineno, unsigned flags)
358322
{
359323
size_t namelen, num_attr, i;
360324
const char *cp, *name, *states;

attr.h

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,4 +240,47 @@ int git_attr_system_is_enabled(void);
240240

241241
extern char *git_attr_tree;
242242

243+
/*
244+
* Exposed for fuzz-testing only.
245+
*/
246+
247+
/* What does a matched pattern decide? */
248+
struct attr_state {
249+
const struct git_attr *attr;
250+
const char *setto;
251+
};
252+
253+
struct pattern {
254+
const char *pattern;
255+
int patternlen;
256+
int nowildcardlen;
257+
unsigned flags; /* PATTERN_FLAG_* */
258+
};
259+
260+
/*
261+
* One rule, as from a .gitattributes file.
262+
*
263+
* If is_macro is true, then u.attr is a pointer to the git_attr being
264+
* defined.
265+
*
266+
* If is_macro is false, then u.pat is the filename pattern to which the
267+
* rule applies.
268+
*
269+
* In either case, num_attr is the number of attributes affected by
270+
* this rule, and state is an array listing them. The attributes are
271+
* listed as they appear in the file (macros unexpanded).
272+
*/
273+
struct match_attr {
274+
union {
275+
struct pattern pat;
276+
const struct git_attr *attr;
277+
} u;
278+
char is_macro;
279+
size_t num_attr;
280+
struct attr_state state[FLEX_ARRAY];
281+
};
282+
283+
struct match_attr *parse_attr_line(const char *line, const char *src,
284+
int lineno, unsigned flags);
285+
243286
#endif /* ATTR_H */

ci/run-build-and-minimal-fuzzers.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ credential-from-url-gently
2020
date
2121
pack-headers
2222
pack-idx
23+
parse-attr-line
2324
"
2425

2526
for fuzzer in $fuzzers; do

oss-fuzz/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ fuzz-credential-from-url-gently
44
fuzz-date
55
fuzz-pack-headers
66
fuzz-pack-idx
7+
fuzz-parse-attr-line

oss-fuzz/fuzz-parse-attr-line.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#include "git-compat-util.h"
2+
#include <stddef.h>
3+
#include <stdlib.h>
4+
#include <stdint.h>
5+
#include <string.h>
6+
#include "attr.h"
7+
8+
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
9+
10+
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
11+
{
12+
struct match_attr *res;
13+
char *buf;
14+
15+
buf = malloc(size + 1);
16+
if (!buf)
17+
return 0;
18+
19+
memcpy(buf, data, size);
20+
buf[size] = 0;
21+
22+
res = parse_attr_line(buf, "dummy", 0, 0);
23+
24+
if (res) {
25+
int j;
26+
for (j = 0; j < res->num_attr; j++) {
27+
const char *setto = res->state[j].setto;
28+
if (ATTR_TRUE(setto) || ATTR_FALSE(setto) ||
29+
ATTR_UNSET(setto))
30+
;
31+
else
32+
free((char *)setto);
33+
}
34+
free(res);
35+
}
36+
free(buf);
37+
38+
return 0;
39+
}

0 commit comments

Comments
 (0)