2 * Copyright (c) 2016 Stanley Uche Godfrey
3 * Copyright (c) 2018 Jonathan Anderson
6 * This software was developed at Memorial University under the
7 * NSERC Discovery program (RGPIN-2015-06048).
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * @file po_libc_wrappers.c
34 * @brief Wrappers of libc functions that access global variables.
38 #include <sys/types.h>
39 #include <sys/socket.h>
43 #ifdef __wasilibc_unmodified_upstream // dlfcn
51 #ifdef __wasilibc_unmodified_upstream
54 #include <wasi/libc.h>
58 #ifdef __wasilibc_unmodified_upstream
60 // Make all of the po_* implementation details private.
61 #include "libpreopen.c"
67 * A default po_map that can be used implicitly by libc wrappers.
71 static struct po_map
*global_map
;
74 * Find a relative path within the po_map given by SHARED_MEMORYFD (if it
77 * @returns a struct po_relpath with dirfd and relative_path as set by po_find
78 * if there is an available po_map, or AT_FDCWD/path otherwise
80 #ifdef __wasilibc_unmodified_upstream
81 static struct po_relpath
find_relative(const char *path
, cap_rights_t
*);
83 static struct po_relpath
find_relative(const char *path
,
84 __wasi_rights_t rights_base
,
85 __wasi_rights_t rights_inheriting
);
89 * Get the map that was handed into the process via `SHARED_MEMORYFD`
92 static struct po_map
* get_shared_map(void);
96 * Wrappers around system calls:
100 * Capability-safe wrapper around the `_open(2)` system call.
102 * `_open(2)` accepts a path argument that can reference the global filesystem
103 * namespace. This is not a capability-safe operation, so this wrapper function
104 * attempts to look up the path (or a prefix of it) within the current global
105 * po_map and converts the call into the capability-safe `openat(2)` if
106 * possible. If the current po_map does not contain the sought-after path,
107 * this wrapper will call `openat(AT_FDCWD, original_path, ...)`, which is
108 * the same as the unwrapped `open(2)` call (i.e., will fail with `ECAPMODE`).
111 #ifdef __wasilibc_unmodified_upstream
112 _open(const char *path
, int flags
, ...)
114 /* We just need a plain open definition. */
115 open(const char *path
, int flags
, ...)
118 struct po_relpath rel
;
122 #ifdef __wasilibc_unmodified_upstream
123 va_start(args
, flags
);
124 mode
= va_arg(args
, int);
125 rel
= find_relative(path
, NULL
);
127 if (flags
& O_CREAT
) {
128 va_start(args
, flags
);
129 mode
= va_arg(args
, int);
134 rel
= find_relative(path
, __WASI_RIGHT_PATH_OPEN
, 0);
136 // If we can't find a preopened directory handle to open this file with,
137 // indicate that the program lacks the capabilities.
138 if (rel
.dirfd
== -1) {
144 #ifdef __wasilibc_unmodified_upstream
145 // If the file is already opened, no need of relative opening!
146 if( strcmp(rel
.relative_path
,".") == 0 )
147 return dup(rel
.dirfd
);
149 return openat(rel
.dirfd
, rel
.relative_path
, flags
, mode
);
151 return openat(rel
.dirfd
, rel
.relative_path
, flags
, mode
);
156 * Capability-safe wrapper around the `access(2)` system call.
158 * `access(2)` accepts a path argument that can reference the global filesystem
159 * namespace. This is not a capability-safe operation, so this wrapper function
160 * attempts to look up the path (or a prefix of it) within the current global
161 * po_map and converts the call into the capability-safe `faccessat(2)` if
162 * possible. If the current po_map does not contain the sought-after path,
163 * this wrapper will call `faccessat(AT_FDCWD, original_path, ...)`, which is
164 * the same as the unwrapped `access(2)` call (i.e., will fail with `ECAPMODE`).
167 access(const char *path
, int mode
)
169 #ifdef __wasilibc_unmodified_upstream
170 struct po_relpath rel
= find_relative(path
, NULL
);
172 struct po_relpath rel
= find_relative(path
, __WASI_RIGHT_PATH_FILESTAT_GET
, 0);
174 // If we can't find a preopened directory handle to open this file with,
175 // indicate that the program lacks the capabilities.
176 if (rel
.dirfd
== -1) {
182 return faccessat(rel
.dirfd
, rel
.relative_path
, mode
,0);
185 #ifdef __wasilibc_unmodified_upstream
187 * Capability-safe wrapper around the `connect(2)` system call.
189 * `connect(2)` accepts a path argument that can reference the global filesystem
190 * namespace. This is not a capability-safe operation, so this wrapper function
191 * attempts to look up the path (or a prefix of it) within the current global
192 * po_map and converts the call into the capability-safe `connectat(2)` if
193 * possible. If the current po_map does not contain the sought-after path, this
194 * wrapper will call `connectat(AT_FDCWD, original_path, ...)`, which is the
195 * same as the unwrapped `connect(2)` call (i.e., will fail with `ECAPMODE`).
198 connect(int s
, const struct sockaddr
*name
, socklen_t namelen
)
200 struct po_relpath rel
;
202 if (name
->sa_family
== AF_UNIX
) {
203 struct sockaddr_un
*usock
= (struct sockaddr_un
*)name
;
204 #ifdef __wasilibc_unmodified_upstream
205 rel
= find_relative(usock
->sun_path
, NULL
);
206 strlcpy(usock
->sun_path
, rel
.relative_path
, sizeof(usock
->sun_path
));
208 rel
= find_relative(usock
->sun_path
, __WASI_RIGHT_CONNECT
, 0);
209 if (strlen(rel
.relative_path
) + 1 > sizeof(usock
->sun_path
)) {
214 // If we can't find a preopened directory handle to open this file with,
215 // indicate that the program lacks the capabilities.
216 if (rel
.dirfd
== -1) {
221 strcpy(usock
->sun_path
, rel
.relative_path
);
223 return connectat(rel
.dirfd
, s
, name
, namelen
);
226 return connectat(AT_FDCWD
, s
, name
, namelen
);
231 * Capability-safe wrapper around the `eaccess(2)` system call.
233 * `eaccess(2)` accepts a path argument that can reference the global filesystem
234 * namespace. This is not a capability-safe operation, so this wrapper function
235 * attempts to look up the path (or a prefix of it) within the current global
236 * po_map and converts the call into the capability-safe `faccessat(2)` if
237 * possible. If the current po_map does not contain the sought-after path, this
238 * wrapper will call `faccessat(AT_FDCWD, original_path, ...)`, which is the
239 * same as the unwrapped `eaccess(2)` call (i.e., will fail with `ECAPMODE`).
242 eaccess(const char *path
, int mode
)
244 #ifdef __wasilibc_unmodified_upstream
245 struct po_relpath rel
= find_relative(path
, NULL
);
247 struct po_relpath rel
= find_relative(path
, __WASI_RIGHT_PATH_FILESTAT_GET
, 0);
249 // If we can't find a preopened directory handle to open this file with,
250 // indicate that the program lacks the capabilities.
251 if (rel
.dirfd
== -1) {
257 return faccessat(rel
.dirfd
, rel
.relative_path
, mode
, 0);
261 * Capability-safe wrapper around the `lstat(2)` system call.
263 * `lstat(2)` accepts a path argument that can reference the global filesystem
264 * namespace. This is not a capability-safe operation, so this wrapper function
265 * attempts to look up the path (or a prefix of it) within the current global
266 * po_map and converts the call into the capability-safe `fstatat(2)` if
267 * possible. If the current po_map does not contain the sought-after path,
268 * this wrapper will call `fstatat(AT_FDCWD, original_path, ...)`, which is
269 * the same as the unwrapped `lstat(2)` call (i.e., will fail with `ECAPMODE`).
272 lstat(const char *path
, struct stat
*st
)
274 #ifdef __wasilibc_unmodified_upstream
275 struct po_relpath rel
= find_relative(path
, NULL
);
277 struct po_relpath rel
= find_relative(path
, __WASI_RIGHT_PATH_FILESTAT_GET
, 0);
279 // If we can't find a preopened directory handle to open this file with,
280 // indicate that the program lacks the capabilities.
281 if (rel
.dirfd
== -1) {
287 return fstatat(rel
.dirfd
, rel
.relative_path
,st
,AT_SYMLINK_NOFOLLOW
);
290 #ifdef __wasilibc_unmodified_upstream
292 * Capability-safe wrapper around the `open(2)` system call.
294 * `open(2)` will behave just like `_open(2)` if the varargs are unpacked and
298 open(const char *path
, int flags
, ...)
303 va_start(args
, flags
);
304 mode
= va_arg(args
, int);
305 return _open(path
, flags
, mode
);
310 * Capability-safe wrapper around the `rename(2)` system call.
312 * `rename(2)` accepts a path argument that can reference the global filesystem
313 * namespace. This is not a capability-safe operation, so this wrapper function
314 * attempts to look up the path (or a prefix of it) within the current global
315 * po_map and converts the call into the capability-safe `renameat(2)` if
316 * possible. If the current po_map does not contain the sought-after path,
317 * this wrapper will call `renameat(AT_FDCWD, original_path, ...)`, which is
318 * the same as the unwrapped `rename(2)` call (i.e., will fail with `ECAPMODE`).
321 rename(const char *from
, const char *to
)
323 #ifdef __wasilibc_unmodified_upstream
324 struct po_relpath rel_from
= find_relative(from
, NULL
);
325 struct po_relpath rel_to
= find_relative(to
, NULL
);
327 struct po_relpath rel_from
= find_relative(from
, __WASI_RIGHT_PATH_RENAME_SOURCE
, 0);
328 struct po_relpath rel_to
= find_relative(to
, __WASI_RIGHT_PATH_RENAME_TARGET
, 0);
330 // If we can't find a preopened directory handle to open this file with,
331 // indicate that the program lacks the capabilities.
332 if (rel_from
.dirfd
== -1 || rel_to
.dirfd
== -1) {
338 return renameat(rel_from
.dirfd
, rel_from
.relative_path
, rel_to
.dirfd
,
339 rel_to
.relative_path
);
343 * Capability-safe wrapper around the `stat(2)` system call.
345 * `stat(2)` accepts a path argument that can reference the global filesystem
346 * namespace. This is not a capability-safe operation, so this wrapper function
347 * attempts to look up the path (or a prefix of it) within the current global
348 * po_map and converts the call into the capability-safe `fstatat(2)` if
349 * possible. If the current po_map does not contain the sought-after path,
350 * this wrapper will call `fstatat(AT_FDCWD, original_path, ...)`, which is
351 * the same as the unwrapped `stat(2)` call (i.e., will fail with `ECAPMODE`).
354 stat(const char *path
, struct stat
*st
)
356 #ifdef __wasilibc_unmodified_upstream
357 struct po_relpath rel
= find_relative(path
, NULL
);
359 struct po_relpath rel
= find_relative(path
, __WASI_RIGHT_PATH_FILESTAT_GET
, 0);
361 // If we can't find a preopened directory handle to open this file with,
362 // indicate that the program lacks the capabilities.
363 if (rel
.dirfd
== -1) {
369 return fstatat(rel
.dirfd
, rel
.relative_path
,st
, AT_SYMLINK_NOFOLLOW
);
373 * Wrappers around other libc calls:
376 #ifdef __wasilibc_unmodified_upstream
378 * Capability-safe wrapper around the `dlopen(3)` libc function.
380 * `dlopen(3)` accepts a path argument that can reference the global filesystem
381 * namespace. This is not a capability-safe operation, so this wrapper function
382 * attempts to look up the path (or a prefix of it) within the current global
383 * po_map and converts the call into the capability-safe `fdlopen(3)` if
384 * possible. If the current po_map does not contain the sought-after path, this
385 * wrapper will call `fdlopen(openat(AT_FDCWD, original_path), ...)`, which is
386 * the same as the unwrapped `dlopen(3)` call (i.e., will fail with `ECAPMODE`).
389 dlopen(const char *path
, int mode
)
391 struct po_relpath rel
= find_relative(path
, NULL
);
393 return fdlopen(openat(rel
.dirfd
, rel
.relative_path
, 0, mode
), mode
);
396 #ifdef __wasilibc_unmodified_upstream
401 unlink(const char *pathname
)
403 struct po_relpath rel_pathname
= find_relative(pathname
, __WASI_RIGHT_PATH_UNLINK_FILE
, 0);
405 // If we can't find a preopened directory handle to open this file with,
406 // indicate that the program lacks the capabilities.
407 if (rel_pathname
.dirfd
== -1) {
412 return __wasilibc_unlinkat(rel_pathname
.dirfd
, rel_pathname
.relative_path
);
416 rmdir(const char *pathname
)
418 struct po_relpath rel_pathname
= find_relative(pathname
, __WASI_RIGHT_PATH_REMOVE_DIRECTORY
, 0);
420 // If we can't find a preopened directory handle to open this file with,
421 // indicate that the program lacks the capabilities.
422 if (rel_pathname
.dirfd
== -1) {
427 return __wasilibc_rmdirat(rel_pathname
.dirfd
, rel_pathname
.relative_path
);
431 remove(const char *pathname
)
433 struct po_relpath rel_pathname
= find_relative(pathname
,
434 __WASI_RIGHT_PATH_UNLINK_FILE
|
435 __WASI_RIGHT_PATH_REMOVE_DIRECTORY
,
438 // If searching for both file and directory rights failed, try searching
439 // for either individually.
440 if (rel_pathname
.dirfd
== -1) {
441 rel_pathname
= find_relative(pathname
, __WASI_RIGHT_PATH_UNLINK_FILE
, 0);
442 if (rel_pathname
.dirfd
== -1) {
443 rel_pathname
= find_relative(pathname
, __WASI_RIGHT_PATH_REMOVE_DIRECTORY
, 0);
447 // If we can't find a preopened directory handle to open this file with,
448 // indicate that the program lacks the capabilities.
449 if (rel_pathname
.dirfd
== -1) {
454 int r
= __wasilibc_unlinkat(rel_pathname
.dirfd
, rel_pathname
.relative_path
);
455 if (r
!= 0 && (errno
== EISDIR
|| errno
== ENOTCAPABLE
))
456 r
= __wasilibc_rmdirat(rel_pathname
.dirfd
, rel_pathname
.relative_path
);
461 link(const char *oldpath
, const char *newpath
)
463 struct po_relpath rel_oldpath
= find_relative(oldpath
, __WASI_RIGHT_PATH_LINK_SOURCE
, 0);
464 struct po_relpath rel_newpath
= find_relative(newpath
, __WASI_RIGHT_PATH_LINK_TARGET
, 0);
466 // If we can't find a preopened directory handle to open this file with,
467 // indicate that the program lacks the capabilities.
468 if (rel_oldpath
.dirfd
== -1 || rel_newpath
.dirfd
== -1) {
473 return linkat(rel_oldpath
.dirfd
, rel_oldpath
.relative_path
,
474 rel_newpath
.dirfd
, rel_newpath
.relative_path
,
479 mkdir(const char *pathname
, mode_t mode
)
481 struct po_relpath rel_pathname
= find_relative(pathname
, __WASI_RIGHT_PATH_CREATE_DIRECTORY
, 0);
483 // If we can't find a preopened directory handle to open this file with,
484 // indicate that the program lacks the capabilities.
485 if (rel_pathname
.dirfd
== -1) {
490 return mkdirat(rel_pathname
.dirfd
, rel_pathname
.relative_path
, mode
);
494 opendir(const char *name
)
496 struct po_relpath rel_name
= find_relative(name
, __WASI_RIGHT_PATH_OPEN
, 0);
498 // If we can't find a preopened directory handle to open this file with,
499 // indicate that the program lacks the capabilities.
500 if (rel_name
.dirfd
== -1) {
505 return opendirat(rel_name
.dirfd
, rel_name
.relative_path
);
509 readlink(const char *pathname
, char *buf
, size_t bufsiz
)
511 struct po_relpath rel_pathname
= find_relative(pathname
, __WASI_RIGHT_PATH_READLINK
, 0);
513 // If we can't find a preopened directory handle to open this file with,
514 // indicate that the program lacks the capabilities.
515 if (rel_pathname
.dirfd
== -1) {
520 return readlinkat(rel_pathname
.dirfd
, rel_pathname
.relative_path
,
525 scandir(const char *dirp
, struct dirent
***namelist
,
526 int (*filter
)(const struct dirent
*),
527 int (*compar
)(const struct dirent
**, const struct dirent
**))
529 struct po_relpath rel_dirp
= find_relative(dirp
,
530 __WASI_RIGHT_PATH_OPEN
,
531 __WASI_RIGHT_FD_READDIR
);
533 // If we can't find a preopened directory handle to open this file with,
534 // indicate that the program lacks the capabilities.
535 if (rel_dirp
.dirfd
== -1) {
540 return scandirat(rel_dirp
.dirfd
, rel_dirp
.relative_path
,
541 namelist
, filter
, compar
);
545 symlink(const char *target
, const char *linkpath
)
547 struct po_relpath rel_linkpath
= find_relative(linkpath
, __WASI_RIGHT_PATH_SYMLINK
, 0);
549 // If we can't find a preopened directory handle to open this file with,
550 // indicate that the program lacks the capabilities.
551 if (rel_linkpath
.dirfd
== -1) {
556 return symlinkat(target
, rel_linkpath
.dirfd
, rel_linkpath
.relative_path
);
560 #ifdef __wasilibc_unmodified_upstream
564 /* Provide tests with mechanism to set our static po_map */
566 po_set_libc_map(struct po_map
*map
)
568 po_map_assertvalid(map
);
572 if (global_map
!= NULL
) {
573 po_map_release(global_map
);
579 static struct po_relpath
580 #ifdef __wasilibc_unmodified_upstream
581 find_relative(const char *path
, cap_rights_t
*rights
)
583 find_relative(const char *path
,
584 __wasi_rights_t rights_base
,
585 __wasi_rights_t rights_inheriting
)
588 struct po_relpath rel
;
591 map
= get_shared_map();
592 #ifdef __wasilibc_unmodified_upstream
594 rel
.dirfd
= AT_FDCWD
;
595 rel
.relative_path
= path
;
597 rel
= po_find(map
, path
, NULL
);
600 rel
= po_find(map
, path
, rights_base
, rights_inheriting
);
606 static struct po_map
*
609 #ifdef __wasilibc_unmodified_upstream
614 // Do we already have a default map?
616 po_map_assertvalid(global_map
);
620 // Attempt to unwrap po_map from a shared memory segment specified by
622 env
= getenv("SHARED_MEMORYFD");
623 if (env
== NULL
|| *env
== '\0') {
627 // We expect this environment variable to be an integer and nothing but
629 fd
= strtol(env
, &end
, 10);
644 po_map_assertvalid(global_map
);
648 #ifdef __wasilibc_unmodified_upstream
651 __wasilibc_init_preopen(void)
653 global_map
= po_map_create(4);
654 if (global_map
== NULL
)
656 po_map_assertvalid(global_map
);
660 * Register the given pre-opened file descriptor under the given path.
663 __wasilibc_register_preopened_fd(int fd
, const char *path
)
665 po_map_assertvalid(global_map
);
672 #error "__wasilibc_register_preopened_fd doesn't yet support multiple threads"
675 struct po_map
*map
= po_add(global_map
, path
, fd
);
680 po_map_assertvalid(map
);
686 int __wasilibc_find_relpath(const char *path
,
687 __wasi_rights_t rights_base
,
688 __wasi_rights_t rights_inheriting
,
689 const char **relpath
)
691 struct po_relpath rel
= find_relative(path
, rights_base
, rights_inheriting
);
692 *relpath
= rel
.relative_path
;