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 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 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