aboutsummaryrefslogtreecommitdiffhomepage
path: root/projects/libgit2/download_refs_fuzzer.cc
blob: 939e67f9ad8517f5451702ddbcd81c82c5b847db (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#include <git2.h>
#include <git2/sys/transport.h>

#include <cstring>
#include <cstdlib>
#include <sys/stat.h>

#include <string>

struct fuzz_buffer {
    const uint8_t *data;
    size_t size;
};

class fuzzer_stream {
public:
    git_smart_subtransport_stream base;
    fuzzer_stream(fuzz_buffer data) : readp(data.data), endp(data.data + data.size) {
        base.read = fuzzer_stream::read;
        base.write = fuzzer_stream::write;
        base.free = fuzzer_stream::free;
    }

    int do_read(char *buffer, size_t buf_size, size_t *bytes_read) {
        size_t avail = endp - readp;
        *bytes_read = std::min(buf_size, avail);
        memcpy(buffer, readp, *bytes_read);
        readp += *bytes_read;
        return 0;
    }

    static int read(git_smart_subtransport_stream *stream,
                    char *buffer,
                    size_t buf_size,
                    size_t *bytes_read) {
        fuzzer_stream *fs = reinterpret_cast<fuzzer_stream*>(stream);
        return fs->do_read(buffer, buf_size, bytes_read);
    }

    static int write(git_smart_subtransport_stream *stream,
              const char *buffer,
              size_t len) {
        return 0;
    }

    static void free(git_smart_subtransport_stream *stream) {
        fuzzer_stream *fs = reinterpret_cast<fuzzer_stream*>(stream);
        delete fs;
    }
private:
    const uint8_t *readp;
    const uint8_t *endp;
};

class fuzzer_subtransport {
public:
    git_smart_subtransport base;
    fuzzer_subtransport(git_transport *owner, fuzz_buffer data) : owner(owner), data(data) {
        base.action = fuzzer_subtransport::action;
        base.close = fuzzer_subtransport::close;
        base.free = fuzzer_subtransport::free;
    }

    int do_action(git_smart_subtransport_stream **out,
                  git_smart_subtransport *transport,
                  const char *url,
                  git_smart_service_t action) {
        fuzzer_stream *stream = new fuzzer_stream(this->data);
        *out = &stream->base;
        return 0;
    }

    static int action(git_smart_subtransport_stream **out,
                      git_smart_subtransport *transport,
                      const char *url,
                      git_smart_service_t action) {
        fuzzer_subtransport *ft = reinterpret_cast<fuzzer_subtransport*>(transport);
        return ft->do_action(out, transport, url, action);
    }

    static int close(git_smart_subtransport *transport) {
        return 0;
    }

    static void free(git_smart_subtransport *transport) {
        fuzzer_subtransport *ft = reinterpret_cast<fuzzer_subtransport*>(transport);
        delete ft;
    }

private:
    git_transport *owner;
    fuzz_buffer data;
};

int fuzzer_subtransport_cb(git_smart_subtransport **out,
                           git_transport* owner,
                           void* param) {
    fuzz_buffer *buf = static_cast<fuzz_buffer*>(param);
    fuzzer_subtransport *sub = new fuzzer_subtransport(owner, *buf);

    *out = &sub->base;
    return 0;
}

int create_fuzzer_transport(git_transport **out, git_remote *owner, void *param) {
    git_smart_subtransport_definition fuzzer_subtransport {fuzzer_subtransport_cb, 1, param};
    return git_transport_smart(out, owner, &fuzzer_subtransport);
}

void fuzzer_git_abort(const char *op) {
    const git_error *err  = giterr_last();
    fprintf(stderr, "unexpected libgit error: %s: %s\n",
            op, err ? err->message : "<none>");
    abort();
}


extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
  static git_repository *repo = nullptr;
  if (repo == nullptr) {
      git_libgit2_init();
      char tmp[] = "/tmp/git2.XXXXXX";
      if (mkdtemp(tmp) != tmp) {
          abort();
      }
      int err = git_repository_init(&repo, tmp, true);
      if (err != 0) {
          fuzzer_git_abort("git_repository_init");
      }
  }

  int err;
  git_remote *remote;
  err = git_remote_create_anonymous(&remote, repo, "fuzzer://remote-url");
  if (err != 0) {
      fuzzer_git_abort("git_remote_create");
  }


  fuzz_buffer buffer = {data, size};
  git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
  callbacks.transport = create_fuzzer_transport;
  callbacks.payload = &buffer;

  err = git_remote_connect(remote, GIT_DIRECTION_FETCH, &callbacks, nullptr, nullptr);
  if (err != 0) {
      goto out;
  }

  git_remote_download(remote, nullptr, nullptr);

 out:
  git_remote_free(remote);

  return 0;
}