[openssh-commits] [openssh] 09/15: OSS-Fuzz: Fix kex_fuzz to include rotation and new protocol
git+noreply at mindrot.org
git+noreply at mindrot.org
Fri Oct 9 21:38:27 AEDT 2026
This is an automated email from the git hooks/post-receive script.
dtucker pushed a commit to branch master
in repository openssh.
commit 9c7004f6c86faa7cbf746022c903467fd6106e4b
Author: Arthur Chan <arthur.chan at adalogics.com>
AuthorDate: Fri May 29 13:31:00 2026 +0100
OSS-Fuzz: Fix kex_fuzz to include rotation and new protocol
Signed-off-by: Arthur Chan <arthur.chan at adalogics.com>
---
regress/misc/fuzz-harness/kex_fuzz.cc | 128 ++++++++++++++++++++++++++++------
1 file changed, 107 insertions(+), 21 deletions(-)
diff --git a/regress/misc/fuzz-harness/kex_fuzz.cc b/regress/misc/fuzz-harness/kex_fuzz.cc
index f126d93f6..24b6690d2 100644
--- a/regress/misc/fuzz-harness/kex_fuzz.cc
+++ b/regress/misc/fuzz-harness/kex_fuzz.cc
@@ -37,6 +37,16 @@ struct test_state {
struct sshbuf *s_template, *c_template; /* main copy of input */
};
+/*
+ * Optional proposal overrides applied to both sides. Each field
+ * replaces one KEXINIT slot when non-NULL.
+ */
+struct kex_overrides {
+ const char *cipher;
+ const char *mac;
+ const char *comp;
+};
+
static int
do_send_and_receive(struct ssh *from, struct ssh *to,
struct sshbuf *store, int clobber, size_t *n)
@@ -170,16 +180,49 @@ get_privkey(struct shared_state *st, int keytype)
return st->privkeys[keytype];
}
+static int
+build_proposal_side(char **out, const char *defaults[PROPOSAL_MAX],
+ const char *kex, const char *keyname,
+ const char *cipher, const char *mac, const char *comp)
+{
+ int i;
+ for (i = 0; i < PROPOSAL_MAX; i++) {
+ const char *ccp = defaults[i];
+#ifdef CIPHER_NONE_AVAIL
+ if (i == PROPOSAL_ENC_ALGS_CTOS ||
+ i == PROPOSAL_ENC_ALGS_STOC)
+ ccp = "none";
+#endif
+ if (i == PROPOSAL_SERVER_HOST_KEY_ALGS)
+ ccp = keyname;
+ else if (i == PROPOSAL_KEX_ALGS && kex != NULL)
+ ccp = kex;
+ else if ((i == PROPOSAL_ENC_ALGS_CTOS ||
+ i == PROPOSAL_ENC_ALGS_STOC) && cipher != NULL)
+ ccp = cipher;
+ else if ((i == PROPOSAL_MAC_ALGS_CTOS ||
+ i == PROPOSAL_MAC_ALGS_STOC) && mac != NULL)
+ ccp = mac;
+ else if ((i == PROPOSAL_COMP_ALGS_CTOS ||
+ i == PROPOSAL_COMP_ALGS_STOC) && comp != NULL)
+ ccp = comp;
+ if ((out[i] = strdup(ccp)) == NULL)
+ return -1;
+ }
+ return 0;
+}
+
static int
do_kex_with_key(struct shared_state *st, struct test_state *ts,
- const char *kex, int keytype)
+ const char *kex, int keytype, const struct kex_overrides *ov)
{
struct ssh *client = NULL, *server = NULL;
struct sshkey *privkey = NULL, *pubkey = NULL;
struct sshbuf *state = NULL;
struct kex_params kex_params;
- const char *ccp, *proposal[PROPOSAL_MAX] = { KEX_CLIENT };
- char *myproposal[PROPOSAL_MAX] = {0}, *keyname = NULL;
+ const char *proposal[PROPOSAL_MAX] = { KEX_CLIENT };
+ char *myproposal[PROPOSAL_MAX] = {0};
+ char *keyname = NULL;
int i, r;
ts->cin = ts->sin = NULL;
@@ -202,20 +245,13 @@ do_kex_with_key(struct shared_state *st, struct test_state *ts,
} else
debug_f("%s %s noclobber", kex, keyname);
- for (i = 0; i < PROPOSAL_MAX; i++) {
- ccp = proposal[i];
-#ifdef CIPHER_NONE_AVAIL
- if (i == PROPOSAL_ENC_ALGS_CTOS || i == PROPOSAL_ENC_ALGS_STOC)
- ccp = "none";
-#endif
- if (i == PROPOSAL_SERVER_HOST_KEY_ALGS)
- ccp = keyname;
- else if (i == PROPOSAL_KEX_ALGS && kex != NULL)
- ccp = kex;
- if ((myproposal[i] = strdup(ccp)) == NULL) {
- error_f("strdup prop %d", i);
- goto fail;
- }
+ if (build_proposal_side(myproposal, proposal, kex, keyname,
+ ov ? ov->cipher : NULL,
+ ov ? ov->mac : NULL,
+ ov ? ov->comp : NULL) != 0) {
+ error_f("build proposal");
+ r = SSH_ERR_ALLOC_FAIL;
+ goto fail;
}
memcpy(kex_params.proposal, myproposal, sizeof(myproposal));
if ((r = ssh_init(&client, 0, &kex_params)) != 0) {
@@ -322,6 +358,7 @@ int main(void)
struct test_state *ts;
const int keytypes[] = { KEY_RSA, KEY_ECDSA, KEY_ED25519, -1 };
static const char * const kextypes[] = {
+ "mlkem768x25519-sha256",
"sntrup761x25519-sha512 at openssh.com",
"curve25519-sha256 at libssh.org",
"ecdh-sha2-nistp256",
@@ -345,7 +382,7 @@ int main(void)
ts = (struct test_state *)xcalloc(1, sizeof(*ts));
ts->smsgs = sshbuf_new();
ts->cmsgs = sshbuf_new();
- do_kex_with_key(st, ts, kextypes[j], keytypes[i]);
+ do_kex_with_key(st, ts, kextypes[j], keytypes[i], NULL);
xasprintf(&path, "S2C-%s-%s",
kextypes[j], sshkey_type(st->pubkeys[keytypes[i]]));
debug_f("%s", path);
@@ -393,19 +430,68 @@ int main(void)
static void
do_kex(struct shared_state *st, struct test_state *ts, const char *kex)
{
- do_kex_with_key(st, ts, kex, KEY_RSA);
- do_kex_with_key(st, ts, kex, KEY_ECDSA);
- do_kex_with_key(st, ts, kex, KEY_ED25519);
+ do_kex_with_key(st, ts, kex, KEY_RSA, NULL);
+ do_kex_with_key(st, ts, kex, KEY_ECDSA, NULL);
+ do_kex_with_key(st, ts, kex, KEY_ED25519, NULL);
+}
+
+/*
+ * Run one full KEX per variant on the cheapest kex+key combo
+ * (curve25519 + ed25519) so cipher/MAC/compression/mismatch paths get
+ * exercised once per fuzz input without multiplying total attempts.
+ */
+static void
+kex_extras(struct shared_state *st, struct test_state *ts)
+{
+ static const char * const ciphers[] = {
+ "chacha20-poly1305 at openssh.com",
+ "aes256-gcm at openssh.com",
+ "aes128-ctr",
+ "aes256-ctr",
+ NULL,
+ };
+ static const char * const macs[] = {
+ "hmac-sha2-256-etm at openssh.com",
+ "hmac-sha2-512-etm at openssh.com",
+ "umac-128-etm at openssh.com",
+ NULL,
+ };
+ static const char * const comps[] = {
+ "zlib at openssh.com",
+ "zlib",
+ NULL,
+ };
+ const char *kex = "curve25519-sha256 at libssh.org";
+ int i;
+
+ for (i = 0; ciphers[i] != NULL; i++) {
+ struct kex_overrides ov = { 0 };
+ ov.cipher = ciphers[i];
+ do_kex_with_key(st, ts, kex, KEY_ED25519, &ov);
+ }
+ for (i = 0; macs[i] != NULL; i++) {
+ struct kex_overrides ov = { 0 };
+ ov.cipher = "aes128-ctr";
+ ov.mac = macs[i];
+ do_kex_with_key(st, ts, kex, KEY_ED25519, &ov);
+ }
+ for (i = 0; comps[i] != NULL; i++) {
+ struct kex_overrides ov = { 0 };
+ ov.comp = comps[i];
+ do_kex_with_key(st, ts, kex, KEY_ED25519, &ov);
+ }
}
static void
kex_tests(struct shared_state *st, struct test_state *ts)
{
+ do_kex(st, ts, "mlkem768x25519-sha256");
do_kex(st, ts, "sntrup761x25519-sha512 at openssh.com");
do_kex(st, ts, "curve25519-sha256 at libssh.org");
do_kex(st, ts, "ecdh-sha2-nistp256");
do_kex(st, ts, "diffie-hellman-group1-sha1");
do_kex(st, ts, "diffie-hellman-group-exchange-sha1");
+ kex_extras(st, ts);
}
int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
--
To stop receiving notification emails like this one, please contact
djm at mindrot.org.
More information about the openssh-commits
mailing list