summaryrefslogtreecommitdiffstats
path: root/src/fuzz/fuzz-varlink.c
blob: 2342e34d8f77c71070516230294b43ec9c5355db (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/* SPDX-License-Identifier: LGPL-2.1-or-later */

#include <unistd.h>

#include "sd-varlink.h"

#include "errno-util.h"
#include "fd-util.h"
#include "fuzz.h"
#include "hexdecoct.h"
#include "iovec-util.h"
#include "log.h"

static FILE *null = NULL;

static int method_something(sd_varlink *v, sd_json_variant *p, sd_varlink_method_flags_t flags, void *userdata) {
        sd_json_variant_dump(p, SD_JSON_FORMAT_NEWLINE|SD_JSON_FORMAT_PRETTY, null, NULL);
        return 0;
}

static int reply_callback(sd_varlink *v, sd_json_variant *p, const char *error_id, sd_varlink_reply_flags_t flags, void *userdata) {
        sd_json_variant_dump(p, SD_JSON_FORMAT_NEWLINE|SD_JSON_FORMAT_PRETTY, null, NULL);
        return 0;
}

static int io_callback(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
        struct iovec *iov = ASSERT_PTR(userdata);
        bool write_eof = false, read_eof = false;

        assert(s);
        assert(fd >= 0);

        if ((revents & (EPOLLOUT|EPOLLHUP|EPOLLERR)) && iov->iov_len > 0) {
                ssize_t n;

                /* never write more than 143 bytes a time, to make broken up recv()s on the other side more
                 * likely, and thus test some additional code paths. */
                n = send(fd, iov->iov_base, MIN(iov->iov_len, 143U), MSG_NOSIGNAL|MSG_DONTWAIT);
                if (n < 0) {
                        if (ERRNO_IS_DISCONNECT(errno))
                                write_eof = true;
                        else
                                assert_se(errno == EAGAIN);
                } else
                        iovec_increment(iov, 1, n);
        }

        if (revents & EPOLLIN) {
                char c[137];
                ssize_t n;

                n = recv(fd, c, sizeof(c), MSG_DONTWAIT);
                if (n < 0) {
                        if (ERRNO_IS_DISCONNECT(errno))
                                read_eof = true;
                        else
                                assert_se(errno == EAGAIN);
                } else if (n == 0)
                        read_eof = true;
                else
                        hexdump(null, c, (size_t) n);
        }

        /* After we wrote everything we could turn off EPOLLOUT. And if we reached read EOF too turn off the
         * whole thing. */
        if (write_eof || iov->iov_len == 0) {

                if (read_eof)
                        assert_se(sd_event_source_set_enabled(s, SD_EVENT_OFF) >= 0);
                else
                        assert_se(sd_event_source_set_io_events(s, EPOLLIN) >= 0);
        }

        return 0;
}

static int idle_callback(sd_event_source *s, void *userdata) {
        assert(s);

        /* Called as idle callback when there's nothing else to do anymore */
        sd_event_exit(sd_event_source_get_event(s), 0);
        return 0;
}

int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
        struct iovec server_iov = IOVEC_MAKE((void*) data, size), client_iov = IOVEC_MAKE((void*) data, size);
        /* Important: the declaration order matters here! we want that the fds are closed on return after the
         * event sources, hence we declare the fds first, the event sources second */
        _cleanup_close_pair_ int server_pair[2] = EBADF_PAIR, client_pair[2] = EBADF_PAIR;
        _cleanup_(sd_event_source_unrefp) sd_event_source *idle_event_source = NULL,
                *server_event_source = NULL, *client_event_source = NULL;
        _cleanup_(sd_varlink_server_unrefp) sd_varlink_server *s = NULL;
        _cleanup_(sd_varlink_flush_close_unrefp) sd_varlink *c = NULL;
        _cleanup_(sd_event_unrefp) sd_event *e = NULL;

        fuzz_setup_logging();

        assert_se(null = fopen("/dev/null", "we"));

        assert_se(sd_event_default(&e) >= 0);

        /* Test one: write the data as method call to a server */
        assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, server_pair) >= 0);
        assert_se(sd_varlink_server_new(&s, 0) >= 0);
        assert_se(sd_varlink_server_set_description(s, "myserver") >= 0);
        assert_se(sd_varlink_server_attach_event(s, e, 0) >= 0);
        assert_se(sd_varlink_server_add_connection(s, server_pair[0], NULL) >= 0);
        TAKE_FD(server_pair[0]);
        assert_se(sd_varlink_server_bind_method(s, "io.test.DoSomething", method_something) >= 0);
        assert_se(sd_event_add_io(e, &server_event_source, server_pair[1], EPOLLIN|EPOLLOUT, io_callback, &server_iov) >= 0);

        /* Test two: write the data as method response to a client */
        assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, client_pair) >= 0);
        assert_se(sd_varlink_connect_fd(&c, client_pair[0]) >= 0);
        TAKE_FD(client_pair[0]);
        assert_se(sd_varlink_set_description(c, "myclient") >= 0);
        assert_se(sd_varlink_attach_event(c, e, 0) >= 0);
        assert_se(sd_varlink_bind_reply(c, reply_callback) >= 0);
        assert_se(sd_varlink_invoke(c, "io.test.DoSomething", NULL) >= 0);
        assert_se(sd_event_add_io(e, &client_event_source, client_pair[1], EPOLLIN|EPOLLOUT, io_callback, &client_iov) >= 0);

        assert_se(sd_event_add_defer(e, &idle_event_source, idle_callback, NULL) >= 0);
        assert_se(sd_event_source_set_priority(idle_event_source, SD_EVENT_PRIORITY_IDLE) >= 0);

        assert_se(sd_event_loop(e) >= 0);

        null = safe_fclose(null);

        return 0;
}