Download raw body.
leak in got_fetch_pack
Stefan Sperling <stsp@stsp.name> writes:
> On Sun, Feb 13, 2022 at 10:49:05PM +0100, Omar Polo wrote:
>> Sure, but I fail to see how it's leaking if imsg_clear is called outside
>> of the loop.
>>
>> If I'm reading correctly imsg{,-buf}.c all it does is to dequeue pending
>> ibufs and file descriptors and frees/closes them, so while I agree that
>> doing it inside the loop (possibly) frees these imsgbuf internal queues
>> more quickly, I don't see how it avoids leaking memory.
>>
>> I'm clearly missing something obvious here, sorry, I just don't see it.
>
> This API is not very intuitive, and the imsg_init(3) man page does
> not clearly explain when imsg_clear(3) is intended to be used.
>
> Looking at the code it seems that only the writing side of the imsg
> channel is supposed to call imsg_clear() ever?
>
> The reader receives the data in a single buffer (ibuf->r.buf) which
> is pre-allocated as part of struct ibuf, so we don't need to free it
> since it lives on the stack.
>
> And any file descriptors that arrive in an ibuf are copied into struct
> imsg by imsg_get() and in such cases we close imsg->fd ourselves or
> copy its value elsewhere.
>
> So imsg_clear() at the receiving end looks like a no-op?
My reading is the same as yours. Well, it kind of makes sense since you
can enqueue multiple messages but read one at a time.
I think that we could factor out all the imsg_clear in privsep.c, and
only when imsg_flush fails. Sure, I'm being a bit pedantic here since
we always bail out pretty quickly if an error occurs so even if we leak
a bit of memory it's not the end of the world, but could save us future
confusions :)
(all the removed calls were only after we successfully receive
something, so there can't be any imsg pending in the sending queue.)
After this, the only other imsg_clear call in lib/ is repository.c:801
(offset after patch is applied): i don't think it's needed, but it since
it's a common cleanup step I thought it wouldn't hurt to keep it anyway.
(it's really two diffs, one for the missing close and the other for the
imsg_clear, err, cleanup, sending all together because it's easier.)
diff 32b5305fbed2d1a4c55b0eef6b93349b93ba7db0 /home/op/w/got-fetch-leak
blob - 6c0dbf4a0ba8148e5f95f882f51bda4b71ebc4f1
file + lib/fetch.c
--- lib/fetch.c
+++ lib/fetch.c
@@ -353,6 +353,10 @@ got_fetch_pack(struct got_object_id **pack_hash, struc
packfile_size = packfile_size_cur;
}
}
+ if (close(imsg_fetchfds[0]) == -1) {
+ err = got_error_from_errno("close");
+ goto done;
+ }
if (waitpid(fetchpid, &fetchstatus, 0) == -1) {
err = got_error_from_errno("waitpid");
goto done;
@@ -472,7 +476,6 @@ got_fetch_pack(struct got_object_id **pack_hash, struc
if (err)
break;
}
- imsg_clear(&idxibuf);
}
if (close(imsg_idxfds[0]) == -1) {
err = got_error_from_errno("close");
blob - 664f170821f75b4356a1f141c1aebcf860ac1868
file + lib/gotconfig.c
--- lib/gotconfig.c
+++ lib/gotconfig.c
@@ -108,7 +108,6 @@ got_gotconfig_read(struct got_gotconfig **conf, const
if (err)
goto done;
- imsg_clear(ibuf);
err = got_privsep_send_stop(imsg_fds[0]);
child_err = got_privsep_wait_for_child(pid);
if (child_err && err == NULL)
blob - 8a01d22a8f4fe6686821dab1e44a139ea351a8a1
file + lib/pack.c
--- lib/pack.c
+++ lib/pack.c
@@ -577,7 +577,6 @@ pack_stop_privsep_child(struct got_pack *pack)
err = got_privsep_wait_for_child(pack->privsep_child->pid);
if (close(pack->privsep_child->imsg_fd) == -1 && err == NULL)
err = got_error_from_errno("close");
- imsg_clear(pack->privsep_child->ibuf);
free(pack->privsep_child->ibuf);
free(pack->privsep_child);
pack->privsep_child = NULL;
blob - 9efd7ae9b91bb9c96b3d3a23e15d29ff1e58f89f
file + lib/privsep.c
--- lib/privsep.c
+++ lib/privsep.c
@@ -208,6 +208,7 @@ got_privsep_send_error(struct imsgbuf *ibuf, const str
if (ret == -1) {
fprintf(stderr, "%s: error %d \"%s\": imsg_flush: %s\n",
getprogname(), err->code, err->msg, strerror(errno));
+ imsg_clear(ibuf);
return;
}
}
@@ -221,8 +222,10 @@ flush_imsg(struct imsgbuf *ibuf)
if (err)
return err;
- if (imsg_flush(ibuf) == -1)
+ if (imsg_flush(ibuf) == -1) {
+ imsg_clear(ibuf);
return got_error_from_errno("imsg_flush");
+ }
return NULL;
}
@@ -245,7 +248,6 @@ got_privsep_send_stop(int fd)
return got_error_from_errno("imsg_compose STOP");
err = flush_imsg(&ibuf);
- imsg_clear(&ibuf);
return err;
}
blob - 40f5562db7ad3596203bf083a5084f34f1eb1b05
file + lib/repository.c
--- lib/repository.c
+++ lib/repository.c
@@ -577,7 +577,6 @@ parse_gitconfig_file(int *gitconfig_repository_format_
goto done;
}
- imsg_clear(ibuf);
err = got_privsep_send_stop(imsg_fds[0]);
child_err = got_privsep_wait_for_child(pid);
if (child_err && err == NULL)
blob - f9be16f797d60019bfb883aac0e9b41c7beb846a
file + lib/repository_admin.c
--- lib/repository_admin.c
+++ lib/repository_admin.c
@@ -386,7 +386,6 @@ got_repo_index_pack(FILE *packfile, struct got_object_
if (err)
break;
}
- imsg_clear(&idxibuf);
}
if (close(imsg_idxfds[0]) == -1) {
err = got_error_from_errno("close");
leak in got_fetch_pack