sudo/lib/util/regress/mktemp/mktemp_test.c

208 lines
5.3 KiB
C

/*
* Copyright (c) 2010 Philip Guenther <guenther@openbsd.org>
*
* Public domain.
*
* Verify that mkdtemp() and mkstemps() doesn't overrun or underrun
* the template buffer and that it can generate names that don't
* contain any X's
*/
#include <config.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#define SUDO_ERROR_WRAP 0
#include <sudo_compat.h>
#include <sudo_util.h>
#include <sudo_fatal.h>
#ifndef MAP_ANON
# if defined(MAP_ANONYMOUS)
# define MAP_ANON MAP_ANONYMOUS
# endif
#endif
#ifndef MAP_FAILED
# define MAP_FAILED ((void *) -1)
#endif
#define MAX_TEMPLATE_LEN 10
#define MAX_TRIES 100
#define MIN_Xs 6
#define SUFFIX ".suff"
#define SLEN (sizeof SUFFIX - 1)
sudo_dso_public int main(int argc, char *argv[]);
/*
* verify that a path generated by mkdtemp() or mkstemp() looks like a
* reasonable expansion of the template and matches the fd. Returns true
* if all the X's were replaced with non-X's
*/
static int
check(int fd, char const *kind, char const *path, char const *prefix,
size_t plen, char const *suffix, size_t slen, size_t tlen)
{
struct stat sb, fsb;
char const *p;
if (tlen < MIN_Xs) {
if (fd != -1)
sudo_fatalx("%s(%s) succeed with too few Xs", kind, path);
if (errno != EINVAL)
sudo_fatal("%s(%s) failed with wrong errno: %d", kind, path, errno);
return 1;
}
if (fd == -1)
sudo_fatal("%s(%s)", kind, path);
if (stat(path, &sb))
sudo_fatal("%s: stat(%s)", kind, path);
if (fd >= 0) {
if (fstat(fd, &fsb))
sudo_fatal("%s: fstat(%d==%s)", kind, fd, path);
if (sb.st_dev != fsb.st_dev || sb.st_ino != fsb.st_ino)
sudo_fatalx("%s: stat mismatch", kind);
}
if (memcmp(path, prefix, plen) != 0)
sudo_fatalx("%s: prefix changed! %s vs %s", kind, prefix, path);
if (memcmp(path + plen + tlen, suffix, slen + 1) != 0)
sudo_fatalx("%s: suffix changed! %s vs %s", kind, suffix, path);
for (p = path + plen; p < path + plen + tlen; p++)
if (*p == '\0')
sudo_fatalx("%s: unexpected truncation", kind);
else if (*p == 'X')
return 0;
return 1;
}
static void
try_mkdtemp(char *p, char const *prefix, size_t len)
{
size_t plen = strlen(prefix);
int fd, tries, ok;
for (tries = 0; tries < MAX_TRIES; tries++) {
memcpy(p, prefix, plen);
memset(p + plen, 'X', len);
p[plen + len] = '\0';
fd = mkdtemp(p) ? -2 : -1;
ok = check(fd, "mkdtemp", p, prefix, plen, "", 0, len);
rmdir(p);
if (ok)
return;
}
sudo_fatalx("mkdtemp: exceeded MAX_TRIES");
}
static void
try_mkstemps(char *p, char const *prefix, size_t len, char const *suffix)
{
size_t plen = strlen(prefix);
size_t slen = strlen(suffix);
int tries, fd, ok;
for (tries = 0; tries < MAX_TRIES; tries++) {
memcpy(p, prefix, plen);
memset(p + plen, 'X', len);
memcpy(p + plen + len, suffix, slen + 1);
fd = mkstemps(p, (int)slen);
ok = check(fd, "mkstemp", p, prefix, plen, suffix, slen, len);
close(fd);
unlink(p);
if (ok)
return;
}
sudo_fatalx("mkstemps: exceeded MAX_TRIES");
}
int
main(int argc, char *argv[])
{
char cwd[PATH_MAX + 1];
char *p;
size_t clen, i;
size_t pg;
int ch;
initprogname(argc > 0 ? argv[0] : "mktemp_test");
while ((ch = getopt(argc, argv, "v")) != -1) {
switch (ch) {
case 'v':
/* ignore */
break;
default:
fprintf(stderr, "usage: %s [-v]\n", getprogname());
return EXIT_FAILURE;
}
}
argc -= optind;
argv += optind;
pg = (size_t)sysconf(_SC_PAGESIZE);
if (pg == (size_t)-1)
sudo_fatal("sysconf(_SC_PAGESIZE)");
if (getcwd(cwd, sizeof cwd - 1) == NULL)
sudo_fatal("getcwd");
clen = strlen(cwd);
cwd[clen++] = '/';
cwd[clen] = '\0';
#ifdef MAP_ANON
p = mmap(NULL, pg * 3, PROT_READ | PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
#else
ch = open("/dev/zero", O_RDWR);
if (ch == -1)
sudo_fatal("/dev/zero");
p = mmap(NULL, pg * 3, PROT_READ | PROT_WRITE, MAP_PRIVATE, ch, 0);
#endif
if (p == MAP_FAILED)
sudo_fatal("mmap");
if (mprotect(p, pg, PROT_NONE) || mprotect(p + pg * 2, pg, PROT_NONE))
sudo_fatal("mprotect");
p += pg;
for (i = MAX_TEMPLATE_LEN; i != 0; i--) {
/* try first at the start of a page, no prefix */
try_mkdtemp(p, "", i);
/* now at the end of the page, no prefix */
try_mkdtemp(p + pg - i - 1, "", i);
/* start of the page, prefixed with the cwd */
try_mkdtemp(p, cwd, i);
/* how about at the end of the page, prefixed with cwd? */
try_mkdtemp(p + pg - clen - i - 1, cwd, i);
/* again, with mkstemps() and an empty suffix */
/* try first at the start of a page, no prefix */
try_mkstemps(p, "", i, "");
/* now at the end of the page, no prefix */
try_mkstemps(p + pg - i - 1, "", i, "");
/* start of the page, prefixed with the cwd */
try_mkstemps(p, cwd, i, "");
/* how about at the end of the page, prefixed with cwd? */
try_mkstemps(p + pg - clen - i - 1, cwd, i, "");
/* mkstemps() and a non-empty suffix */
/* try first at the start of a page, no prefix */
try_mkstemps(p, "", i, SUFFIX);
/* now at the end of the page, no prefix */
try_mkstemps(p + pg - i - SLEN - 1, "", i, SUFFIX);
/* start of the page, prefixed with the cwd */
try_mkstemps(p, cwd, i, SUFFIX);
/* how about at the end of the page, prefixed with cwd? */
try_mkstemps(p + pg - clen - i - SLEN - 1, cwd, i, SUFFIX);
}
return 0;
}