Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion src/internal.c
Original file line number Diff line number Diff line change
Expand Up @@ -5910,7 +5910,14 @@ static int GetInputData(WOLFSSH* ssh, word32 size)
return WS_FATAL_ERROR;
}

if (in >= 0) {
/* A recv-style 0 is the peer closing the connection. */
if (in == 0) {
ssh->isClosed = 1;
ssh->error = WS_SOCKET_ERROR_E;
return WS_FATAL_ERROR;
}

if (in > 0) {
ssh->inputBuffer.length += in;
size -= in;
}
Expand Down
4 changes: 4 additions & 0 deletions src/io.c
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,10 @@ int wsEmbedSend(WOLFSSH* ssh, void* data, word32 sz, void* ctx)
WLOG(WS_LOG_DEBUG," Socket EPIPE");
return WS_CBIO_ERR_CONN_CLOSE;
}
else if (err == SOCKET_ECONNABORTED) {
WLOG(WS_LOG_DEBUG," Connection aborted");
return WS_CBIO_ERR_CONN_CLOSE;
}
else {
WLOG(WS_LOG_DEBUG," General error %d", err);
return WS_CBIO_ERR_GENERAL;
Expand Down
150 changes: 150 additions & 0 deletions tests/unit.c
Original file line number Diff line number Diff line change
Expand Up @@ -2713,6 +2713,142 @@ static int test_DoReceive_CbcRejectDiscards(void)
#endif /* WOLFSSH_TEST_INTERNAL && !WOLFSSH_NO_AES_CBC && HMAC-SHA2-256 */


#ifdef WOLFSSH_TEST_INTERNAL
/* Gives up after this many calls, so an unfixed reader that keeps
* retrying a 0 return still terminates. */
#define RECV_ZERO_CALL_CAP 100

static int RecvZeroCalls;

static int RecvAlwaysZero(WOLFSSH* ssh, void* data, word32 sz, void* ctx)
{
WOLFSSH_UNUSED(ssh);
WOLFSSH_UNUSED(data);
WOLFSSH_UNUSED(sz);
WOLFSSH_UNUSED(ctx);

if (++RecvZeroCalls > RECV_ZERO_CALL_CAP)
return WS_CBIO_ERR_GENERAL;
return 0;
}

/* Verify DoReceive treats a 0 from the recv callback as the peer closing
* the connection: it fails after one call and marks the session closed,
* instead of calling the callback again. */
static int test_DoReceive_RecvZeroIsClose(void)
{
WOLFSSH_CTX* ctx = NULL;
WOLFSSH* ssh = NULL;
int ret;
int result = 0;

ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_CLIENT, NULL);
if (ctx == NULL)
return -794;
wolfSSH_SetIORecv(ctx, RecvAlwaysZero);
ssh = wolfSSH_new(ctx);
if (ssh == NULL) {
wolfSSH_CTX_free(ctx);
return -795;
}

RecvZeroCalls = 0;
ret = wolfSSH_TestDoReceive(ssh);
if (ret != WS_FATAL_ERROR || ssh->error != WS_SOCKET_ERROR_E) {
result = -796;
goto done;
}
if (RecvZeroCalls != 1) {
result = -797;
goto done;
}
if (!ssh->isClosed) {
result = -798;
goto done;
}

done:
wolfSSH_free(ssh);
wolfSSH_CTX_free(ctx);
return result;
}
#endif /* WOLFSSH_TEST_INTERNAL */


#if defined(WOLFSSH_TEST_INTERNAL) && !defined(WOLFSSH_USER_IO) && \
!defined(WOLFSSH_TEST_BLOCK) && !defined(WOLFSSH_LWIP) && \
(defined(__linux__) || defined(__APPLE__))
Comment thread
ejohnstown marked this conversation as resolved.
Resolved
#include <errno.h>
#include <sys/socket.h>

/* No socket can be made to fail a send() with ECONNABORTED on demand, so
* this test replaces send() for the whole program. The library is linked in
* statically, so wsEmbedSend() binds to this definition. A send() on the
* marked descriptor fails with the chosen errno; any other passes through
* to sendto(), which is send() on a connected socket. */
#define SEND_ERRNO_FD 0x5e4d
static int sendErrnoInject = 0;
static int sendErrnoCalls = 0;

ssize_t send(int sd, const void* buf, size_t len, int flags)
{
if (sd == SEND_ERRNO_FD && sendErrnoInject != 0) {
sendErrnoCalls++;
errno = sendErrnoInject;
return -1;
}
return sendto(sd, buf, len, flags, NULL, 0);
}

/* Verify wsEmbedSend maps ECONNABORTED to a closed connection, and that
* the packet send path then marks the session closed. */
static int test_EmbedSend_ConnAbortedIsClose(void)
{
WOLFSSH_CTX* ctx = NULL;
WOLFSSH* ssh = NULL;
byte data[1] = { 0 };
int ret;
int result = 0;

ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_CLIENT, NULL);
if (ctx == NULL)
return -799;
ssh = wolfSSH_new(ctx);
if (ssh == NULL) {
wolfSSH_CTX_free(ctx);
return -800;
}
wolfSSH_set_fd(ssh, SEND_ERRNO_FD);
sendErrnoInject = ECONNABORTED;
sendErrnoCalls = 0;

ret = wsEmbedSend(ssh, data, (word32)sizeof(data), ssh->ioWriteCtx);
if (sendErrnoCalls != 1 || ret != WS_CBIO_ERR_CONN_CLOSE) {
Comment thread
ejohnstown marked this conversation as resolved.
Resolved
result = -801;
goto done;
}

/* Not keyed yet, so strict KEX would refuse the IGNORE outright. */
ssh->sendStrictKex = 0;
ret = wolfSSH_SendIgnore(ssh, NULL, 0);
if (ret != WS_SOCKET_ERROR_E || ssh->error != WS_SOCKET_ERROR_E) {
result = -802;
goto done;
}
if (!ssh->isClosed) {
result = -803;
goto done;
}

done:
sendErrnoInject = 0;
wolfSSH_free(ssh);
wolfSSH_CTX_free(ctx);
return result;
}
#endif /* WOLFSSH_TEST_INTERNAL && !WOLFSSH_USER_IO && (linux || apple) */


#if defined(WOLFSSH_TEST_INTERNAL) && !defined(WOLFSSH_NO_AES_GCM)
/* Verify DoReceive rejects an AES-GCM record whose length field is not a
* multiple of the cipher block size. RFC 5647 aligns the body alone, so a
Expand Down Expand Up @@ -25979,6 +26115,20 @@ int wolfSSH_UnitTest(int argc, char** argv)
printf("DoReceiveRejectsMisalignedPacket: %s\n",
(unitResult == 0 ? "SUCCESS" : "FAILED"));
testResult = testResult || unitResult;

unitResult = test_DoReceive_RecvZeroIsClose();
printf("DoReceiveRecvZeroIsClose: %s\n",
(unitResult == 0 ? "SUCCESS" : "FAILED"));
testResult = testResult || unitResult;
#endif

#if defined(WOLFSSH_TEST_INTERNAL) && !defined(WOLFSSH_USER_IO) && \
!defined(WOLFSSH_TEST_BLOCK) && !defined(WOLFSSH_LWIP) && \
(defined(__linux__) || defined(__APPLE__))
unitResult = test_EmbedSend_ConnAbortedIsClose();
printf("EmbedSendConnAbortedIsClose: %s\n",
(unitResult == 0 ? "SUCCESS" : "FAILED"));
testResult = testResult || unitResult;
#endif

#if defined(WOLFSSH_TEST_INTERNAL) && !defined(WOLFSSH_NO_AES_CTR)
Expand Down
Loading