Skip to content

Commit

Permalink
Fix leak in evbuffer_add_file() on empty files
Browse files Browse the repository at this point in the history
Found by oss-fuzz, after coverage had been improved in google/oss-fuzz#11257
v2: adjust test
  • Loading branch information
azat committed Mar 11, 2024
1 parent b9e1fe7 commit 372b737
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 4 deletions.
14 changes: 12 additions & 2 deletions buffer.c
Original file line number Diff line number Diff line change
Expand Up @@ -3087,6 +3087,7 @@ evbuffer_file_segment_materialize(struct evbuffer_file_segment *seg)
return 0; /* already materialized */

#if defined(EVENT__HAVE_MMAP)
fprintf(stderr, "%s: EVENT__HAVE_MMAP\n", __func__);
if (!(flags & EVBUF_FS_DISABLE_MMAP)) {
off_t offset_rounded = 0, offset_leftover = 0;
void *mapped;
Expand Down Expand Up @@ -3126,15 +3127,18 @@ evbuffer_file_segment_materialize(struct evbuffer_file_segment *seg)
}
#endif
#ifdef _WIN32
fprintf(stderr, "%s: _WIN32\n", __func__);
if (!(flags & EVBUF_FS_DISABLE_MMAP)) {
intptr_t h = _get_osfhandle(fd);
HANDLE m;
ev_uint64_t total_size = length+offset;
if ((HANDLE)h == INVALID_HANDLE_VALUE)
goto err;
fprintf(stderr, "%s: before CreateFileMapping\n", __func__);
m = CreateFileMapping((HANDLE)h, NULL, PAGE_READONLY,
(total_size >> 32), total_size & 0xfffffffful,
NULL);
fprintf(stderr, "%s: after CreateFileMapping\n", __func__);
if (m != INVALID_HANDLE_VALUE) { /* Does h leak? */
seg->mapping_handle = m;
seg->mmap_offset = offset;
Expand Down Expand Up @@ -3259,15 +3263,16 @@ evbuffer_add_file_segment(struct evbuffer *buf,
struct evbuffer_chain_file_segment *extra;
int can_use_sendfile = 0;

fprintf(stderr, "%s: enter\n", __func__);

EVBUFFER_LOCK(buf);
EVLOCK_LOCK(seg->lock, 0);
if (buf->flags & EVBUFFER_FLAG_DRAINS_TO_FD) {
can_use_sendfile = 1;
} else {
if (evbuffer_file_segment_materialize(seg)<0) {
EVLOCK_UNLOCK(seg->lock, 0);
EVBUFFER_UNLOCK(buf);
return -1;
goto err;
}
}
EVLOCK_UNLOCK(seg->lock, 0);
Expand Down Expand Up @@ -3308,12 +3313,14 @@ evbuffer_add_file_segment(struct evbuffer *buf,
offset_remaining = total_offset % si.dwAllocationGranularity;
offset_rounded = total_offset - offset_remaining;
}
fprintf(stderr, "%s: before MapViewOfFile\n", __func__);
data = MapViewOfFile(
seg->mapping_handle,
FILE_MAP_READ,
offset_rounded >> 32,
offset_rounded & 0xfffffffful,
length + offset_remaining);
fprintf(stderr, "%s: after MapViewOfFile\n", __func__);
if (data == NULL) {
mm_free(chain);
goto err;
Expand Down Expand Up @@ -3358,7 +3365,10 @@ evbuffer_add_file(struct evbuffer *buf, int fd, ev_off_t offset, ev_off_t length
unsigned flags = EVBUF_FS_CLOSE_ON_FREE;
int r;

fprintf(stderr, "%s: enter\n", __func__);

seg = evbuffer_file_segment_new(fd, offset, length, flags);
fprintf(stderr, "%s: seg %p\n", __func__, seg);
if (!seg)
return -1;
r = evbuffer_add_file_segment(buf, seg, 0, length);
Expand Down
4 changes: 2 additions & 2 deletions include/event2/buffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -555,8 +555,8 @@ int evbuffer_add_reference_with_offset(struct evbuffer *outbuf, const void *data
flag is set, it uses those functions. Otherwise, it tries to use
mmap (or CreateFileMapping on Windows).
The function owns the resulting file descriptor and will close it
when finished transferring data.
The function owns the resulting file descriptor and will close (even in case
of error) it when finished transferring data.
The results of using evbuffer_remove() or evbuffer_pullup() on
evbuffers whose data was added using this function are undefined.
Expand Down
37 changes: 37 additions & 0 deletions test/regress_buffer.c
Original file line number Diff line number Diff line change
Expand Up @@ -1107,6 +1107,42 @@ addfile_test_readcb(evutil_socket_t fd, short what, void *arg)
}
}

static void
test_evbuffer_add_file_leak1(void *ptr)
{
struct basic_test_data *testdata = ptr;
struct evbuffer *buf = NULL;
char *tmpfilename = NULL;
int fd;

(void)testdata;

fd = regress_make_tmpfile("", 0, &tmpfilename);
/* On Windows, if TMP environment variable is corrupted, we may not be
* able create temporary file, just skip it */
if (fd < 0)
tt_skip();
TT_BLATHER(("Temporary path: %s, fd: %i", tmpfilename, fd));

/* close fd before usage, so that the fallback with pread() will fail (in
* evbuffer_file_segment_materialize()) */
close(fd);

/* mmap(offset=0, length=0) will fail, this is enough */
buf = evbuffer_new();
tt_assert(evbuffer_add_file(buf, fd, 0, 0) == -1);
evbuffer_validate(buf);

end:
if (tmpfilename) {
unlink(tmpfilename);
free(tmpfilename);
}
if (buf)
evbuffer_free(buf);
/* NOTE: file will be closed in evbuffer_add_file() */
}

static void
test_evbuffer_add_file(void *ptr)
{
Expand Down Expand Up @@ -2922,6 +2958,7 @@ struct testcase_t evbuffer_testcases[] = {
{ "copyout", test_evbuffer_copyout, 0, NULL, NULL},
{ "file_segment_add_cleanup_cb", test_evbuffer_file_segment_add_cleanup_cb, 0, NULL, NULL },
{ "pullup_with_empty", test_evbuffer_pullup_with_empty, 0, NULL, NULL },
{ "add_file_leak1", test_evbuffer_add_file_leak1, 0, NULL, NULL },

#define ADDFILE_TEST(name, parameters) \
{ name, test_evbuffer_add_file, TT_FORK|TT_NEED_BASE, \
Expand Down

0 comments on commit 372b737

Please sign in to comment.