|
Message-Id: <20210827204950.10223-3-ismael@iodev.co.uk> Date: Fri, 27 Aug 2021 22:49:50 +0200 From: Ismael Luceno <ismael@...ev.co.uk> To: musl@...ts.openwall.com Cc: Rich Felker <dalias@...c.org>, Ismael Luceno <ismael@...ev.co.uk> Subject: [PATCH v5 3/3] glob: implement GLOB_ALTDIRFUNC et al Signed-off-by: Ismael Luceno <ismael@...ev.co.uk> --- Notes: Changes since v4: - Rebased on top of do_glob context patch - Added alternate definitions of glob_t for BSD/GNU vs POSIX - Added static check to ensure the memory layout of both glob_t versions matches plus a wrapper for the POSIX glob function (necessary?) Changes since v3: - Wrap pointers used by GLOB_ALTDIRFUNC on the header to protect from namespace contamination. GNU and BSD code shouldn't be using "dirent", so it should be safe. Changes since v2: - Rebased Changes since v1: - Avoid overwriting the function pointers in glob_t - Wrap {open,read}dir too include/glob.h | 18 +++++++++++- src/regex/glob.c | 73 ++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 84 insertions(+), 7 deletions(-) diff --git a/include/glob.h b/include/glob.h index 0ff70bdfeef2..32c03610020b 100644 --- a/include/glob.h +++ b/include/glob.h @@ -11,12 +11,27 @@ extern "C" { #include <bits/alltypes.h> +#if defined(_BSD_SOURCE) || defined(_GNU_SOURCE) +#define __dirent dirent +#define __stat stat +#else +#define glob __posix_glob +#endif + +struct __dirent; +struct __stat; + typedef struct { size_t gl_pathc; char **gl_pathv; size_t gl_offs; int __dummy1; - void *__dummy2[5]; + + void (*gl_closedir)(void *); + struct __dirent *(*gl_readdir)(void *); + void *(*gl_opendir)(const char *); + int (*gl_lstat)(const char *__restrict, struct __stat *__restrict); + int (*gl_stat)(const char *__restrict, struct __stat *__restrict); } glob_t; int glob(const char *__restrict, int, int (*)(const char *, int), glob_t *__restrict); @@ -31,6 +46,7 @@ void globfree(glob_t *); #define GLOB_NOESCAPE 0x40 #define GLOB_PERIOD 0x80 +#define GLOB_ALTDIRFUNC 0x0200 #define GLOB_NOMAGIC 0x0800 #define GLOB_TILDE 0x1000 #define GLOB_TILDE_CHECK 0x4000 diff --git a/src/regex/glob.c b/src/regex/glob.c index 9491eaeef266..be7be0b62da9 100644 --- a/src/regex/glob.c +++ b/src/regex/glob.c @@ -36,6 +36,13 @@ struct glob_ctx { struct match **tail; int flags; int (*errfunc)(const char *path, int err); + + /* for GLOB_ALTDIRFUNC */ + void (*closedir)(void *); + struct dirent *(*readdir)(void *); + void *(*opendir)(const char *); + int (*lstat)(const char *restrict, struct stat *restrict); + int (*stat)(const char *restrict, struct stat *restrict); }; static int do_glob(char *buf, size_t pos, int type, char *pat, const struct glob_ctx *restrict ctx) @@ -106,11 +113,11 @@ static int do_glob(char *buf, size_t pos, int type, char *pat, const struct glob * or if that fails, use lstat for determining existence to * avoid false negatives in the case of broken symlinks. */ struct stat st; - if ((ctx->flags & GLOB_MARK) && (!type||type==DT_LNK) && !stat(buf, &st)) { + if ((ctx->flags & GLOB_MARK) && (!type||type==DT_LNK) && !ctx->stat(buf, &st)) { if (S_ISDIR(st.st_mode)) type = DT_DIR; else type = DT_REG; } - if (!type && lstat(buf, &st)) { + if (!type && ctx->lstat(buf, &st)) { if (errno!=ENOENT && (ctx->errfunc(buf, errno) || (ctx->flags & GLOB_ERR))) return GLOB_ABORTED; return 0; @@ -130,7 +137,7 @@ static int do_glob(char *buf, size_t pos, int type, char *pat, const struct glob saved_sep = '\\'; } } - DIR *dir = opendir(pos ? buf : "."); + DIR *dir = ctx->opendir(pos ? buf : "."); if (!dir) { if (ctx->errfunc(buf, errno) || (ctx->flags & GLOB_ERR)) return GLOB_ABORTED; @@ -138,7 +145,7 @@ static int do_glob(char *buf, size_t pos, int type, char *pat, const struct glob } int old_errno = errno; struct dirent *de; - while (errno=0, de=readdir(dir)) { + while (errno=0, de=ctx->readdir(dir)) { /* Quickly skip non-directories when there's pattern left. */ if (p2 && de->d_type && de->d_type!=DT_DIR && de->d_type!=DT_LNK) continue; @@ -165,13 +172,13 @@ static int do_glob(char *buf, size_t pos, int type, char *pat, const struct glob if (p2) *p2 = saved_sep; int r = do_glob(buf, pos+l, de->d_type, p2 ? p2 : "", ctx); if (r) { - closedir(dir); + ctx->closedir(dir); return r; } } int readerr = errno; if (p2) *p2 = saved_sep; - closedir(dir); + ctx->closedir(dir); if (readerr && (ctx->errfunc(buf, errno) || (ctx->flags & GLOB_ERR))) return GLOB_ABORTED; errno = old_errno; @@ -230,6 +237,10 @@ static int expand_tilde(char **pat, char *buf, size_t *pos) return 0; } +static void wrap_closedir(void *p) { __closedir(p); } +static struct dirent *wrap_readdir(void *d) { return __readdir(d); } +static void *wrap_opendir(const char *path) { return __opendir(path); } + int glob(const char *restrict pat, int flags, int (*errfunc)(const char *path, int err), glob_t *restrict g) { struct match head = { .next = NULL }, *tail = &head; @@ -259,6 +270,11 @@ int glob(const char *restrict pat, int flags, int (*errfunc)(const char *path, i .tail = &tail, .flags = flags, .errfunc = errfunc, + .closedir = (flags & GLOB_ALTDIRFUNC) ? g->gl_closedir : wrap_closedir, + .readdir = (flags & GLOB_ALTDIRFUNC) ? g->gl_readdir : wrap_readdir, + .opendir = (flags & GLOB_ALTDIRFUNC) ? g->gl_opendir : wrap_opendir, + .lstat = (flags & GLOB_ALTDIRFUNC) ? g->gl_lstat : lstat, + .stat = (flags & GLOB_ALTDIRFUNC) ? g->gl_stat : stat, }; error = do_glob(buf, pos, 0, s, &ctx); } @@ -326,3 +342,48 @@ void globfree(glob_t *g) weak_alias(glob, glob64); weak_alias(globfree, globfree64); + +/* + * Following code exists to work-around UB by different versions of glob_t + * (BSD/GNU vs POSIX), which are required to avoid namespace contamination. + */ +#undef __dirent +#undef __stat +struct __dirent; +struct __stat; +typedef struct { + size_t gl_pathc; + char **gl_pathv; + size_t gl_offs; + int __dummy1; + + void (*gl_closedir)(void *); + struct __dirent *(*gl_readdir)(void *); + void *(*gl_opendir)(const char *); + int (*gl_lstat)(const char *__restrict, struct __stat *__restrict); + int (*gl_stat)(const char *__restrict, struct __stat *__restrict); +} __posix_glob_t; + +#define CMP_MEMB(A, B, memb) ( \ + offsetof(A, memb) == offsetof(B, memb) && \ + sizeof(((A*)0)->memb) == sizeof(((B*)0)->memb)) + +int __posix_glob(const char *restrict pat, int flags, int (*errfunc)(const char *path, int err), __posix_glob_t *restrict g) +{ + /* statically check size and member offsets */ + switch(1) { + case 0: + case (sizeof(__posix_glob_t) == sizeof(glob_t) && + CMP_MEMB(__posix_glob_t, glob_t, gl_pathc) && + CMP_MEMB(__posix_glob_t, glob_t, gl_pathv) && + CMP_MEMB(__posix_glob_t, glob_t, gl_offs) && + CMP_MEMB(__posix_glob_t, glob_t, gl_closedir) && + CMP_MEMB(__posix_glob_t, glob_t, gl_readdir) && + CMP_MEMB(__posix_glob_t, glob_t, gl_opendir) && + CMP_MEMB(__posix_glob_t, glob_t, gl_lstat) && + CMP_MEMB(__posix_glob_t, glob_t, gl_stat)): + break; + } + + return glob(pat, flags, errfunc, (glob_t *)g); +} -- 2.33.0
Powered by blists - more mailing lists
Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.