1 /*
2  * Copyright (c) 2019 Yubico AB. All rights reserved.
3  * Use of this source code is governed by a BSD-style
4  * license that can be found in the LICENSE file.
5  * SPDX-License-Identifier: BSD-2-Clause
6  */
7 
8 /*
9  * cc -fPIC -D_GNU_SOURCE -shared -o preload-fuzz.so preload-fuzz.c
10  * LD_PRELOAD=$(realpath preload-fuzz.so)
11  */
12 
13 #include <sys/types.h>
14 #include <sys/stat.h>
15 
16 #include <dlfcn.h>
17 #include <err.h>
18 #include <errno.h>
19 #include <fcntl.h>
20 #include <limits.h>
21 #include <stdarg.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 
27 #define FUZZ_DEV_PREFIX	"nodev"
28 
29 static int               fd_fuzz = -1;
30 static int              (*open_f)(const char *, int, mode_t);
31 static int              (*close_f)(int);
32 static ssize_t          (*write_f)(int, const void *, size_t);
33 
34 int
35 open(const char *path, int flags, ...)
36 {
37 	va_list	ap;
38 	mode_t	mode;
39 
40 	va_start(ap, flags);
41 	mode = va_arg(ap, mode_t);
42 	va_end(ap);
43 
44 	if (open_f == NULL) {
45 		open_f = dlsym(RTLD_NEXT, "open");
46 		if (open_f == NULL) {
47 			warnx("%s: dlsym", __func__);
48 			errno = EACCES;
49 			return (-1);
50 		}
51 	}
52 
53 	if (strncmp(path, FUZZ_DEV_PREFIX, strlen(FUZZ_DEV_PREFIX)) != 0)
54 		return (open_f(path, flags, mode));
55 
56 	if (fd_fuzz != -1) {
57 		warnx("%s: fd_fuzz != -1", __func__);
58 		errno = EACCES;
59 		return (-1);
60 	}
61 
62 	if ((fd_fuzz = dup(STDIN_FILENO)) < 0) {
63 		warn("%s: dup", __func__);
64 		errno = EACCES;
65 		return (-1);
66 	}
67 
68 	return (fd_fuzz);
69 }
70 
71 int
72 close(int fd)
73 {
74 	if (close_f == NULL) {
75 		close_f = dlsym(RTLD_NEXT, "close");
76 		if (close_f == NULL) {
77 			warnx("%s: dlsym", __func__);
78 			errno = EACCES;
79 			return (-1);
80 		}
81 	}
82 
83 	if (fd == fd_fuzz)
84 		fd_fuzz = -1;
85 
86 	return (close_f(fd));
87 }
88 
89 ssize_t
90 write(int fd, const void *buf, size_t nbytes)
91 {
92 	if (write_f == NULL) {
93 		write_f = dlsym(RTLD_NEXT, "write");
94 		if (write_f == NULL) {
95 			warnx("%s: dlsym", __func__);
96 			errno = EBADF;
97 			return (-1);
98 		}
99 	}
100 
101 	if (fd != fd_fuzz)
102 		return (write_f(fd, buf, nbytes));
103 
104 	return (nbytes);
105 }
106