From 245baf01f0cd8a18895c236ef82ee2cb5a328b85 Mon Sep 17 00:00:00 2001 From: John Safranek Date: Tue, 18 Aug 2026 08:58:52 -0700 Subject: [PATCH] Document the wolfSSH_SetChannelType contract The refusals added for names the peer cannot use changed the return contract of a public API whose block comment still promised only WS_SUCCESS. There is no dox_comments entry, so that comment is all an embedder has. - Spell out each WS_BAD_ARGUMENT case, the keep-the-stored-name rule, and that a refused call leaves the selected type alone. - api.c asserts connectChannelId across the refusals. It is the field SendChannelRequest() switches on, so moving the checks back below the assignment would otherwise pass. --- src/ssh.c | 11 +++++++++++ tests/api.c | 5 +++++ 2 files changed, 16 insertions(+) diff --git a/src/ssh.c b/src/ssh.c index 675f7c86..1ad18acc 100644 --- a/src/ssh.c +++ b/src/ssh.c @@ -1645,7 +1645,18 @@ int wolfSSH_SetExitStatus(WOLFSSH* ssh, word32 exitStatus) * name name or command in the case of subsystem and exec channel types * nameSz size of name buffer * + * Exec and subsystem carry a name string the peer requires, so one must be + * available. Passing none keeps the name an earlier call stored; with + * nothing stored the call is refused rather than sending a request the peer + * reads as malformed. Shell and terminal take no name and drop any stored + * one. A refused call changes nothing, the selected type included. + * * returns WS_SUCCESS on success + * returns WS_BAD_ARGUMENT for a NULL ssh or an unknown type, for exec on + * the server side, for a name at or above WOLFSSH_MAX_CHN_NAMESZ, for a + * nameSz with no name behind it, and for exec or subsystem with no name + * given and none stored + * returns WS_MEMORY_E if the name cannot be allocated */ int wolfSSH_SetChannelType(WOLFSSH* ssh, byte type, byte* name, word32 nameSz) { diff --git a/tests/api.c b/tests/api.c index f907713e..58f7f571 100644 --- a/tests/api.c +++ b/tests/api.c @@ -317,16 +317,20 @@ static void test_wolfSSH_SetChannelType(void) AssertIntEQ(WS_BAD_ARGUMENT, wolfSSH_SetChannelType(ssh, WOLFSSH_SESSION_SUBSYSTEM, NULL, 0)); AssertNull(ssh->channelName); + /* a refused call leaves the selected type alone, not just the name */ + AssertIntEQ(WOLFSSH_SESSION_SHELL, ssh->connectChannelId); /* likewise for a size with no name behind it */ AssertIntEQ(WS_BAD_ARGUMENT, wolfSSH_SetChannelType(ssh, WOLFSSH_SESSION_SUBSYSTEM, NULL, 4)); AssertNull(ssh->channelName); + AssertIntEQ(WOLFSSH_SESSION_SHELL, ssh->connectChannelId); /* an oversized name is reported, not silently dropped */ AssertIntEQ(WS_BAD_ARGUMENT, wolfSSH_SetChannelType(ssh, WOLFSSH_SESSION_SUBSYSTEM, (byte*)sub1, WOLFSSH_MAX_CHN_NAMESZ)); AssertNull(ssh->channelName); + AssertIntEQ(WOLFSSH_SESSION_SHELL, ssh->connectChannelId); AssertIntEQ(WS_SUCCESS, wolfSSH_SetChannelType(ssh, WOLFSSH_SESSION_SUBSYSTEM, (byte*)sub1, @@ -347,6 +351,7 @@ static void test_wolfSSH_SetChannelType(void) AssertIntEQ(WS_BAD_ARGUMENT, wolfSSH_SetChannelType(ssh, WOLFSSH_SESSION_SUBSYSTEM, (byte*)sub1, WOLFSSH_MAX_CHN_NAMESZ)); AssertIntEQ(1, ssh->channelName == prevName); + AssertIntEQ(WOLFSSH_SESSION_SUBSYSTEM, ssh->connectChannelId); AssertIntEQ((int)(sizeof(sub1) - 1), (int)ssh->channelNameSz); AssertIntEQ(0, strcmp((const char*)ssh->channelName, (const char*)sub1));