Coverage Report

Created: 2025-11-16 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libssh/tests/fuzz/ssh_client_fuzzer.c
Line
Count
Source
1
/*
2
 * Copyright 2019 Andreas Schneider <asn@cryptomilk.org>
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *      http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
17
#include <assert.h>
18
#include <stdio.h>
19
#include <stdlib.h>
20
#include <signal.h>
21
#include <stdbool.h>
22
23
#define LIBSSH_STATIC 1
24
#include <libssh/libssh.h>
25
#include <libssh/callbacks.h>
26
27
#include "nallocinc.c"
28
29
static void _fuzz_finalize(void)
30
4
{
31
4
    ssh_finalize();
32
4
}
33
34
int LLVMFuzzerInitialize(int *argc, char ***argv)
35
32
{
36
32
    (void)argc;
37
38
32
    nalloc_init(*argv[0]);
39
40
32
    ssh_init();
41
42
32
    atexit(_fuzz_finalize);
43
44
32
    return 0;
45
32
}
46
47
static int auth_callback(const char *prompt,
48
                         char *buf,
49
                         size_t len,
50
                         int echo,
51
                         int verify,
52
                         void *userdata)
53
0
{
54
0
    (void)prompt;   /* unused */
55
0
    (void)echo;     /* unused */
56
0
    (void)verify;   /* unused */
57
0
    (void)userdata; /* unused */
58
59
0
    snprintf(buf, len, "secret");
60
61
0
    return 0;
62
0
}
63
64
struct ssh_callbacks_struct cb = {
65
    .userdata = NULL,
66
    .auth_function = auth_callback,
67
};
68
69
static void select_loop(ssh_session session, ssh_channel channel)
70
0
{
71
0
    ssh_connector connector_in, connector_out, connector_err;
72
73
0
    ssh_event event = ssh_event_new();
74
75
    /* stdin */
76
0
    connector_in = ssh_connector_new(session);
77
0
    ssh_connector_set_out_channel(connector_in, channel, SSH_CONNECTOR_STDINOUT);
78
0
    ssh_connector_set_in_fd(connector_in, 0);
79
0
    ssh_event_add_connector(event, connector_in);
80
81
    /* stdout */
82
0
    connector_out = ssh_connector_new(session);
83
0
    ssh_connector_set_out_fd(connector_out, 1);
84
0
    ssh_connector_set_in_channel(connector_out, channel, SSH_CONNECTOR_STDINOUT);
85
0
    ssh_event_add_connector(event, connector_out);
86
87
    /* stderr */
88
0
    connector_err = ssh_connector_new(session);
89
0
    ssh_connector_set_out_fd(connector_err, 2);
90
0
    ssh_connector_set_in_channel(connector_err, channel, SSH_CONNECTOR_STDERR);
91
0
    ssh_event_add_connector(event, connector_err);
92
93
0
    while (ssh_channel_is_open(channel)) {
94
0
        ssh_event_dopoll(event, 60000);
95
0
    }
96
0
    ssh_event_remove_connector(event, connector_in);
97
0
    ssh_event_remove_connector(event, connector_out);
98
0
    ssh_event_remove_connector(event, connector_err);
99
100
0
    ssh_connector_free(connector_in);
101
0
    ssh_connector_free(connector_out);
102
0
    ssh_connector_free(connector_err);
103
104
0
    ssh_event_free(event);
105
0
}
106
107
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
108
3.72k
{
109
3.72k
    ssh_session session = NULL;
110
3.72k
    ssh_channel channel = NULL;
111
3.72k
    const char *env = NULL;
112
3.72k
    int socket_fds[2] = {-1, -1};
113
3.72k
    ssize_t nwritten;
114
3.72k
    bool no = false;
115
3.72k
    int rc;
116
3.72k
    long timeout = 1; /* use short timeout to avoid timeouts during fuzzing */
117
118
    /* This is the maximum that can be handled by the socket buffer before the
119
     * other side will read some data. Other option would be feeding the socket
120
     * from different thread which would not mind if it would be blocked, but I
121
     * believe all the important inputs should fit into this size */
122
3.72k
    if (size > 219264) {
123
5
        return -1;
124
5
    }
125
126
    /* Set up the socket to send data */
127
3.72k
    rc = socketpair(AF_UNIX, SOCK_STREAM, 0, socket_fds);
128
3.72k
    assert(rc == 0);
129
130
3.72k
    nwritten = send(socket_fds[1], data, size, 0);
131
3.72k
    assert((size_t)nwritten == size);
132
133
3.72k
    rc = shutdown(socket_fds[1], SHUT_WR);
134
3.72k
    assert(rc == 0);
135
136
3.72k
    assert(nalloc_start(data, size) > 0);
137
138
3.72k
    session = ssh_new();
139
3.72k
    if (session == NULL) {
140
182
        goto out;
141
182
    }
142
143
3.54k
    env = getenv("LIBSSH_VERBOSITY");
144
3.54k
    if (env != NULL && strlen(env) > 0) {
145
0
        ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY_STR, env);
146
0
    }
147
3.54k
    rc = ssh_options_set(session, SSH_OPTIONS_FD, &socket_fds[0]);
148
3.54k
    if (rc != SSH_OK) {
149
0
        goto out;
150
0
    }
151
3.54k
    rc = ssh_options_set(session, SSH_OPTIONS_HOST, "127.0.0.1");
152
3.54k
    if (rc != SSH_OK) {
153
0
        goto out;
154
0
    }
155
3.54k
    rc = ssh_options_set(session, SSH_OPTIONS_USER, "alice");
156
3.54k
    if (rc != SSH_OK) {
157
0
        goto out;
158
0
    }
159
3.54k
    rc = ssh_options_set(session, SSH_OPTIONS_CIPHERS_C_S, "none");
160
3.54k
    if (rc != SSH_OK) {
161
0
        goto out;
162
0
    }
163
3.54k
    rc = ssh_options_set(session, SSH_OPTIONS_CIPHERS_S_C, "none");
164
3.54k
    if (rc != SSH_OK) {
165
0
        goto out;
166
0
    }
167
3.54k
    rc = ssh_options_set(session, SSH_OPTIONS_HMAC_C_S, "none");
168
3.54k
    if (rc != SSH_OK) {
169
0
        goto out;
170
0
    }
171
3.54k
    rc = ssh_options_set(session, SSH_OPTIONS_HMAC_S_C, "none");
172
3.54k
    if (rc != SSH_OK) {
173
0
        goto out;
174
0
    }
175
3.54k
    rc = ssh_options_set(session, SSH_OPTIONS_PROCESS_CONFIG, &no);
176
3.54k
    if (rc != SSH_OK) {
177
0
        goto out;
178
0
    }
179
3.54k
    rc = ssh_options_set(session, SSH_OPTIONS_TIMEOUT, &timeout);
180
3.54k
    if (rc != SSH_OK) {
181
0
        goto out;
182
0
    }
183
184
3.54k
    ssh_callbacks_init(&cb);
185
3.54k
    ssh_set_callbacks(session, &cb);
186
187
3.54k
    rc = ssh_connect(session);
188
3.54k
    if (rc != SSH_OK) {
189
3.53k
        goto out;
190
3.53k
    }
191
192
6
    rc = ssh_userauth_none(session, NULL);
193
6
    if (rc != SSH_OK) {
194
6
        goto out;
195
6
    }
196
197
0
    channel = ssh_channel_new(session);
198
0
    if (channel == NULL) {
199
0
        goto out;
200
0
    }
201
202
0
    rc = ssh_channel_open_session(channel);
203
0
    if (rc != SSH_OK) {
204
0
        goto out;
205
0
    }
206
207
0
    rc = ssh_channel_request_exec(channel, "ls");
208
0
    if (rc != SSH_OK) {
209
0
        goto out;
210
0
    }
211
212
0
    select_loop(session, channel);
213
214
3.72k
out:
215
3.72k
    ssh_channel_free(channel);
216
3.72k
    ssh_disconnect(session);
217
3.72k
    ssh_free(session);
218
219
3.72k
    close(socket_fds[0]);
220
3.72k
    close(socket_fds[1]);
221
222
3.72k
    nalloc_end();
223
3.72k
    return 0;
224
0
}