1 /* $OpenBSD: realpathtest.c,v 1.13 2019/08/06 11:38:16 bluhm Exp $ */
2
3 /*
4 * Copyright (c) 2019 Bob Beck <beck@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <sys/types.h>
20 #include <sys/stat.h>
21
22 #include <stdio.h>
23 #include <limits.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include <err.h>
28 #include <errno.h>
29
30 /*
31 * Reference copy of userland realpath(3) implementation.
32 * Assumed to be correct.
33 */
34 extern char *realpath3(const char *pathname, char *resolved);
35
36 struct rp_compare {
37 char * resolv2;
38 char * resolv3;
39 char * r2;
40 char * r3;
41 int e2;
42 int e3;
43 };
44
45 static struct rp_compare
rpcompare(const char * pathname,char * resolv2,char * resolv3)46 rpcompare(const char *pathname, char *resolv2,
47 char *resolv3) {
48 struct rp_compare ret = {0};
49
50 errno = 0;
51 ret.r2 = realpath(pathname, resolv2);
52 ret.e2 = errno;
53 ret.resolv2 = resolv2;
54 errno = 0;
55 ret.r3 = realpath3(pathname, resolv3);
56 ret.e3 = errno;
57 ret.resolv3 = resolv3;
58 errno = 0;
59 return ret;
60 }
61
62 #define RP_SHOULD_SUCCEED(A, B, C) do { \
63 struct rp_compare rc = rpcompare(A, B, C); \
64 if (rc.r2 == NULL) { \
65 errno = rc.e2; \
66 err(1, "%s:%d - realpath of '%s' failed", __FILE__, \
67 __LINE__, (A)); \
68 } \
69 if (rc.r3 == NULL) { \
70 errno = rc.e3; \
71 err(1, "%s:%d - realpath3 of '%s' failed", __FILE__, \
72 __LINE__, (A)); \
73 } \
74 if (strcmp(rc.r2, rc.r3) != 0) \
75 errx(1, "%s:%d - realpath of '%s' result '%s', " \
76 "expected '%s", __FILE__, __LINE__, (A), rc.r2, \
77 rc.r3); \
78 } while(0);
79
80 #define RP_SHOULD_FAIL(A, B, C) do { \
81 struct rp_compare rc = rpcompare(A, B, C); \
82 if (rc.r2 != NULL) \
83 errx(1, "%s:%d - realpath of '%s' should have failed, " \
84 "returned '%s'", __FILE__, __LINE__, (A), rc.r2); \
85 if (rc.r3 != NULL) \
86 errx(1, "%s:%d - realpath3 of '%s' should have failed, "\
87 "returned '%s'", __FILE__, __LINE__, (A), rc.r3); \
88 if (rc.e2 != rc.e3) \
89 errx(1, "%s:%d - realpath of '%s' errno %d does not " \
90 "match realpath3 errno %d", __FILE__, __LINE__, (A),\
91 rc.e2, rc.e3 ); \
92 } while(0);
93
94 int
main(int argc,char * argv[])95 main(int argc, char *argv[])
96 {
97 int i, j;
98 char big[PATH_MAX+PATH_MAX];
99 char r2[PATH_MAX];
100 char r3[PATH_MAX];
101
102 /* some basics */
103 RP_SHOULD_FAIL(NULL, NULL, NULL);
104 RP_SHOULD_FAIL("", NULL, NULL);
105 RP_SHOULD_SUCCEED("/", NULL, NULL);
106 RP_SHOULD_SUCCEED("//", NULL, NULL);
107 RP_SHOULD_SUCCEED("/./", NULL, NULL);
108 RP_SHOULD_SUCCEED("/./.", NULL, NULL);
109 RP_SHOULD_SUCCEED("/./..", NULL, NULL);
110 RP_SHOULD_SUCCEED("/../../", NULL, NULL);
111 RP_SHOULD_SUCCEED("/tmp", NULL, NULL);
112 RP_SHOULD_FAIL("/tmp/noreallydoesntexist", NULL, NULL);
113 RP_SHOULD_FAIL("/tmp/noreallydoesntexist/stillnope", NULL, NULL);
114 RP_SHOULD_SUCCEED("/bin", NULL, NULL);
115 RP_SHOULD_FAIL("/bin/herp", NULL, NULL);
116 RP_SHOULD_SUCCEED("////usr/bin", NULL, NULL);
117 RP_SHOULD_FAIL("//.//usr/bin/.././../herp", r2, r3);
118 RP_SHOULD_SUCCEED("/usr/include/machine/setjmp.h", r2, r3);
119 RP_SHOULD_FAIL("//.//usr/bin/.././../herp/derp", r2, r3);
120 RP_SHOULD_FAIL("/../.../usr/bin", r2, r3);
121 RP_SHOULD_FAIL("/bsd/herp", r2, r3);
122
123 /* relative paths */
124 if (mkdir("hoobla", 0755) == -1) {
125 if (errno != EEXIST)
126 err(1, "mkdir");
127 }
128 RP_SHOULD_SUCCEED("hoobla", r2, r3);
129 RP_SHOULD_FAIL("hoobla/porkrind", r2, r3);
130 RP_SHOULD_FAIL("hoobla/porkrind/peepee", r2, r3);
131
132 /* total size */
133 memset(big, '/', PATH_MAX + 1);
134 RP_SHOULD_FAIL(big, r2, r3);
135
136 /* component size */
137 memset(big, 'a', PATH_MAX + 1);
138 big[0] = '/';
139 big[NAME_MAX+1] = '\0';
140 RP_SHOULD_FAIL(big, r2, r3);
141 memset(big, 'a', PATH_MAX + 1);
142 big[0] = '/';
143 big[NAME_MAX+2] = '\0';
144 RP_SHOULD_FAIL(big, r2, r3);
145
146 /* long relatives back to root */
147 for (i = 0; i < (PATH_MAX - 4); i += 3) {
148 big[i] = '.';
149 big[i+1] = '.';
150 big[i+2] = '/';
151 }
152 i-= 3;
153 strlcpy(big+i, "bsd", 4);
154 RP_SHOULD_SUCCEED(big, r2, r3);
155
156 for (i = 0; i < (PATH_MAX - 5); i += 3) {
157 big[i] = '.';
158 big[i+1] = '.';
159 big[i+2] = '/';
160 }
161 i-= 3;
162 strlcpy(big+i, "bsd/", 5);
163 RP_SHOULD_FAIL(big, r2, r3);
164
165 for (i = 0; i < (PATH_MAX - 5); i += 3) {
166 big[i] = '.';
167 big[i+1] = '.';
168 big[i+2] = '/';
169 }
170 i-= 3;
171 strlcpy(big+i, "derp", 5);
172 RP_SHOULD_FAIL(big, r2, r3);
173
174 for (i = 0; i < (PATH_MAX - 6); i += 3) {
175 big[i] = '.';
176 big[i+1] = '.';
177 big[i+2] = '/';
178 }
179 i-= 3;
180 strlcpy(big+i, "derp/", 6);
181 RP_SHOULD_FAIL(big, r2, r3);
182
183 for (i = 0; i < (PATH_MAX - 4); i += 3) {
184 big[i] = '.';
185 big[i+1] = '.';
186 big[i+2] = '/';
187 }
188 i-= 3;
189 strlcpy(big+i, "xxx", 4);
190 RP_SHOULD_FAIL(big, r2, r3);
191
192 for (i = 0; i < (PATH_MAX - 8); i += 3) {
193 big[i] = '.';
194 big[i+1] = '.';
195 big[i+2] = '/';
196 }
197 i-= 3;
198 strlcpy(big+i, "xxx/../", 8);
199 RP_SHOULD_FAIL(big, r2, r3);
200
201 return (0);
202 }
203