Merge branch 'jk/hash-algo-leak-fixes'
Various code paths that initialize a cryptographic hash context but bail out or finish without calling 'git_hash_final()' have been taught to call 'git_hash_discard()' to release allocated resources, fixing memory leaks when Git is built with non-default backends like 'OpenSSL' or 'libgcrypt'. * jk/hash-algo-leak-fixes: hash: add platform-specific discard functions hash: fix memory leak copying sha256 gcrypt handles http: discard hash in dumb-http http_object_request check_stream_oid(): discard hash on read error patch-id: discard hash when done csum-file: provide a function to release checkpoints csum-file: always finalize or discard hash hash: add discard primitive csum-file: drop discard_hashfile()main
commit
e47f7de7de
|
|
@ -1216,6 +1216,7 @@ static void stream_blob(uintmax_t len, struct object_id *oidout, uintmax_t mark)
|
|||
out:
|
||||
free(in_buf);
|
||||
free(out_buf);
|
||||
hashfile_checkpoint_release(&checkpoint);
|
||||
}
|
||||
|
||||
/* All calls must be guarded by find_object() or find_mark() to
|
||||
|
|
|
|||
|
|
@ -173,6 +173,7 @@ static size_t get_one_patchid(struct object_id *next_oid, struct object_id *resu
|
|||
oidclr(next_oid, the_repository->hash_algo);
|
||||
|
||||
flush_one_hunk(result, &ctx);
|
||||
git_hash_discard(&ctx);
|
||||
|
||||
return patchlen;
|
||||
}
|
||||
|
|
|
|||
30
csum-file.c
30
csum-file.c
|
|
@ -55,13 +55,19 @@ void hashflush(struct hashfile *f)
|
|||
}
|
||||
}
|
||||
|
||||
void free_hashfile(struct hashfile *f)
|
||||
static void free_hashfile_memory(struct hashfile *f)
|
||||
{
|
||||
free(f->buffer);
|
||||
free(f->check_buffer);
|
||||
free(f);
|
||||
}
|
||||
|
||||
void free_hashfile(struct hashfile *f)
|
||||
{
|
||||
git_hash_discard(&f->ctx);
|
||||
free_hashfile_memory(f);
|
||||
}
|
||||
|
||||
int finalize_hashfile(struct hashfile *f, unsigned char *result,
|
||||
enum fsync_component component, unsigned int flags)
|
||||
{
|
||||
|
|
@ -69,10 +75,12 @@ int finalize_hashfile(struct hashfile *f, unsigned char *result,
|
|||
|
||||
hashflush(f);
|
||||
|
||||
if (f->skip_hash)
|
||||
if (f->skip_hash) {
|
||||
git_hash_discard(&f->ctx);
|
||||
hashclr(f->buffer, f->algop);
|
||||
else
|
||||
} else {
|
||||
git_hash_final(f->buffer, &f->ctx);
|
||||
}
|
||||
|
||||
if (result)
|
||||
hashcpy(result, f->buffer, f->algop);
|
||||
|
|
@ -97,19 +105,10 @@ int finalize_hashfile(struct hashfile *f, unsigned char *result,
|
|||
if (close(f->check_fd))
|
||||
die_errno("%s: sha1 file error on close", f->name);
|
||||
}
|
||||
free_hashfile(f);
|
||||
free_hashfile_memory(f);
|
||||
return fd;
|
||||
}
|
||||
|
||||
void discard_hashfile(struct hashfile *f)
|
||||
{
|
||||
if (0 <= f->check_fd)
|
||||
close(f->check_fd);
|
||||
if (0 <= f->fd)
|
||||
close(f->fd);
|
||||
free_hashfile(f);
|
||||
}
|
||||
|
||||
void hashwrite(struct hashfile *f, const void *buf, uint32_t count)
|
||||
{
|
||||
while (count) {
|
||||
|
|
@ -224,6 +223,11 @@ int hashfile_truncate(struct hashfile *f, struct hashfile_checkpoint *checkpoint
|
|||
return 0;
|
||||
}
|
||||
|
||||
void hashfile_checkpoint_release(struct hashfile_checkpoint *checkpoint)
|
||||
{
|
||||
git_hash_discard(&checkpoint->ctx);
|
||||
}
|
||||
|
||||
void crc32_begin(struct hashfile *f)
|
||||
{
|
||||
f->crc32 = crc32(0, NULL, 0);
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ struct hashfile_checkpoint {
|
|||
void hashfile_checkpoint_init(struct hashfile *, struct hashfile_checkpoint *);
|
||||
void hashfile_checkpoint(struct hashfile *, struct hashfile_checkpoint *);
|
||||
int hashfile_truncate(struct hashfile *, struct hashfile_checkpoint *);
|
||||
void hashfile_checkpoint_release(struct hashfile_checkpoint *);
|
||||
|
||||
/* finalize_hashfile flags */
|
||||
#define CSUM_CLOSE 1
|
||||
|
|
@ -74,7 +75,6 @@ void free_hashfile(struct hashfile *f);
|
|||
* Finalize the hashfile by flushing data to disk and free'ing it.
|
||||
*/
|
||||
int finalize_hashfile(struct hashfile *, unsigned char *, enum fsync_component, unsigned int);
|
||||
void discard_hashfile(struct hashfile *);
|
||||
void hashwrite(struct hashfile *, const void *, uint32_t);
|
||||
void hashflush(struct hashfile *f);
|
||||
void crc32_begin(struct hashfile *);
|
||||
|
|
|
|||
1
diff.c
1
diff.c
|
|
@ -6987,6 +6987,7 @@ static int diff_get_patch_id(struct diff_options *options, struct object_id *oid
|
|||
flush_one_hunk(oid, &ctx);
|
||||
}
|
||||
|
||||
git_hash_discard(&ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
29
hash.c
29
hash.c
|
|
@ -72,6 +72,11 @@ static void git_hash_sha1_final_oid(struct object_id *oid, struct git_hash_ctx *
|
|||
oid->algo = GIT_HASH_SHA1;
|
||||
}
|
||||
|
||||
static void git_hash_sha1_discard(struct git_hash_ctx *ctx)
|
||||
{
|
||||
git_SHA1_Discard(&ctx->state.sha1);
|
||||
}
|
||||
|
||||
static void git_hash_sha1_init_unsafe(struct git_hash_ctx *ctx)
|
||||
{
|
||||
ctx->algop = unsafe_hash_algo(&hash_algos[GIT_HASH_SHA1]);
|
||||
|
|
@ -102,6 +107,11 @@ static void git_hash_sha1_final_oid_unsafe(struct object_id *oid, struct git_has
|
|||
oid->algo = GIT_HASH_SHA1;
|
||||
}
|
||||
|
||||
static void git_hash_sha1_discard_unsafe(struct git_hash_ctx *ctx)
|
||||
{
|
||||
git_SHA1_Discard_unsafe(&ctx->state.sha1_unsafe);
|
||||
}
|
||||
|
||||
static void git_hash_sha256_init(struct git_hash_ctx *ctx)
|
||||
{
|
||||
ctx->algop = unsafe_hash_algo(&hash_algos[GIT_HASH_SHA256]);
|
||||
|
|
@ -135,6 +145,11 @@ static void git_hash_sha256_final_oid(struct object_id *oid, struct git_hash_ctx
|
|||
oid->algo = GIT_HASH_SHA256;
|
||||
}
|
||||
|
||||
static void git_hash_sha256_discard(struct git_hash_ctx *ctx)
|
||||
{
|
||||
git_SHA256_Discard(&ctx->state.sha256);
|
||||
}
|
||||
|
||||
static void git_hash_unknown_init(struct git_hash_ctx *ctx UNUSED)
|
||||
{
|
||||
BUG("trying to init unknown hash");
|
||||
|
|
@ -165,6 +180,11 @@ static void git_hash_unknown_final_oid(struct object_id *oid UNUSED,
|
|||
BUG("trying to finalize unknown hash");
|
||||
}
|
||||
|
||||
static void git_hash_unknown_discard(struct git_hash_ctx *ctx UNUSED)
|
||||
{
|
||||
BUG("trying to discard unknown hash");
|
||||
}
|
||||
|
||||
static const struct git_hash_algo sha1_unsafe_algo = {
|
||||
.name = "sha1",
|
||||
.format_id = GIT_SHA1_FORMAT_ID,
|
||||
|
|
@ -176,6 +196,7 @@ static const struct git_hash_algo sha1_unsafe_algo = {
|
|||
.update_fn = git_hash_sha1_update_unsafe,
|
||||
.final_fn = git_hash_sha1_final_unsafe,
|
||||
.final_oid_fn = git_hash_sha1_final_oid_unsafe,
|
||||
.discard_fn = git_hash_sha1_discard_unsafe,
|
||||
.empty_tree = &empty_tree_oid,
|
||||
.empty_blob = &empty_blob_oid,
|
||||
.null_oid = &null_oid_sha1,
|
||||
|
|
@ -193,6 +214,7 @@ const struct git_hash_algo hash_algos[GIT_HASH_NALGOS] = {
|
|||
.update_fn = git_hash_unknown_update,
|
||||
.final_fn = git_hash_unknown_final,
|
||||
.final_oid_fn = git_hash_unknown_final_oid,
|
||||
.discard_fn = git_hash_unknown_discard,
|
||||
.empty_tree = NULL,
|
||||
.empty_blob = NULL,
|
||||
.null_oid = NULL,
|
||||
|
|
@ -208,6 +230,7 @@ const struct git_hash_algo hash_algos[GIT_HASH_NALGOS] = {
|
|||
.update_fn = git_hash_sha1_update,
|
||||
.final_fn = git_hash_sha1_final,
|
||||
.final_oid_fn = git_hash_sha1_final_oid,
|
||||
.discard_fn = git_hash_sha1_discard,
|
||||
.unsafe = &sha1_unsafe_algo,
|
||||
.empty_tree = &empty_tree_oid,
|
||||
.empty_blob = &empty_blob_oid,
|
||||
|
|
@ -224,6 +247,7 @@ const struct git_hash_algo hash_algos[GIT_HASH_NALGOS] = {
|
|||
.update_fn = git_hash_sha256_update,
|
||||
.final_fn = git_hash_sha256_final,
|
||||
.final_oid_fn = git_hash_sha256_final_oid,
|
||||
.discard_fn = git_hash_sha256_discard,
|
||||
.empty_tree = &empty_tree_oid_sha256,
|
||||
.empty_blob = &empty_blob_oid_sha256,
|
||||
.null_oid = &null_oid_sha256,
|
||||
|
|
@ -283,6 +307,11 @@ void git_hash_final_oid(struct object_id *oid, struct git_hash_ctx *ctx)
|
|||
ctx->algop->final_oid_fn(oid, ctx);
|
||||
}
|
||||
|
||||
void git_hash_discard(struct git_hash_ctx *ctx)
|
||||
{
|
||||
ctx->algop->discard_fn(ctx);
|
||||
}
|
||||
|
||||
uint32_t hash_algo_by_name(const char *name)
|
||||
{
|
||||
if (!name)
|
||||
|
|
|
|||
22
hash.h
22
hash.h
|
|
@ -37,6 +37,7 @@
|
|||
# define platform_SHA1_Clone_unsafe openssl_SHA1_Clone
|
||||
# define platform_SHA1_Update_unsafe openssl_SHA1_Update
|
||||
# define platform_SHA1_Final_unsafe openssl_SHA1_Final
|
||||
# define platform_SHA1_Discard_unsafe openssl_SHA1_Discard
|
||||
# else
|
||||
# define platform_SHA_CTX_unsafe SHA_CTX
|
||||
# define platform_SHA1_Init_unsafe SHA1_Init
|
||||
|
|
@ -92,6 +93,7 @@
|
|||
# define platform_SHA1_Final_unsafe platform_SHA1_Final
|
||||
# ifdef platform_SHA1_Clone
|
||||
# define platform_SHA1_Clone_unsafe platform_SHA1_Clone
|
||||
# define platform_SHA1_Discard_unsafe platform_SHA1_Discard
|
||||
# endif
|
||||
# ifdef SHA1_NEEDS_CLONE_HELPER
|
||||
# define SHA1_NEEDS_CLONE_HELPER_UNSAFE
|
||||
|
|
@ -110,9 +112,11 @@
|
|||
|
||||
#ifdef platform_SHA1_Clone
|
||||
#define git_SHA1_Clone platform_SHA1_Clone
|
||||
#define git_SHA1_Discard platform_SHA1_Discard
|
||||
#endif
|
||||
#ifdef platform_SHA1_Clone_unsafe
|
||||
# define git_SHA1_Clone_unsafe platform_SHA1_Clone_unsafe
|
||||
# define git_SHA1_Discard_unsafe platform_SHA1_Discard_unsafe
|
||||
#endif
|
||||
|
||||
#ifndef platform_SHA256_CTX
|
||||
|
|
@ -129,6 +133,7 @@
|
|||
|
||||
#ifdef platform_SHA256_Clone
|
||||
#define git_SHA256_Clone platform_SHA256_Clone
|
||||
#define git_SHA256_Discard platform_SHA256_Discard
|
||||
#endif
|
||||
|
||||
#ifdef SHA1_MAX_BLOCK_SIZE
|
||||
|
|
@ -142,6 +147,10 @@ static inline void git_SHA1_Clone(git_SHA_CTX *dst, const git_SHA_CTX *src)
|
|||
{
|
||||
memcpy(dst, src, sizeof(*dst));
|
||||
}
|
||||
static inline void git_SHA1_Discard(git_SHA_CTX *ctx UNUSED)
|
||||
{
|
||||
/* noop */
|
||||
}
|
||||
#endif
|
||||
#ifndef SHA1_NEEDS_CLONE_HELPER_UNSAFE
|
||||
static inline void git_SHA1_Clone_unsafe(git_SHA_CTX_unsafe *dst,
|
||||
|
|
@ -149,6 +158,10 @@ static inline void git_SHA1_Clone_unsafe(git_SHA_CTX_unsafe *dst,
|
|||
{
|
||||
memcpy(dst, src, sizeof(*dst));
|
||||
}
|
||||
static inline void git_SHA1_Discard_unsafe(git_SHA_CTX_unsafe *ctx UNUSED)
|
||||
{
|
||||
/* noop */
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef SHA256_NEEDS_CLONE_HELPER
|
||||
|
|
@ -156,6 +169,10 @@ static inline void git_SHA256_Clone(git_SHA256_CTX *dst, const git_SHA256_CTX *s
|
|||
{
|
||||
memcpy(dst, src, sizeof(*dst));
|
||||
}
|
||||
static inline void git_SHA256_Discard(git_SHA256_CTX *ctx UNUSED)
|
||||
{
|
||||
/* noop */
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
|
@ -271,6 +288,7 @@ typedef void (*git_hash_clone_fn)(struct git_hash_ctx *dst, const struct git_has
|
|||
typedef void (*git_hash_update_fn)(struct git_hash_ctx *ctx, const void *in, size_t len);
|
||||
typedef void (*git_hash_final_fn)(unsigned char *hash, struct git_hash_ctx *ctx);
|
||||
typedef void (*git_hash_final_oid_fn)(struct object_id *oid, struct git_hash_ctx *ctx);
|
||||
typedef void (*git_hash_discard_fn)(struct git_hash_ctx *ctx);
|
||||
|
||||
struct git_hash_algo {
|
||||
/*
|
||||
|
|
@ -306,6 +324,9 @@ struct git_hash_algo {
|
|||
/* The hash finalization function for object IDs. */
|
||||
git_hash_final_oid_fn final_oid_fn;
|
||||
|
||||
/* Discard an initialized hash without finalizing. */
|
||||
git_hash_discard_fn discard_fn;
|
||||
|
||||
/* The OID of the empty tree. */
|
||||
const struct object_id *empty_tree;
|
||||
|
||||
|
|
@ -325,6 +346,7 @@ void git_hash_clone(struct git_hash_ctx *dst, const struct git_hash_ctx *src);
|
|||
void git_hash_update(struct git_hash_ctx *ctx, const void *in, size_t len);
|
||||
void git_hash_final(unsigned char *hash, struct git_hash_ctx *ctx);
|
||||
void git_hash_final_oid(struct object_id *oid, struct git_hash_ctx *ctx);
|
||||
void git_hash_discard(struct git_hash_ctx *ctx);
|
||||
const struct git_hash_algo *hash_algo_ptr_by_number(uint32_t algo);
|
||||
struct git_hash_ctx *git_hash_alloc(void);
|
||||
void git_hash_free(struct git_hash_ctx *ctx);
|
||||
|
|
|
|||
4
http.c
4
http.c
|
|
@ -2880,6 +2880,7 @@ struct http_object_request *new_http_object_request(const char *base_url,
|
|||
git_inflate_init(&freq->stream);
|
||||
|
||||
the_hash_algo->init_fn(&freq->c);
|
||||
freq->hash_ctx_valid = 1;
|
||||
|
||||
freq->url = get_remote_object_url(base_url, hex, 0);
|
||||
|
||||
|
|
@ -2988,6 +2989,7 @@ int finish_http_object_request(struct http_object_request *freq)
|
|||
}
|
||||
|
||||
git_hash_final_oid(&freq->real_oid, &freq->c);
|
||||
freq->hash_ctx_valid = 0;
|
||||
if (freq->zret != Z_STREAM_END) {
|
||||
unlink_or_warn(freq->tmpfile.buf);
|
||||
return -1;
|
||||
|
|
@ -3028,6 +3030,8 @@ void release_http_object_request(struct http_object_request **freq_p)
|
|||
curl_slist_free_all(freq->headers);
|
||||
strbuf_release(&freq->tmpfile);
|
||||
git_inflate_end(&freq->stream);
|
||||
if (freq->hash_ctx_valid)
|
||||
git_hash_discard(&freq->c);
|
||||
|
||||
free(freq);
|
||||
*freq_p = NULL;
|
||||
|
|
|
|||
1
http.h
1
http.h
|
|
@ -255,6 +255,7 @@ struct http_object_request {
|
|||
struct object_id oid;
|
||||
struct object_id real_oid;
|
||||
struct git_hash_ctx c;
|
||||
int hash_ctx_valid;
|
||||
git_zstream stream;
|
||||
int zret;
|
||||
int rename;
|
||||
|
|
|
|||
|
|
@ -1352,6 +1352,8 @@ static int odb_transaction_files_write_object_stream(struct odb_transaction *bas
|
|||
state->alloc_written);
|
||||
state->written[state->nr_written++] = idx;
|
||||
}
|
||||
|
||||
hashfile_checkpoint_release(&checkpoint);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1585,11 +1587,13 @@ static int check_stream_oid(git_zstream *stream,
|
|||
|
||||
if (status != Z_STREAM_END) {
|
||||
error(_("corrupt loose object '%s'"), oid_to_hex(expected_oid));
|
||||
git_hash_discard(&c);
|
||||
return -1;
|
||||
}
|
||||
if (stream->avail_in) {
|
||||
error(_("garbage at end of loose object '%s'"),
|
||||
oid_to_hex(expected_oid));
|
||||
git_hash_discard(&c);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -40,12 +40,18 @@ static inline void openssl_SHA1_Clone(struct openssl_SHA1_CTX *dst,
|
|||
EVP_MD_CTX_copy_ex(dst->ectx, src->ectx);
|
||||
}
|
||||
|
||||
static inline void openssl_SHA1_Discard(struct openssl_SHA1_CTX *ctx)
|
||||
{
|
||||
EVP_MD_CTX_free(ctx->ectx);
|
||||
}
|
||||
|
||||
#ifndef platform_SHA_CTX
|
||||
#define platform_SHA_CTX openssl_SHA1_CTX
|
||||
#define platform_SHA1_Init openssl_SHA1_Init
|
||||
#define platform_SHA1_Clone openssl_SHA1_Clone
|
||||
#define platform_SHA1_Update openssl_SHA1_Update
|
||||
#define platform_SHA1_Final openssl_SHA1_Final
|
||||
#define platform_SHA1_Discard openssl_SHA1_Discard
|
||||
#endif
|
||||
|
||||
#endif /* SHA1_OPENSSL_H */
|
||||
|
|
|
|||
|
|
@ -27,13 +27,20 @@ static inline void gcrypt_SHA256_Final(unsigned char *digest, gcrypt_SHA256_CTX
|
|||
|
||||
static inline void gcrypt_SHA256_Clone(gcrypt_SHA256_CTX *dst, const gcrypt_SHA256_CTX *src)
|
||||
{
|
||||
gcry_md_close(*dst);
|
||||
gcry_md_copy(dst, *src);
|
||||
}
|
||||
|
||||
static inline void gcrypt_SHA256_Discard(gcrypt_SHA256_CTX *ctx)
|
||||
{
|
||||
gcry_md_close(*ctx);
|
||||
}
|
||||
|
||||
#define platform_SHA256_CTX gcrypt_SHA256_CTX
|
||||
#define platform_SHA256_Init gcrypt_SHA256_Init
|
||||
#define platform_SHA256_Clone gcrypt_SHA256_Clone
|
||||
#define platform_SHA256_Update gcrypt_SHA256_Update
|
||||
#define platform_SHA256_Final gcrypt_SHA256_Final
|
||||
#define platform_SHA256_Discard gcrypt_SHA256_Discard
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -40,10 +40,16 @@ static inline void openssl_SHA256_Clone(struct openssl_SHA256_CTX *dst,
|
|||
EVP_MD_CTX_copy_ex(dst->ectx, src->ectx);
|
||||
}
|
||||
|
||||
static inline void openssl_SHA256_Discard(struct openssl_SHA256_CTX *ctx)
|
||||
{
|
||||
EVP_MD_CTX_free(ctx->ectx);
|
||||
}
|
||||
|
||||
#define platform_SHA256_CTX openssl_SHA256_CTX
|
||||
#define platform_SHA256_Init openssl_SHA256_Init
|
||||
#define platform_SHA256_Clone openssl_SHA256_Clone
|
||||
#define platform_SHA256_Update openssl_SHA256_Update
|
||||
#define platform_SHA256_Final openssl_SHA256_Final
|
||||
#define platform_SHA256_Discard openssl_SHA256_Discard
|
||||
|
||||
#endif /* SHA256_OPENSSL_H */
|
||||
|
|
|
|||
Loading…
Reference in New Issue