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
29static int               fd_fuzz = -1;
30static int              (*open_f)(const char *, int, mode_t);
31static int              (*close_f)(int);
32static ssize_t          (*write_f)(int, const void *, size_t);
33
34int
35open(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
71int
72close(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
89ssize_t
90write(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