1 /*
2  * Copyright (c) 2010  Philip Guenther <guenther@openbsd.org>
3  *
4  * Public domain.
5  *
6  * Verify that mkstemp() and mkstemps() doesn't overrun or underrun
7  * the template buffer and that it can generate names that don't
8  * contain any X's
9  */
10 
11 #include <sys/types.h>
12 #include <sys/mman.h>
13 #include <sys/stat.h>
14 
15 #include <err.h>
16 #include <errno.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <limits.h>
21 #include <unistd.h>
22 
23 #define MAX_TEMPLATE_LEN	10
24 #define MAX_TRIES		100
25 #define MIN_Xs			6
26 
27 #define SUFFIX	".suff"
28 #define SLEN	(sizeof SUFFIX - 1)
29 
30 long pg;
31 
32 /*
33  * verify that a path generated by mkstemp() or mkstemp() looks like a
34  * reasonable expansion of the template and matches the fd.  Returns true
35  * if all the X's were replaced with non-X's
36  */
37 int
check(int fd,char const * path,char const * prefix,size_t plen,char const * suffix,size_t slen,int tlen)38 check(int fd, char const *path, char const *prefix, size_t plen,
39     char const *suffix, size_t slen, int tlen)
40 {
41 	struct stat sb, fsb;
42 	char const *p;
43 
44 	if (tlen < MIN_Xs) {
45 		if (fd >= 0)
46 			errx(1, "mkstemp(%s) succeed with too few Xs", path);
47 		if (errno != EINVAL)
48 			err(1, "mkstemp(%s) failed with wrong errno", path);
49 		return 1;
50 	}
51 	if (fd < 0)
52 		err(1, "mkstemp(%s)", path);
53 	if (stat(path, &sb))
54 		err(1, "stat(%s)", path);
55 	if (fstat(fd, &fsb))
56 		err(1, "fstat(%d==%s)", fd, path);
57 	if (sb.st_dev != fsb.st_dev || sb.st_ino != fsb.st_ino)
58 		errx(1, "stat mismatch");
59 	close(fd);
60 	if (memcmp(path, prefix, plen) != 0)
61 		errx(1, "prefix changed!  %s vs %s", prefix, path);
62 	if (memcmp(path + plen + tlen, suffix, slen + 1) != 0)
63 		errx(1, "suffix changed!  %s vs %s", suffix, path);
64 	for (p = path + plen; p < path + plen + tlen; p++)
65 		if (*p == '\0')
66 			errx(1, "unexpected truncation");
67 		else if (*p == 'X')
68 			return 0;
69 	return 1;
70 }
71 
72 
73 void
try_mkstemp(char * p,char const * prefix,int len)74 try_mkstemp(char *p, char const *prefix, int len)
75 {
76 	char *q;
77 	size_t plen = strlen(prefix);
78 	int tries, fd;
79 
80 	for (tries = 0; tries < MAX_TRIES; tries++) {
81 		memcpy(p, prefix, plen);
82 		memset(p + plen, 'X', len);
83 		p[plen + len] = '\0';
84 		fd = mkstemp(p);
85 		if (check(fd, p, prefix, plen, "", 0, len))
86 			return;
87 	}
88 	errx(1, "exceeded MAX_TRIES");
89 }
90 
91 void
try_mkstemps(char * p,char const * prefix,int len,char const * suffix)92 try_mkstemps(char *p, char const *prefix, int len, char const *suffix)
93 {
94 	char *q;
95 	size_t plen = strlen(prefix);
96 	size_t slen = strlen(suffix);
97 	int tries, fd;
98 
99 	for (tries = 0; tries < MAX_TRIES; tries++) {
100 		memcpy(p, prefix, plen);
101 		memset(p + plen, 'X', len);
102 		memcpy(p + plen + len, suffix, slen + 1);
103 		fd = mkstemps(p, slen);
104 		if (check(fd, p, prefix, plen, suffix, slen, len))
105 			return;
106 	}
107 	errx(1, "exceeded MAX_TRIES");
108 }
109 
110 int
main(void)111 main(void)
112 {
113 	struct stat sb, fsb;
114 	char cwd[PATH_MAX + 1];
115 	char *p;
116 	size_t clen;
117 	int i;
118 
119 	pg = sysconf(_SC_PAGESIZE);
120 	if (getcwd(cwd, sizeof cwd - 1) == NULL)
121 		err(1, "getcwd");
122 	clen = strlen(cwd);
123 	cwd[clen++] = '/';
124 	cwd[clen] = '\0';
125 	p = mmap(NULL, pg * 3, PROT_READ | PROT_WRITE, MAP_ANON, -1, 0);
126 	if (p == MAP_FAILED)
127 		err(1, "mmap");
128 	if (mprotect(p, pg, PROT_NONE) || mprotect(p + pg * 2, pg, PROT_NONE))
129 		err(1, "mprotect");
130 	p += pg;
131 
132 	i = MAX_TEMPLATE_LEN + 1;
133 	while (i-- > 0) {
134 		/* try first at the start of a page, no prefix */
135 		try_mkstemp(p, "", i);
136 		/* now at the end of the page, no prefix */
137 		try_mkstemp(p + pg - i - 1, "", i);
138 		/* start of the page, prefixed with the cwd */
139 		try_mkstemp(p, cwd, i);
140 		/* how about at the end of the page, prefixed with cwd? */
141 		try_mkstemp(p + pg - clen - i - 1, cwd, i);
142 
143 		/* again, with mkstemps() and an empty suffix */
144 		/* try first at the start of a page, no prefix */
145 		try_mkstemps(p, "", i, "");
146 		/* now at the end of the page, no prefix */
147 		try_mkstemps(p + pg - i - 1, "", i, "");
148 		/* start of the page, prefixed with the cwd */
149 		try_mkstemps(p, cwd, i, "");
150 		/* how about at the end of the page, prefixed with cwd? */
151 		try_mkstemps(p + pg - clen - i - 1, cwd, i, "");
152 
153 		/* mkstemps() and a non-empty suffix */
154 		/* try first at the start of a page, no prefix */
155 		try_mkstemps(p, "", i, SUFFIX);
156 		/* now at the end of the page, no prefix */
157 		try_mkstemps(p + pg - i - SLEN - 1, "", i, SUFFIX);
158 		/* start of the page, prefixed with the cwd */
159 		try_mkstemps(p, cwd, i, SUFFIX);
160 		/* how about at the end of the page, prefixed with cwd? */
161 		try_mkstemps(p + pg - clen - i - SLEN - 1, cwd, i, SUFFIX);
162 
163 	}
164 
165 	return 0;
166 }
167