From 6552a1d3f8126d858d64c75fa3c1eb049d8bf235 Mon Sep 17 00:00:00 2001 From: Timo Teräs Date: Sun, 11 Apr 2021 15:01:52 +0300 Subject: io: fix fd leak in error handling paths apk_dir_foreach_file and apk_resolve_[ug]id needs to free the fd in case fdopen/fdopendir fails. Additionally this does not rely on fdopen to fail if openat() returned -1, making sure that we don't call any syscalls with invalid file handle. (cherry picked from commit 3c339a74d1b9dba23d60d6c769d99227d75cc6dc) --- src/io.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/io.c b/src/io.c index 8aac019..81050a4 100644 --- a/src/io.c +++ b/src/io.c @@ -725,8 +725,10 @@ int apk_dir_foreach_file(int dirfd, apk_dir_file_cb cb, void *ctx) return -1; dir = fdopendir(dirfd); - if (dir == NULL) + if (!dir) { + close(dirfd); return -1; + } /* We get called here with dup():ed fd. Since they all refer to * same object, we need to rewind so subsequent calls work. */ @@ -1012,6 +1014,19 @@ void apk_id_cache_reset(struct apk_id_cache *idc) idc->genid = 1; } +static FILE *fopenat(int dirfd, const char *pathname) +{ + FILE *f; + int fd; + + fd = openat(dirfd, pathname, O_RDONLY|O_CLOEXEC); + if (fd < 0) return NULL; + + f = fdopen(fd, "r"); + if (!f) close(fd); + return f; +} + uid_t apk_resolve_uid(struct apk_id_cache *idc, const char *username, uid_t default_uid) { #ifdef HAVE_FGETPWENT_R @@ -1030,8 +1045,8 @@ uid_t apk_resolve_uid(struct apk_id_cache *idc, const char *username, uid_t defa ci->genid = idc->genid; ci->uid = -1; - in = fdopen(openat(idc->root_fd, "etc/passwd", O_RDONLY|O_CLOEXEC), "r"); - if (in != NULL) { + in = fopenat(idc->root_fd, "etc/passwd"); + if (in) { do { #ifdef HAVE_FGETPWENT_R fgetpwent_r(in, &pwent, buf, sizeof(buf), &pwd); @@ -1073,8 +1088,8 @@ uid_t apk_resolve_gid(struct apk_id_cache *idc, const char *groupname, uid_t def ci->genid = idc->genid; ci->gid = -1; - in = fdopen(openat(idc->root_fd, "etc/group", O_RDONLY|O_CLOEXEC), "r"); - if (in != NULL) { + in = fopenat(idc->root_fd, "etc/group"); + if (in) { do { #ifdef HAVE_FGETGRENT_R fgetgrent_r(in, &grent, buf, sizeof(buf), &grp); -- cgit v1.2.3-70-g09d2