diff --git a/MANIFEST b/MANIFEST index 6292985b8..22e2348df 100644 --- a/MANIFEST +++ b/MANIFEST @@ -251,6 +251,7 @@ lib/util/mkdirat.c lib/util/mksiglist.c lib/util/mksigname.c lib/util/mktemp.c +lib/util/mmap_alloc.c lib/util/nanosleep.c lib/util/openat.c lib/util/parseln.c diff --git a/include/sudo_util.h b/include/sudo_util.h index 403a7fe49..65996438b 100644 --- a/include/sudo_util.h +++ b/include/sudo_util.h @@ -248,6 +248,16 @@ sudo_dso_public const char *sudo_logpri2str_v1(int num); sudo_dso_public bool sudo_mkdir_parents_v1(const char *path, uid_t uid, gid_t gid, mode_t mode, bool quiet); #define sudo_mkdir_parents(_a, _b, _c, _d, _e) sudo_mkdir_parents_v1((_a), (_b), (_c), (_d), (_e)) +/* mmap_alloc.c */ +sudo_dso_public void *sudo_mmap_alloc_v1(size_t size); +#define sudo_mmap_alloc(_a) sudo_mmap_alloc_v1(_a) +sudo_dso_public void *sudo_mmap_allocarray_v1(size_t count, size_t size); +#define sudo_mmap_allocarray(_a, _b) sudo_mmap_allocarray_v1((_a), (_b)) +sudo_dso_public void sudo_mmap_free_v1(void *ptr); +#define sudo_mmap_free(_a) sudo_mmap_free_v1(_a) +sudo_dso_public char *sudo_mmap_strdup_v1(const char *str); +#define sudo_mmap_strdup(_a) sudo_mmap_strdup_v1(_a) + /* parseln.c */ sudo_dso_public ssize_t sudo_parseln_v1(char **buf, size_t *bufsize, unsigned int *lineno, FILE *fp); sudo_dso_public ssize_t sudo_parseln_v2(char **buf, size_t *bufsize, unsigned int *lineno, FILE *fp, int flags); diff --git a/lib/util/Makefile.in b/lib/util/Makefile.in index 452f2f07b..67177febd 100644 --- a/lib/util/Makefile.in +++ b/lib/util/Makefile.in @@ -143,10 +143,10 @@ SHELL = @SHELL@ LTOBJS = basename.lo @DIGEST@ event.lo fatal.lo key_val.lo gethostname.lo \ gettime.lo getgrouplist.lo gidlist.lo json.lo lbuf.lo locking.lo \ - logfac.lo logpri.lo mkdir_parents.lo parseln.lo progname.lo rcstr.lo \ - regex.lo roundup.lo secure_path.lo setgroups.lo strsplit.lo \ - strtobool.lo strtoid.lo strtomode.lo strtonum.lo sudo_conf.lo \ - sudo_debug.lo sudo_dso.lo term.lo ttyname_dev.lo \ + logfac.lo logpri.lo mkdir_parents.lo mmap_alloc.lo parseln.lo \ + progname.lo rcstr.lo regex.lo roundup.lo secure_path.lo setgroups.lo \ + strsplit.lo strtobool.lo strtoid.lo strtomode.lo strtonum.lo \ + sudo_conf.lo sudo_debug.lo sudo_dso.lo term.lo ttyname_dev.lo \ ttysize.lo uuid.lo @COMMON_OBJS@ @LTLIBOBJS@ IOBJS = $(LTOBJS:.lo=.i) @@ -1086,6 +1086,16 @@ mktemp_test.i: $(srcdir)/regress/mktemp/mktemp_test.c \ $(CC) -E -o $@ $(CPPFLAGS) $< mktemp_test.plog: mktemp_test.i rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/regress/mktemp/mktemp_test.c --i-file $< --output-file $@ +mmap_alloc.lo: $(srcdir)/mmap_alloc.c $(incdir)/compat/stdbool.h \ + $(incdir)/sudo_compat.h $(incdir)/sudo_util.h \ + $(top_builddir)/config.h + $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(HARDENING_CFLAGS) $(srcdir)/mmap_alloc.c +mmap_alloc.i: $(srcdir)/mmap_alloc.c $(incdir)/compat/stdbool.h \ + $(incdir)/sudo_compat.h $(incdir)/sudo_util.h \ + $(top_builddir)/config.h + $(CC) -E -o $@ $(CPPFLAGS) $< +mmap_alloc.plog: mmap_alloc.i + rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/mmap_alloc.c --i-file $< --output-file $@ nanosleep.lo: $(srcdir)/nanosleep.c $(incdir)/compat/stdbool.h \ $(incdir)/sudo_compat.h $(incdir)/sudo_util.h \ $(top_builddir)/config.h diff --git a/lib/util/mmap_alloc.c b/lib/util/mmap_alloc.c new file mode 100644 index 000000000..9e7bf84d6 --- /dev/null +++ b/lib/util/mmap_alloc.c @@ -0,0 +1,137 @@ +/* + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2008 Otto Moerbeek + * Copyright (c) 2022 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This is an open source non-commercial project. Dear PVS-Studio, please check it. + * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + */ + +#include + +#include +#include + +#include +#include +#if defined(HAVE_STDINT_H) +# include +#elif defined(HAVE_INTTYPES_H) +# include +#endif + +#include "sudo_compat.h" +#include "sudo_util.h" + +#if !defined(MAP_ANON) && defined(MAP_ANONYMOUS) +# define MAP_ANON MAP_ANONYMOUS +#endif + +#ifndef MAP_FAILED +# define MAP_FAILED ((void *)-1) +#endif + +/* + * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX + * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW + */ +#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4)) + +/* + * Allocate "size" bytes via mmap(). + * Space is allocated to store the size for later unmapping. + */ +void * +sudo_mmap_alloc_v1(size_t size) +{ + void *ptr; + unsigned long *ulp; +#ifndef MAP_ANON + int fd; + + /* SunOS-style mmap allocation using /dev/zero. */ + if ((fd = open("/dev/zero", O_RDWR)) == -1) + return NULL; + size += sizeof(unsigned long); + ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); + close(fd); +#else + size += sizeof(unsigned long); + ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0); +#endif + if (ptr == MAP_FAILED) { + errno = ENOMEM; + return NULL; + } + + /* Store size before the actual data. */ + ulp = (unsigned long *)ptr; + ulp[0] = size; + return (void *)&ulp[1]; +} + +/* + * Allocate "nmemb" elements of "size" bytes via mmap(). + * If overflow would occur, errno is set to ENOMEM and + * NULL is returned. + */ +void * +sudo_mmap_allocarray_v1(size_t nmemb, size_t size) +{ + if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && + nmemb > 0 && SIZE_MAX / nmemb < size) { + errno = ENOMEM; + return NULL; + } + return sudo_mmap_alloc_v1(nmemb * size); +} + +/* + * Make a copy of "str" via mmap() and return it. + */ +char * +sudo_mmap_strdup_v1(const char *str) +{ + size_t len = strlen(str); + char *newstr; + + if (len == SIZE_MAX) { + errno = ENOMEM; + return NULL; + } + newstr = sudo_mmap_alloc_v1(len + 1); + memcpy(newstr, str, len); + newstr[len] = '\0'; + + return newstr; +} + +/* + * Free "ptr" allocated by sudo_mmap_alloc(). + * The allocated size is stored (as unsigned long) in ptr[-1]. + */ +void +sudo_mmap_free_v1(void *ptr) +{ + if (ptr != NULL) { + unsigned long *ulp = ptr; + const unsigned long size = ulp[-1]; + + munmap((void *)&ulp[-1], size); + } +} diff --git a/lib/util/util.exp.in b/lib/util/util.exp.in index 48dcfb4af..b289174e2 100644 --- a/lib/util/util.exp.in +++ b/lib/util/util.exp.in @@ -110,6 +110,10 @@ sudo_lock_region_v1 sudo_logfac2str_v1 sudo_logpri2str_v1 sudo_mkdir_parents_v1 +sudo_mmap_alloc_v1 +sudo_mmap_allocarray_v1 +sudo_mmap_free_v1 +sudo_mmap_strdup_v1 sudo_new_key_val_v1 sudo_parse_gids_v1 sudo_parseln_v1