Skip to content

Commit 67c7b9c

Browse files
committed
Address items in the review
1 parent aa1b167 commit 67c7b9c

File tree

6 files changed

+40
-29
lines changed

6 files changed

+40
-29
lines changed

.github/workflows/paramiko-sftp-test.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,8 @@ jobs:
188188
num_iterations = 10
189189
timeout_seconds = 30 # Per-operation timeout to detect hangs
190190
191+
orig_hash = get_file_hash('/tmp/sftp_upload/test_upload.dat')
192+
191193
for i in range(num_iterations):
192194
signal.signal(signal.SIGALRM, timeout_handler)
193195
signal.alarm(timeout_seconds)
@@ -197,10 +199,8 @@ jobs:
197199
# Paramiko uses prefetch by default for get()
198200
sftp.get('/tmp/test_upload.dat', download_path)
199201
elapsed = time.time() - start_time
200-
signal.alarm(0) # Cancel alarm
201202
202203
# Verify integrity
203-
orig_hash = get_file_hash('/tmp/sftp_upload/test_upload.dat')
204204
down_hash = get_file_hash(download_path)
205205
if orig_hash != down_hash:
206206
print(f" Iteration {i+1}: FAILED - hash mismatch!")
@@ -213,6 +213,8 @@ jobs:
213213
print(f" Iteration {i+1}: FAILED - {e}")
214214
print(" This may indicate the WS_WANT_WRITE hang bug!")
215215
return False
216+
finally:
217+
signal.alarm(0) # Always cancel alarm
216218
217219
print(f"=== Stress test completed: {num_iterations} iterations OK ===\n")
218220

src/internal.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3462,6 +3462,12 @@ int wolfSSH_SendPacket(WOLFSSH* ssh)
34623462
}
34633463

34643464

3465+
int wolfSSH_OutputPending(WOLFSSH* ssh)
3466+
{
3467+
return (ssh != NULL && ssh->outputBuffer.length > ssh->outputBuffer.idx);
3468+
}
3469+
3470+
34653471
static int GetInputData(WOLFSSH* ssh, word32 size)
34663472
{
34673473
int in;

src/wolfsftp.c

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,13 @@ enum WS_SFTP_LSTAT_STATE_ID {
145145
STATE_LSTAT_CLEANUP
146146
};
147147

148-
/* WS_SFTP_BUFFER is defined in wolfsftp.h for use with nonblocking state.
148+
/* SFTP buffer for nonblocking state tracking.
149149
* If adding any read/writes use the wolfSSH_SFTP_buffer_read/send functions */
150+
typedef struct WS_SFTP_BUFFER {
151+
byte* data;
152+
word32 sz;
153+
word32 idx;
154+
} WS_SFTP_BUFFER;
150155

151156
typedef struct WS_SFTP_CHMOD_STATE {
152157
enum WS_SFTP_CHMOD_STATE_ID state;
@@ -508,7 +513,7 @@ static void wolfSSH_SFTP_buffer_rewind(WS_SFTP_BUFFER* buffer)
508513

509514
/* try to send the rest of the buffer (buffer.sz - buffer.idx)
510515
* increments idx with amount sent */
511-
WOLFSSH_LOCAL int wolfSSH_SFTP_buffer_send(WOLFSSH* ssh, WS_SFTP_BUFFER* buffer)
516+
static int wolfSSH_SFTP_buffer_send(WOLFSSH* ssh, WS_SFTP_BUFFER* buffer)
512517
{
513518
int ret = WS_SUCCESS;
514519
int err;
@@ -524,7 +529,7 @@ WOLFSSH_LOCAL int wolfSSH_SFTP_buffer_send(WOLFSSH* ssh, WS_SFTP_BUFFER* buffer)
524529
/* Flush any pending data in SSH output buffer first.
525530
* Handles case where previous send returned WS_WANT_WRITE
526531
* and data is still buffered at the SSH layer. */
527-
if (ssh->outputBuffer.length > ssh->outputBuffer.idx) {
532+
if (wolfSSH_OutputPending(ssh)) {
528533
ret = wolfSSH_SendPacket(ssh);
529534
if (ret == WS_WANT_WRITE) {
530535
return ret;
@@ -554,6 +559,20 @@ WOLFSSH_LOCAL int wolfSSH_SFTP_buffer_send(WOLFSSH* ssh, WS_SFTP_BUFFER* buffer)
554559
}
555560

556561

562+
#ifdef WOLFSSH_TEST_INTERNAL
563+
int wolfSSH_TestSftpBufferSend(WOLFSSH* ssh,
564+
byte* data, word32 sz, word32 idx)
565+
{
566+
WS_SFTP_BUFFER buffer;
567+
568+
buffer.data = data;
569+
buffer.sz = sz;
570+
buffer.idx = idx;
571+
return wolfSSH_SFTP_buffer_send(ssh, &buffer);
572+
}
573+
#endif
574+
575+
557576
/* returns the amount read on success */
558577
static int wolfSSH_SFTP_buffer_read(WOLFSSH* ssh, WS_SFTP_BUFFER* buffer,
559578
int readSz)
@@ -1614,8 +1633,7 @@ int wolfSSH_SFTP_read(WOLFSSH* ssh)
16141633
/* Check if SSH layer still has pending data from WS_WANT_WRITE.
16151634
* Even if SFTP buffer is fully consumed, the data may still be
16161635
* sitting in the SSH output buffer waiting to be sent. */
1617-
if (ssh->error == WS_WANT_WRITE ||
1618-
ssh->outputBuffer.length > ssh->outputBuffer.idx) {
1636+
if (wolfSSH_OutputPending(ssh)) {
16191637
ssh->error = WS_WANT_WRITE;
16201638
return WS_FATAL_ERROR;
16211639
}

tests/regress.c

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -515,7 +515,6 @@ static void TestSftpBufferSendPendingOutput(void)
515515
{
516516
WOLFSSH_CTX* ctx;
517517
WOLFSSH* ssh;
518-
WS_SFTP_BUFFER sftpBuf;
519518
byte testData[16];
520519
int ret;
521520

@@ -528,12 +527,7 @@ static void TestSftpBufferSendPendingOutput(void)
528527
ssh = wolfSSH_new(ctx);
529528
AssertNotNull(ssh);
530529

531-
/* Set up SFTP buffer with some data to send */
532530
WMEMSET(testData, 0x42, sizeof(testData));
533-
WMEMSET(&sftpBuf, 0, sizeof(sftpBuf));
534-
sftpBuf.data = testData;
535-
sftpBuf.sz = sizeof(testData);
536-
sftpBuf.idx = 0;
537531

538532
/* Simulate pending data in SSH output buffer (as if previous send
539533
* returned WS_WANT_WRITE and data was buffered).
@@ -544,21 +538,16 @@ static void TestSftpBufferSendPendingOutput(void)
544538

545539
sftpWantWriteCallCount = 0;
546540

547-
/* Call wolfSSH_SFTP_buffer_send - should return WS_WANT_WRITE because
541+
/* Call wolfSSH_TestSftpBufferSend - should return WS_WANT_WRITE because
548542
* the fix detects pending data in outputBuffer and tries to flush it,
549543
* which fails with WS_WANT_WRITE from our callback.
550544
*
551545
* Before the fix, the function would ignore the pending SSH output buffer
552546
* data and proceed to send new SFTP data, leading to a hang because the
553547
* pending data was never flushed. */
554-
ret = wolfSSH_SFTP_buffer_send(ssh, &sftpBuf);
548+
ret = wolfSSH_TestSftpBufferSend(ssh, testData, sizeof(testData), 0);
555549
AssertIntEQ(ret, WS_WANT_WRITE);
556550

557-
/* Verify the SFTP buffer was NOT consumed (idx should still be 0).
558-
* This is critical - the SFTP layer must not advance its buffer index
559-
* until the SSH output buffer is flushed. */
560-
AssertIntEQ(sftpBuf.idx, 0);
561-
562551
/* Verify the SSH output buffer still has pending data */
563552
AssertTrue(ssh->outputBuffer.length > ssh->outputBuffer.idx);
564553

wolfssh/internal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1072,6 +1072,7 @@ enum ChannelOpenFailReasons {
10721072
WOLFSSH_LOCAL int DoReceive(WOLFSSH*);
10731073
WOLFSSH_LOCAL int DoProtoId(WOLFSSH*);
10741074
WOLFSSH_LOCAL int wolfSSH_SendPacket(WOLFSSH*);
1075+
WOLFSSH_LOCAL int wolfSSH_OutputPending(WOLFSSH*);
10751076
WOLFSSH_LOCAL int SendProtoId(WOLFSSH*);
10761077
WOLFSSH_LOCAL int SendKexInit(WOLFSSH*);
10771078
WOLFSSH_LOCAL int SendKexDhInit(WOLFSSH*);

wolfssh/wolfsftp.h

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -173,13 +173,6 @@ struct WS_SFTPNAME {
173173
#define WOLFSSH_MAX_SFTP_RECV 32768
174174
#endif
175175

176-
/* SFTP buffer for nonblocking state tracking */
177-
typedef struct WS_SFTP_BUFFER {
178-
byte* data;
179-
word32 sz;
180-
word32 idx;
181-
} WS_SFTP_BUFFER;
182-
183176
/* functions for establishing a connection */
184177
WOLFSSH_API int wolfSSH_SFTP_accept(WOLFSSH* ssh);
185178
WOLFSSH_API int wolfSSH_SFTP_connect(WOLFSSH* ssh);
@@ -289,8 +282,10 @@ WOLFSSL_LOCAL int SFTP_RemoveHandleNode(WOLFSSH* ssh, byte* handle,
289282

290283
WOLFSSH_LOCAL void wolfSSH_SFTP_ShowSizes(void);
291284

292-
/* SFTP buffer send - exposed for testing */
293-
WOLFSSH_LOCAL int wolfSSH_SFTP_buffer_send(WOLFSSH* ssh, WS_SFTP_BUFFER* buffer);
285+
#ifdef WOLFSSH_TEST_INTERNAL
286+
WOLFSSH_API int wolfSSH_TestSftpBufferSend(WOLFSSH* ssh,
287+
byte* data, word32 sz, word32 idx);
288+
#endif
294289

295290
#ifdef __cplusplus
296291
}

0 commit comments

Comments
 (0)