Skip to content

Commit 3c1aa23

Browse files
authored
Merge pull request libgit2#5232 from pks-t/pks/buffer-ensure-size-oom
buffer: fix writes into out-of-memory buffers
2 parents bfdb979 + 174b7a3 commit 3c1aa23

File tree

2 files changed

+53
-10
lines changed

2 files changed

+53
-10
lines changed

src/buffer.c

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ char git_buf__initbuf[1];
1818
char git_buf__oom[1];
1919

2020
#define ENSURE_SIZE(b, d) \
21-
if ((d) > (b)->asize && git_buf_grow((b), (d)) < 0)\
21+
if ((b)->ptr == git_buf__oom || \
22+
((d) > (b)->asize && git_buf_grow((b), (d)) < 0))\
2223
return -1;
2324

2425

@@ -58,20 +59,26 @@ int git_buf_try_grow(
5859
new_ptr = NULL;
5960
} else {
6061
new_size = buf->asize;
62+
/*
63+
* Grow the allocated buffer by 1.5 to allow
64+
* re-use of memory holes resulting from the
65+
* realloc. If this is still too small, then just
66+
* use the target size.
67+
*/
68+
if ((new_size = (new_size << 1) - (new_size >> 1)) < target_size)
69+
new_size = target_size;
6170
new_ptr = buf->ptr;
6271
}
6372

64-
/* grow the buffer size by 1.5, until it's big enough
65-
* to fit our target size */
66-
while (new_size < target_size)
67-
new_size = (new_size << 1) - (new_size >> 1);
68-
6973
/* round allocation up to multiple of 8 */
7074
new_size = (new_size + 7) & ~7;
7175

7276
if (new_size < buf->size) {
73-
if (mark_oom)
77+
if (mark_oom) {
78+
if (buf->ptr && buf->ptr != git_buf__initbuf)
79+
git__free(buf->ptr);
7480
buf->ptr = git_buf__oom;
81+
}
7582

7683
git_error_set_oom();
7784
return -1;

tests/core/buffer.c

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -231,9 +231,9 @@ check_buf_append(
231231
git_buf_puts(&tgt, data_b);
232232
cl_assert(git_buf_oom(&tgt) == 0);
233233
cl_assert_equal_s(expected_data, git_buf_cstr(&tgt));
234-
cl_assert(tgt.size == expected_size);
234+
cl_assert_equal_i(tgt.size, expected_size);
235235
if (expected_asize > 0)
236-
cl_assert(tgt.asize == expected_asize);
236+
cl_assert_equal_i(tgt.asize, expected_asize);
237237

238238
git_buf_dispose(&tgt);
239239
}
@@ -308,7 +308,7 @@ void test_core_buffer__5(void)
308308
REP16("x") REP16("o"), 32, 40);
309309

310310
check_buf_append(test_4096, "", test_4096, 4096, 4104);
311-
check_buf_append(test_4096, test_4096, test_8192, 8192, 9240);
311+
check_buf_append(test_4096, test_4096, test_8192, 8192, 8200);
312312

313313
/* check sequences of appends */
314314
check_buf_append_abc("a", "b", "c",
@@ -1204,3 +1204,39 @@ void test_core_buffer__dont_grow_borrowed(void)
12041204

12051205
cl_git_fail_with(GIT_EINVALID, git_buf_grow(&buf, 1024));
12061206
}
1207+
1208+
void test_core_buffer__dont_hit_infinite_loop_when_resizing(void)
1209+
{
1210+
git_buf buf = GIT_BUF_INIT;
1211+
1212+
cl_git_pass(git_buf_puts(&buf, "foobar"));
1213+
/*
1214+
* We do not care whether this succeeds or fails, which
1215+
* would depend on platform-specific allocation
1216+
* semantics. We only want to know that the function
1217+
* actually returns.
1218+
*/
1219+
(void)git_buf_try_grow(&buf, SIZE_MAX, true);
1220+
1221+
git_buf_dispose(&buf);
1222+
}
1223+
1224+
void test_core_buffer__avoid_printing_into_oom_buffer(void)
1225+
{
1226+
git_buf buf = GIT_BUF_INIT;
1227+
1228+
/* Emulate OOM situation with a previous allocation */
1229+
buf.asize = 8;
1230+
buf.ptr = git_buf__oom;
1231+
1232+
/*
1233+
* Print the same string again. As the buffer still has
1234+
* an `asize` of 8 due to the previous print,
1235+
* `ENSURE_SIZE` would not try to reallocate the array at
1236+
* all. As it didn't explicitly check for `git_buf__oom`
1237+
* in earlier versions, this would've resulted in it
1238+
* returning successfully and thus `git_buf_puts` would
1239+
* just print into the `git_buf__oom` array.
1240+
*/
1241+
cl_git_fail(git_buf_puts(&buf, "foobar"));
1242+
}

0 commit comments

Comments
 (0)