From f13a8d7ced240456d9002460bfc243c3097e77e5 Mon Sep 17 00:00:00 2001 From: Arthur Chan Date: Wed, 28 Jan 2026 09:44:11 +0000 Subject: [PATCH] OSS-Fuzz Add fuzzer and corpora for sftp attr parsing Signed-off-by: Arthur Chan Reviewed-by: Jakub Jelen --- tests/fuzz/CMakeLists.txt | 1 + tests/fuzz/ssh_sftp_attr_fuzzer.c | 131 ++++++++++++++++++ .../04069f01e1b3cead6a975b05c89ed937ecdccd8b | Bin 0 -> 12 bytes .../3c7d22add37e715968d08d7ecdc485468224e22e | Bin 0 -> 12 bytes .../42e5dec24d93b7e96782a637f3bba4d4c81694c0 | Bin 0 -> 12 bytes .../8e2f0674d9cde8f8d4420afcc510b46efa401c34 | Bin 0 -> 8 bytes .../9069ca78e7450a285173431b3e52c5c25299e473 | Bin 0 -> 4 bytes .../b2527d28f012dfaf7fa8beae32c05c4a0c8a41f3 | Bin 0 -> 16 bytes .../be142bc50527cebc6e024382c1609e7ab38cd236 | Bin 0 -> 77 bytes 9 files changed, 132 insertions(+) create mode 100644 tests/fuzz/ssh_sftp_attr_fuzzer.c create mode 100644 tests/fuzz/ssh_sftp_attr_fuzzer_corpus/04069f01e1b3cead6a975b05c89ed937ecdccd8b create mode 100644 tests/fuzz/ssh_sftp_attr_fuzzer_corpus/3c7d22add37e715968d08d7ecdc485468224e22e create mode 100644 tests/fuzz/ssh_sftp_attr_fuzzer_corpus/42e5dec24d93b7e96782a637f3bba4d4c81694c0 create mode 100644 tests/fuzz/ssh_sftp_attr_fuzzer_corpus/8e2f0674d9cde8f8d4420afcc510b46efa401c34 create mode 100644 tests/fuzz/ssh_sftp_attr_fuzzer_corpus/9069ca78e7450a285173431b3e52c5c25299e473 create mode 100644 tests/fuzz/ssh_sftp_attr_fuzzer_corpus/b2527d28f012dfaf7fa8beae32c05c4a0c8a41f3 create mode 100644 tests/fuzz/ssh_sftp_attr_fuzzer_corpus/be142bc50527cebc6e024382c1609e7ab38cd236 diff --git a/tests/fuzz/CMakeLists.txt b/tests/fuzz/CMakeLists.txt index 8f2f97e6..35ba2609 100644 --- a/tests/fuzz/CMakeLists.txt +++ b/tests/fuzz/CMakeLists.txt @@ -33,6 +33,7 @@ fuzzer(ssh_client_config_fuzzer) fuzzer(ssh_known_hosts_fuzzer) fuzzer(ssh_privkey_fuzzer) fuzzer(ssh_pubkey_fuzzer) +fuzzer(ssh_sftp_attr_fuzzer) fuzzer(ssh_sshsig_fuzzer) if (WITH_SERVER) fuzzer(ssh_server_fuzzer) diff --git a/tests/fuzz/ssh_sftp_attr_fuzzer.c b/tests/fuzz/ssh_sftp_attr_fuzzer.c new file mode 100644 index 00000000..e843cc37 --- /dev/null +++ b/tests/fuzz/ssh_sftp_attr_fuzzer.c @@ -0,0 +1,131 @@ +/* + * Copyright 2026 libssh authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +#define LIBSSH_STATIC 1 +#include "libssh/libssh.h" +#include "libssh/sftp.h" +#include "libssh/sftp_priv.h" + +#include "nallocinc.c" + +/* SFTP protocol version constants */ +#define SFTP_PROTOCOL_VERSION_3 3 +#define SFTP_PROTOCOL_VERSION_4 4 + +/* Flags for sftp_parse_attr expectname parameter */ +#define SFTP_EXPECT_NAME 1 +#define SFTP_NO_NAME 0 + +/* + * Helper to create a minimal sftp_session for fuzzing. + * We don't use sftp_new() as it requires a real SSH connection. + */ +static sftp_session create_minimal_sftp_session(ssh_session session) +{ + sftp_session sftp; + + sftp = calloc(1, sizeof(struct sftp_session_struct)); + if (sftp == NULL) { + return NULL; + } + sftp->session = session; + + return sftp; +} + +static void _fuzz_finalize(void) +{ + ssh_finalize(); +} + +int LLVMFuzzerInitialize(int *argc, char ***argv) +{ + (void)argc; + + nalloc_init(*argv[0]); + + ssh_init(); + + atexit(_fuzz_finalize); + + return 0; +} + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + ssh_session session = NULL; + sftp_session sftp = NULL; + ssh_buffer buffer = NULL; + sftp_attributes attr = NULL; + int versions[] = { + SFTP_PROTOCOL_VERSION_3, SFTP_PROTOCOL_VERSION_3, + SFTP_PROTOCOL_VERSION_4, SFTP_PROTOCOL_VERSION_4 + }; + int expectnames[] = {SFTP_NO_NAME, SFTP_EXPECT_NAME, SFTP_NO_NAME, SFTP_EXPECT_NAME}; + size_t i; + + /* Minimum bytes for a valid SFTP message */ + if (size == 0) { + return 0; + } + + assert(nalloc_start(data, size) > 0); + + /* Allocate shared resources once for all test iterations */ + session = ssh_new(); + if (session == NULL) { + goto cleanup; + } + + sftp = create_minimal_sftp_session(session); + if (sftp == NULL) { + goto cleanup; + } + + buffer = ssh_buffer_new(); + if (buffer == NULL) { + goto cleanup; + } + + /* Main fuzzing target: sftp_parse_attr */ + /* Parses untrusted SFTP messages from client */ + /* Test all combinations (v3/v4, with/without name) */ + for (i = 0; i < (sizeof(versions) / sizeof(versions[0])); i++) { + sftp->version = versions[i]; + + /* Reset and repopulate buffer for each iteration */ + ssh_buffer_reinit(buffer); + if (ssh_buffer_add_data(buffer, data, size) == SSH_OK) { + attr = sftp_parse_attr(sftp, buffer, expectnames[i]); + sftp_attributes_free(attr); + attr = NULL; + } + } + +cleanup: + ssh_buffer_free(buffer); + free(sftp); + ssh_free(session); + nalloc_end(); + + return 0; +} diff --git a/tests/fuzz/ssh_sftp_attr_fuzzer_corpus/04069f01e1b3cead6a975b05c89ed937ecdccd8b b/tests/fuzz/ssh_sftp_attr_fuzzer_corpus/04069f01e1b3cead6a975b05c89ed937ecdccd8b new file mode 100644 index 0000000000000000000000000000000000000000..0bf30cc524c4cda86ed36e76d124cdc73b180f88 GIT binary patch literal 12 QcmZQzU|?ckV15C@00pK2*#H0l literal 0 HcmV?d00001 diff --git a/tests/fuzz/ssh_sftp_attr_fuzzer_corpus/3c7d22add37e715968d08d7ecdc485468224e22e b/tests/fuzz/ssh_sftp_attr_fuzzer_corpus/3c7d22add37e715968d08d7ecdc485468224e22e new file mode 100644 index 0000000000000000000000000000000000000000..6ea125482a4ffc312a16065c8a66f7c44c418b5c GIT binary patch literal 12 PcmZQzU|?hb0|5pA05t#- literal 0 HcmV?d00001 diff --git a/tests/fuzz/ssh_sftp_attr_fuzzer_corpus/42e5dec24d93b7e96782a637f3bba4d4c81694c0 b/tests/fuzz/ssh_sftp_attr_fuzzer_corpus/42e5dec24d93b7e96782a637f3bba4d4c81694c0 new file mode 100644 index 0000000000000000000000000000000000000000..208edefe5d366d59e27b62c49499faba8e7276e8 GIT binary patch literal 12 TcmZQzVBm;nU|@(BU|;|M1o!~g literal 0 HcmV?d00001 diff --git a/tests/fuzz/ssh_sftp_attr_fuzzer_corpus/8e2f0674d9cde8f8d4420afcc510b46efa401c34 b/tests/fuzz/ssh_sftp_attr_fuzzer_corpus/8e2f0674d9cde8f8d4420afcc510b46efa401c34 new file mode 100644 index 0000000000000000000000000000000000000000..a0fd5efc8ca25ece30a861509c03c92359cbaaee GIT binary patch literal 8 PcmZQzU|?ZjXj}pS0m1<) literal 0 HcmV?d00001 diff --git a/tests/fuzz/ssh_sftp_attr_fuzzer_corpus/9069ca78e7450a285173431b3e52c5c25299e473 b/tests/fuzz/ssh_sftp_attr_fuzzer_corpus/9069ca78e7450a285173431b3e52c5c25299e473 new file mode 100644 index 0000000000000000000000000000000000000000..593f4708db84ac8fd0f5cc47c634f38c013fe9e4 GIT binary patch literal 4 LcmZQzU|;|M00aO5 literal 0 HcmV?d00001 diff --git a/tests/fuzz/ssh_sftp_attr_fuzzer_corpus/b2527d28f012dfaf7fa8beae32c05c4a0c8a41f3 b/tests/fuzz/ssh_sftp_attr_fuzzer_corpus/b2527d28f012dfaf7fa8beae32c05c4a0c8a41f3 new file mode 100644 index 0000000000000000000000000000000000000000..b14dc38535b19d796426a3a6ad6b95e98742a83f GIT binary patch literal 16 ScmZQzU|?kc0|g-2_#XfR5&@