Skip to content

Commit c6cac73

Browse files
committed
blob: validate that blob sizes fit in a size_t
Our blob size is a `git_off_t`, which is a signed 64 bit int. This may be erroneously negative or larger than `SIZE_MAX`. Ensure that the blob size fits into a `size_t` before casting.
1 parent 3aa6d96 commit c6cac73

File tree

8 files changed

+52
-20
lines changed

8 files changed

+52
-20
lines changed

src/attr_file.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "attrcache.h"
1313
#include "git2/blob.h"
1414
#include "git2/tree.h"
15+
#include "blob.h"
1516
#include "index.h"
1617
#include <ctype.h>
1718

@@ -119,14 +120,18 @@ int git_attr_file__load(
119120
break;
120121
case GIT_ATTR_FILE__FROM_INDEX: {
121122
git_oid id;
123+
git_off_t blobsize;
122124

123125
if ((error = attr_file_oid_from_index(&id, repo, entry->path)) < 0 ||
124126
(error = git_blob_lookup(&blob, repo, &id)) < 0)
125127
return error;
126128

127129
/* Do not assume that data straight from the ODB is NULL-terminated;
128130
* copy the contents of a file to a buffer to work on */
129-
git_buf_put(&content, git_blob_rawcontent(blob), git_blob_rawsize(blob));
131+
blobsize = git_blob_rawsize(blob);
132+
133+
GIT_ERROR_CHECK_BLOBSIZE(blobsize);
134+
git_buf_put(&content, git_blob_rawcontent(blob), (size_t)blobsize);
130135
break;
131136
}
132137
case GIT_ATTR_FILE__FROM_FILE: {

src/blob.c

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,10 @@ git_off_t git_blob_rawsize(const git_blob *blob)
3636

3737
int git_blob__getbuf(git_buf *buffer, git_blob *blob)
3838
{
39-
return git_buf_set(
40-
buffer,
41-
git_blob_rawcontent(blob),
42-
git_blob_rawsize(blob));
39+
git_off_t size = git_blob_rawsize(blob);
40+
41+
GIT_ERROR_CHECK_BLOBSIZE(size);
42+
return git_buf_set(buffer, git_blob_rawcontent(blob), (size_t)size);
4343
}
4444

4545
void git_blob__free(void *_blob)
@@ -389,12 +389,14 @@ int git_blob_create_fromstream_commit(git_oid *out, git_writestream *_stream)
389389
int git_blob_is_binary(const git_blob *blob)
390390
{
391391
git_buf content = GIT_BUF_INIT;
392+
git_off_t size;
392393

393394
assert(blob);
394395

396+
size = git_blob_rawsize(blob);
397+
395398
git_buf_attach_notowned(&content, git_blob_rawcontent(blob),
396-
min(git_blob_rawsize(blob),
397-
GIT_FILTER_BYTES_TO_CHECK_NUL));
399+
(size_t)min(size, GIT_FILTER_BYTES_TO_CHECK_NUL));
398400
return git_buf_text_is_binary(&content);
399401
}
400402

src/blob.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,14 @@ struct git_blob {
2727
unsigned int raw:1;
2828
};
2929

30+
#define GIT_ERROR_CHECK_BLOBSIZE(n) \
31+
do { \
32+
if (!git__is_sizet(n)) { \
33+
git_error_set(GIT_ERROR_NOMEMORY, "blob contents too large to fit in memory"); \
34+
return -1; \
35+
} \
36+
} while(0)
37+
3038
void git_blob__free(void *blob);
3139
int git_blob__parse(void *blob, git_odb_object *obj);
3240
int git_blob__parse_raw(void *blob, const char *data, size_t size);

src/diff_generate.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -564,9 +564,14 @@ int git_diff__oid_for_file(
564564
{
565565
git_index_entry entry;
566566

567+
if (!git__is_sizet(size)) {
568+
git_error_set(GIT_ERROR_NOMEMORY, "file size overflow (for 32-bits) on '%s'", path);
569+
return -1;
570+
}
571+
567572
memset(&entry, 0, sizeof(entry));
568573
entry.mode = mode;
569-
entry.file_size = size;
574+
entry.file_size = (size_t)size;
570575
entry.path = (char *)path;
571576

572577
return git_diff__oid_for_entry(out, diff, &entry, mode, NULL);
@@ -628,7 +633,7 @@ int git_diff__oid_for_entry(
628633
error = git_odb__hashlink(out, full_path.ptr);
629634
diff->base.perf.oid_calculations++;
630635
} else if (!git__is_sizet(entry.file_size)) {
631-
git_error_set(GIT_ERROR_OS, "file size overflow (for 32-bits) on '%s'",
636+
git_error_set(GIT_ERROR_NOMEMORY, "file size overflow (for 32-bits) on '%s'",
632637
entry.path);
633638
error = -1;
634639
} else if (!(error = git_filter_list_load(&fl,

src/diff_stats.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,8 @@ int git_diff_file_stats__full_to_buf(
5454
size_t width)
5555
{
5656
const char *old_path = NULL, *new_path = NULL;
57-
size_t padding, old_size, new_size;
57+
size_t padding;
58+
git_off_t old_size, new_size;
5859

5960
old_path = delta->old_file.path;
6061
new_path = delta->new_file.path;
@@ -96,7 +97,7 @@ int git_diff_file_stats__full_to_buf(
9697

9798
if (delta->flags & GIT_DIFF_FLAG_BINARY) {
9899
if (git_buf_printf(out,
99-
"Bin %" PRIuZ " -> %" PRIuZ " bytes", old_size, new_size) < 0)
100+
"Bin %" PRId64 " -> %" PRId64 " bytes", old_size, new_size) < 0)
100101
goto on_error;
101102
}
102103
else {

src/notes.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "config.h"
1313
#include "iterator.h"
1414
#include "signature.h"
15+
#include "blob.h"
1516

1617
static int note_error_notfound(void)
1718
{
@@ -319,6 +320,7 @@ static int note_new(
319320
git_blob *blob)
320321
{
321322
git_note *note = NULL;
323+
git_off_t blobsize;
322324

323325
note = git__malloc(sizeof(git_note));
324326
GIT_ERROR_CHECK_ALLOC(note);
@@ -329,7 +331,10 @@ static int note_new(
329331
git_signature_dup(&note->committer, git_commit_committer(commit)) < 0)
330332
return -1;
331333

332-
note->message = git__strndup(git_blob_rawcontent(blob), git_blob_rawsize(blob));
334+
blobsize = git_blob_rawsize(blob);
335+
GIT_ERROR_CHECK_BLOBSIZE(blobsize);
336+
337+
note->message = git__strndup(git_blob_rawcontent(blob), (size_t)blobsize);
333338
GIT_ERROR_CHECK_ALLOC(note->message);
334339

335340
*out = note;

src/odb.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "delta.h"
1616
#include "filter.h"
1717
#include "repository.h"
18+
#include "blob.h"
1819

1920
#include "git2/odb_backend.h"
2021
#include "git2/oid.h"
@@ -387,18 +388,17 @@ static void fake_wstream__free(git_odb_stream *_stream)
387388
static int init_fake_wstream(git_odb_stream **stream_p, git_odb_backend *backend, git_off_t size, git_object_t type)
388389
{
389390
fake_wstream *stream;
391+
size_t blobsize;
390392

391-
if (!git__is_ssizet(size)) {
392-
git_error_set(GIT_ERROR_ODB, "object size too large to keep in memory");
393-
return -1;
394-
}
393+
GIT_ERROR_CHECK_BLOBSIZE(size);
394+
blobsize = (size_t)size;
395395

396396
stream = git__calloc(1, sizeof(fake_wstream));
397397
GIT_ERROR_CHECK_ALLOC(stream);
398398

399-
stream->size = size;
399+
stream->size = blobsize;
400400
stream->type = type;
401-
stream->buffer = git__malloc(size);
401+
stream->buffer = git__malloc(blobsize);
402402
if (stream->buffer == NULL) {
403403
git__free(stream);
404404
return -1;

src/reader.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,17 @@ static int tree_reader_read(
3232
tree_reader *reader = (tree_reader *)_reader;
3333
git_tree_entry *tree_entry = NULL;
3434
git_blob *blob = NULL;
35+
git_off_t blobsize;
3536
int error;
3637

3738
if ((error = git_tree_entry_bypath(&tree_entry, reader->tree, filename)) < 0 ||
38-
(error = git_blob_lookup(&blob, git_tree_owner(reader->tree), git_tree_entry_id(tree_entry))) < 0 ||
39-
(error = git_buf_set(out, git_blob_rawcontent(blob), git_blob_rawsize(blob))) < 0)
39+
(error = git_blob_lookup(&blob, git_tree_owner(reader->tree), git_tree_entry_id(tree_entry))) < 0)
40+
goto done;
41+
42+
blobsize = git_blob_rawsize(blob);
43+
GIT_ERROR_CHECK_BLOBSIZE(blobsize);
44+
45+
if ((error = git_buf_set(out, git_blob_rawcontent(blob), (size_t)blobsize)) < 0)
4046
goto done;
4147

4248
if (out_id)

0 commit comments

Comments
 (0)