diff options
Diffstat (limited to 'script')
-rwxr-xr-x | script/backport.sh | 23 | ||||
-rwxr-xr-x | script/leaks.sh | 6 | ||||
-rwxr-xr-x | script/release.py | 191 | ||||
-rw-r--r-- | script/sanitizers.supp | 4 | ||||
-rw-r--r-- | script/thread-sanitizer.supp | 26 | ||||
-rw-r--r-- | script/user_model.c | 98 | ||||
-rw-r--r-- | script/user_nodefs.h | 34 | ||||
-rwxr-xr-x | script/valgrind.sh | 2 | ||||
-rw-r--r-- | script/valgrind.supp | 244 |
9 files changed, 628 insertions, 0 deletions
diff --git a/script/backport.sh b/script/backport.sh new file mode 100755 index 0000000..3c2f899 --- /dev/null +++ b/script/backport.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +if test $# -eq 0 +then + echo "USAGE: $0 <#PR> [<#PR>...]" + exit +fi + +commits= + +for pr in $* +do + mergecommit=$(git rev-parse ":/Merge pull request #$pr" || exit 1) + mergebase=$(git merge-base "$mergecommit"^1 "$mergecommit"^2 || exit 1) + + commits="$commits $(git rev-list --reverse "$mergecommit"^2 ^"$mergebase")" +done + +echo "Cherry-picking the following commits:" +git rev-list --no-walk --oneline $commits +echo + +git cherry-pick $commits diff --git a/script/leaks.sh b/script/leaks.sh new file mode 100755 index 0000000..efeead5 --- /dev/null +++ b/script/leaks.sh @@ -0,0 +1,6 @@ +#!/bin/sh +export MallocStackLogging=1 +export MallocScribble=1 +export MallocLogFile=/dev/null +export CLAR_AT_EXIT="leaks -quiet \$PPID" +exec "$@" diff --git a/script/release.py b/script/release.py new file mode 100755 index 0000000..1a240de --- /dev/null +++ b/script/release.py @@ -0,0 +1,191 @@ +#!/usr/bin/env python + +from collections import namedtuple + +import argparse +import base64 +import copy +import json +import subprocess +import sys +import urllib.parse +import urllib.request +import urllib.error + +class Error(Exception): + pass + +class Version(object): + def __init__(self, version): + versions = version.split(sep='.') + if len(versions) < 2 or len(versions) > 3: + raise Error("Invalid version string '{}'".format(version)) + self.major = int(versions[0]) + self.minor = int(versions[1]) + self.revision = int(versions[2]) if len(versions) == 3 else 0 + + def __str__(self): + return '{}.{}.{}'.format(self.major, self.minor, self.revision) + + def __eq__(self, other): + return self.major == other.major and self.minor == other.minor and self.revision == other.revision + +def verify_version(version): + expected = { + 'VERSION': [ '"{}"'.format(version), None ], + 'VER_MAJOR': [ str(version.major), None ], + 'VER_MINOR': [ str(version.minor), None ], + 'VER_REVISION': [ str(version.revision), None ], + 'VER_PATCH': [ '0', None ], + 'SOVERSION': [ '"{}.{}"'.format(version.major, version.minor), None ], + } + + # Parse CMakeLists + with open('CMakeLists.txt') as f: + for line in f.readlines(): + if line.startswith('project(libgit2 VERSION "{}"'.format(version)): + break + else: + raise Error("cmake: invalid project definition") + + # Parse version.h + with open('include/git2/version.h') as f: + lines = f.readlines() + + for key in expected.keys(): + define = '#define LIBGIT2_{} '.format(key) + for line in lines: + if line.startswith(define): + expected[key][1] = line[len(define):].strip() + break + else: + raise Error("version.h: missing define for '{}'".format(key)) + + for k, v in expected.items(): + if v[0] != v[1]: + raise Error("version.h: define '{}' does not match (got '{}', expected '{}')".format(k, v[0], v[1])) + + with open('package.json') as f: + pkg = json.load(f) + + try: + pkg_version = Version(pkg["version"]) + except KeyError as err: + raise Error("package.json: missing the field {}".format(err)) + + if pkg_version != version: + raise Error("package.json: version does not match (got '{}', expected '{}')".format(pkg_version, version)) + +def generate_relnotes(tree, version): + with open('docs/changelog.md') as f: + lines = f.readlines() + + if not lines[0].startswith('v'): + raise Error("changelog.md: missing section for v{}".format(version)) + try: + v = Version(lines[0][1:].strip()) + except: + raise Error("changelog.md: invalid version string {}".format(lines[0].strip())) + if v != version: + raise Error("changelog.md: changelog version doesn't match (got {}, expected {})".format(v, version)) + if not lines[1].startswith('----'): + raise Error("changelog.md: missing version header") + if lines[2] != '\n': + raise Error("changelog.md: missing newline after version header") + + for i, line in enumerate(lines[3:]): + if not line.startswith('v'): + continue + try: + Version(line[1:].strip()) + break + except: + continue + else: + raise Error("changelog.md: cannot find section header of preceding release") + + return ''.join(lines[3:i + 3]).strip() + +def git(*args): + process = subprocess.run([ 'git', *args ], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + if process.returncode != 0: + raise Error('Failed executing git {}: {}'.format(' '.join(args), process.stderr.decode())) + return process.stdout + +def post(url, data, contenttype, user, password): + request = urllib.request.Request(url, data=data) + request.add_header('Accept', 'application/json') + request.add_header('Content-Type', contenttype) + request.add_header('Content-Length', len(data)) + request.add_header('Authorization', 'Basic ' + base64.b64encode('{}:{}'.format(user, password).encode()).decode()) + + try: + response = urllib.request.urlopen(request) + if response.getcode() != 201: + raise Error("POST to '{}' failed: {}".format(url, response.reason)) + except urllib.error.URLError as e: + raise Error("POST to '{}' failed: {}".format(url, e)) + data = json.load(response) + + return data + +def generate_asset(version, tree, archive_format): + Asset = namedtuple('Asset', ['name', 'label', 'mimetype', 'data']) + mimetype = 'application/{}'.format('gzip' if archive_format == 'tar.gz' else 'zip') + return Asset( + "libgit2-{}.{}".format(version, archive_format), "Release sources: libgit2-{}.{}".format(version, archive_format), mimetype, + git('archive', '--format', archive_format, '--prefix', 'libgit2-{}/'.format(version), tree) + ) + +def release(args): + params = { + "tag_name": 'v' + str(args.version), + "name": 'libgit2 v' + str(args.version), + "target_commitish": git('rev-parse', args.tree).decode().strip(), + "body": generate_relnotes(args.tree, args.version), + } + assets = [ + generate_asset(args.version, args.tree, 'tar.gz'), + generate_asset(args.version, args.tree, 'zip'), + ] + + if args.dryrun: + for k, v in params.items(): + print('{}: {}'.format(k, v)) + for asset in assets: + print('asset: name={}, label={}, mimetype={}, bytes={}'.format(asset.name, asset.label, asset.mimetype, len(asset.data))) + return + + try: + url = 'https://api.github.com/repos/{}/releases'.format(args.repository) + response = post(url, json.dumps(params).encode(), 'application/json', args.user, args.password) + except Error as e: + raise Error('Could not create release: ' + str(e)) + + for asset in assets: + try: + url = list(urllib.parse.urlparse(response['upload_url'].split('{?')[0])) + url[4] = urllib.parse.urlencode({ 'name': asset.name, 'label': asset.label }) + post(urllib.parse.urlunparse(url), asset.data, asset.mimetype, args.user, args.password) + except Error as e: + raise Error('Could not upload asset: ' + str(e)) + +def main(): + parser = argparse.ArgumentParser(description='Create a libgit2 release') + parser.add_argument('--tree', default='HEAD', help='tree to create release for (default: HEAD)') + parser.add_argument('--dryrun', action='store_true', help='generate release, but do not post it') + parser.add_argument('--repository', default='libgit2/libgit2', help='GitHub repository to create repository in') + parser.add_argument('--user', help='user to authenticate as') + parser.add_argument('--password', help='password to authenticate with') + parser.add_argument('version', type=Version, help='version of the new release') + args = parser.parse_args() + + verify_version(args.version) + release(args) + +if __name__ == '__main__': + try: + main() + except Error as e: + print(e) + sys.exit(1) diff --git a/script/sanitizers.supp b/script/sanitizers.supp new file mode 100644 index 0000000..4e0e9be --- /dev/null +++ b/script/sanitizers.supp @@ -0,0 +1,4 @@ +[undefined] +# This library allows unaligned access on Intel-like processors. Prevent UBSan +# from complaining about that. +fun:sha1_compression_states diff --git a/script/thread-sanitizer.supp b/script/thread-sanitizer.supp new file mode 100644 index 0000000..97d2304 --- /dev/null +++ b/script/thread-sanitizer.supp @@ -0,0 +1,26 @@ +# In attr_file_free, the locks are acquired in the opposite order in which they +# are normally acquired. This is probably something worth fixing to have a +# consistent lock hierarchy that is easy to understand. +deadlock:attr_cache_lock + +# git_mwindow_file_register has the possibility of evicting some files from the +# global cache. In order to avoid races and closing files that are currently +# being accessed, before evicting any file it will attempt to acquire that +# file's lock. Finally, git_mwindow_file_register is typically called with a +# file lock held, because the caller will use the fd in the mwf immediately +# after registering it. This causes ThreadSanitizer to observe different orders +# of acquisition of the mutex (which implies a possibility of a deadlock), +# _but_ since the files are added to the cache after other files have been +# evicted, there cannot be a case where mwf A is trying to be registered while +# evicting mwf B concurrently and viceversa: at most one of them can be present +# in the cache. +deadlock:git_mwindow_file_register + +# When invoking the time/timezone functions from git_signature_now(), they +# access libc methods that need to be instrumented to correctly analyze the +# data races. +called_from_lib:libc.so.6 + +# TODO(#5592): Investigate and fix this. It can be triggered by the `thread` +# test suite. +race:git_filter_list__load_ext diff --git a/script/user_model.c b/script/user_model.c new file mode 100644 index 0000000..4942527 --- /dev/null +++ b/script/user_model.c @@ -0,0 +1,98 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +void *realloc(void *ptr, size_t size); +void *memmove(void *dest, const void *src, size_t n); +size_t strlen(const char *s); + +typedef struct va_list_str *va_list; + +typedef struct git_vector { + void **contents; + size_t length; +} git_vector; + +typedef struct git_buf { + char *ptr; + size_t asize, size; +} git_buf; + +int git_vector_insert(git_vector *v, void *element) +{ + if (!v) + __coverity_panic__(); + + v->contents = realloc(v->contents, ++v->length); + if (!v->contents) + __coverity_panic__(); + v->contents[v->length] = element; + + return 0; +} + +int git_buf_len(const struct git_buf *buf) +{ + return strlen(buf->ptr); +} + +int git_buf_vprintf(git_buf *buf, const char *format, va_list ap) +{ + char ch, *s; + size_t len; + + __coverity_string_null_sink__(format); + __coverity_string_size_sink__(format); + + ch = *format; + ch = *(char *)ap; + + buf->ptr = __coverity_alloc__(len); + __coverity_writeall__(buf->ptr); + buf->size = len; + + return 0; +} + +int git_buf_put(git_buf *buf, const char *data, size_t len) +{ + buf->ptr = __coverity_alloc__(buf->size + len + 1); + memmove(buf->ptr + buf->size, data, len); + buf->size += len; + buf->ptr[buf->size + len] = 0; + return 0; +} + +int git_buf_set(git_buf *buf, const void *data, size_t len) +{ + buf->ptr = __coverity_alloc__(len + 1); + memmove(buf->ptr, data, len); + buf->size = len + 1; + return 0; +} + +void clar__fail( + const char *file, + int line, + const char *error, + const char *description, + int should_abort) +{ + if (should_abort) + __coverity_panic__(); +} + +void clar__assert( + int condition, + const char *file, + int line, + const char *error, + const char *description, + int should_abort) +{ + if (!condition && should_abort) + __coverity_panic__(); +} diff --git a/script/user_nodefs.h b/script/user_nodefs.h new file mode 100644 index 0000000..b6e2df3 --- /dev/null +++ b/script/user_nodefs.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#nodef GIT_ERROR_CHECK_ALLOC(ptr) if (ptr == NULL) { __coverity_panic__(); } +#nodef GIT_ERROR_CHECK_ALLOC_BUF(buf) if (buf == NULL || git_buf_oom(buf)) { __coverity_panic__(); } + +#nodef GITERR_CHECK_ALLOC_ADD(out, one, two) \ + if (GIT_ADD_SIZET_OVERFLOW(out, one, two)) { __coverity_panic__(); } + +#nodef GITERR_CHECK_ALLOC_ADD3(out, one, two, three) \ + if (GIT_ADD_SIZET_OVERFLOW(out, one, two) || \ + GIT_ADD_SIZET_OVERFLOW(out, *(out), three)) { __coverity_panic__(); } + +#nodef GITERR_CHECK_ALLOC_ADD4(out, one, two, three, four) \ + if (GIT_ADD_SIZET_OVERFLOW(out, one, two) || \ + GIT_ADD_SIZET_OVERFLOW(out, *(out), three) || \ + GIT_ADD_SIZET_OVERFLOW(out, *(out), four)) { __coverity_panic__(); } + +#nodef GITERR_CHECK_ALLOC_MULTIPLY(out, nelem, elsize) \ + if (GIT_MULTIPLY_SIZET_OVERFLOW(out, nelem, elsize)) { __coverity_panic__(); } + +#nodef GIT_ERROR_CHECK_VERSION(S,V,N) if (git_error__check_version(S,V,N) < 0) { __coverity_panic__(); } + +#nodef LOOKS_LIKE_DRIVE_PREFIX(S) (strlen(S) >= 2 && git__isalpha((S)[0]) && (S)[1] == ':') + +#nodef git_vector_foreach(v, iter, elem) \ + for ((iter) = 0; (v)->contents != NULL && (iter) < (v)->length && ((elem) = (v)->contents[(iter)], 1); (iter)++ ) + +#nodef git_vector_rforeach(v, iter, elem) \ + for ((iter) = (v)->length - 1; (v)->contents != NULL && (iter) < SIZE_MAX && ((elem) = (v)->contents[(iter)], 1); (iter)-- ) diff --git a/script/valgrind.sh b/script/valgrind.sh new file mode 100755 index 0000000..b5deed2 --- /dev/null +++ b/script/valgrind.sh @@ -0,0 +1,2 @@ +#!/bin/bash +exec valgrind --leak-check=full --show-reachable=yes --error-exitcode=125 --num-callers=50 --suppressions="$(dirname "${BASH_SOURCE[0]}")/valgrind.supp" "$@" diff --git a/script/valgrind.supp b/script/valgrind.supp new file mode 100644 index 0000000..8c4549f --- /dev/null +++ b/script/valgrind.supp @@ -0,0 +1,244 @@ +{ + ignore-zlib-errors-cond + Memcheck:Cond + obj:*libz.so* +} + +{ + ignore-giterror-set-leak + Memcheck:Leak + ... + fun:giterror_set +} + +{ + ignore-git-global-state-leak + Memcheck:Leak + ... + fun:git__global_state +} + +{ + ignore-openssl-ssl-leak + Memcheck:Leak + ... + obj:*libssl.so* + ... +} + +{ + ignore-openssl-crypto-leak + Memcheck:Leak + ... + obj:*libcrypto.so* + ... +} + +{ + ignore-openssl-crypto-cond + Memcheck:Cond + obj:*libcrypto.so* + ... +} + +{ + ignore-openssl-init-leak + Memcheck:Leak + ... + fun:git_openssl_stream_global_init + ... +} + +{ + ignore-openssl-legacy-init-leak + Memcheck:Leak + ... + fun:OPENSSL_init_ssl__legacy + ... +} + +{ + ignore-openssl-malloc-leak + Memcheck:Leak + ... + fun:git_openssl_malloc + ... +} + +{ + ignore-openssl-realloc-leak + Memcheck:Leak + ... + fun:git_openssl_realloc + ... +} + +{ + ignore-glibc-getaddrinfo-cache + Memcheck:Leak + ... + fun:__check_pf +} + +{ + ignore-curl-global-init + Memcheck:Leak + ... + fun:curl_global_init +} + +{ + ignore-libssh2-init + Memcheck:Leak + ... + fun:gcry_control + fun:libssh2_init + ... +} + +{ + ignore-libssh2-session-create + Memcheck:Leak + ... + fun:_git_ssh_session_create + ... +} + +{ + ignore-libssh2-setup-conn + Memcheck:Leak + ... + fun:_git_ssh_setup_conn + ... +} + +{ + ignore-libssh2-gcrypt-control-leak + Memcheck:Leak + ... + fun:gcry_control + obj:*libssh2.so* +} + +{ + ignore-libssh2-gcrypt-mpinew-leak + Memcheck:Leak + ... + fun:gcry_mpi_new + obj:*libssh2.so* +} + +{ + ignore-libssh2-gcrypt-mpiscan-leak + Memcheck:Leak + ... + fun:gcry_mpi_scan + obj:*libssh2.so* + ... +} + +{ + ignore-libssh2-gcrypt-randomize-leak + Memcheck:Leak + ... + fun:gcry_randomize + obj:*libssh2.so* +} + +{ + ignore-libssh2-gcrypt-sexpfindtoken-leak + Memcheck:Leak + ... + fun:gcry_sexp_find_token + obj:*libssh2.so* +} + +{ + ignore-libssh2-gcrypt-pksign-leak + Memcheck:Leak + ... + fun:gcry_pk_sign + obj:*libssh2.so* +} + +{ + ignore-libssh2-gcrypt-session-handshake + Memcheck:Leak + ... + obj:*libssh2.so* + obj:*libssh2.so* + fun:libssh2_session_handshake + ... +} + +{ + ignore-openssl-undefined-in-read + Memcheck:Cond + ... + obj:*libssl.so* + ... + fun:openssl_read + ... +} + +{ + ignore-openssl-undefined-in-connect + Memcheck:Cond + ... + obj:*libssl.so* + ... + fun:openssl_connect + ... +} + +{ + ignore-libssh2-rsa-sha1-sign + Memcheck:Leak + ... + obj:*libgcrypt.so* + fun:_libssh2_rsa_sha1_sign + ... +} + +{ + ignore-libssh2-kexinit + Memcheck:Leak + ... + obj:*libssh2.so* + fun:kexinit + ... +} + +{ + ignore-noai6ai_cached-double-free + Memcheck:Free + fun:free + fun:__libc_freeres + ... + fun:exit + ... +} + +{ + ignore-libcrypto-uninitialized-read-for-entropy + Memcheck:Value8 + ... + obj:*libcrypto.so* + ... +} + +{ + ignore-dlopen-leak + Memcheck:Leak + ... + fun:dlopen + ... +} + +{ + ignore-dlopen-leak + Memcheck:Leak + ... + fun:_dlerror_run + ... +} |