From 114ae6d4a297538895253a0ae37bd0411187c19f Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Wed, 15 Apr 2026 15:43:27 +0300 Subject: [PATCH] io: fix using io.read_all on a SSLConn giving a strange error (fixes #16557) --- vlib/io/reader.v | 6 ++++ vlib/io/reader_test.v | 30 ++++++++++++++++ vlib/net/ssl/ssl_read_all_test.v | 59 ++++++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+) create mode 100644 vlib/net/ssl/ssl_read_all_test.v diff --git a/vlib/io/reader.v b/vlib/io/reader.v index bc745d0d8..10b11b065 100644 --- a/vlib/io/reader.v +++ b/vlib/io/reader.v @@ -50,6 +50,9 @@ pub fn read_all(config ReadAllConfig) ![]u8 { mut read := 0 for { new_read := r.read(mut b[read..]) or { break } + if new_read < 0 { + return error('io.read_all: reader returned a negative read count (${new_read})') + } read += new_read if !read_till_eof && new_read == 0 { break @@ -68,6 +71,9 @@ pub fn read_any(mut r Reader) ![]u8 { mut read := 0 for { new_read := r.read(mut b[read..]) or { return error('none') } + if new_read < 0 { + return error('io.read_any: reader returned a negative read count (${new_read})') + } read += new_read if new_read == 0 { break diff --git a/vlib/io/reader_test.v b/vlib/io/reader_test.v index 5639bc13d..d3b30c59f 100644 --- a/vlib/io/reader_test.v +++ b/vlib/io/reader_test.v @@ -229,6 +229,36 @@ fn test_buffered_reader_retries_zero_length_reads() { assert r.total_read == total } +struct NegativeReader { +mut: + read_count int +} + +fn (mut r NegativeReader) read(mut _ []u8) !int { + r.read_count++ + return -1 +} + +fn test_read_all_errors_on_negative_read_count() { + mut reader := &NegativeReader{} + if _ := read_all(reader: reader) { + assert false + } else { + assert err.msg() == 'io.read_all: reader returned a negative read count (-1)' + } + assert reader.read_count == 1 +} + +fn test_read_any_errors_on_negative_read_count() { + mut reader := &NegativeReader{} + if _ := read_any(mut reader) { + assert false + } else { + assert err.msg() == 'io.read_any: reader returned a negative read count (-1)' + } + assert reader.read_count == 1 +} + fn test_totalread_readline() { text := 'Some testing text\nmore_enters' mut s := StringReaderTest{ diff --git a/vlib/net/ssl/ssl_read_all_test.v b/vlib/net/ssl/ssl_read_all_test.v new file mode 100644 index 000000000..c5de16bf3 --- /dev/null +++ b/vlib/net/ssl/ssl_read_all_test.v @@ -0,0 +1,59 @@ +module ssl + +import io +import net +import net.mbedtls + +const issue_16557_server_cert = @VMODROOT + '/examples/ssl_server/cert/server.crt' +const issue_16557_server_key = @VMODROOT + '/examples/ssl_server/cert/server.key' +const issue_16557_request = 'ping\r\n' +const issue_16557_response_chunk = 'hello over tls' +const issue_16557_response_repeat = 2_000 + +fn issue_16557_serve_once(mut listener mbedtls.SSLListener) { + defer { + listener.shutdown() or {} + } + mut conn := listener.accept() or { panic(err) } + defer { + conn.shutdown() or {} + } + mut request_buf := []u8{len: issue_16557_request.len} + _ = conn.read(mut request_buf) or { panic(err) } + response := issue_16557_expected_response() + mut start := 0 + for start < response.len { + end := if start + 257 > response.len { response.len } else { start + 257 } + conn.write_string(response[start..end]) or { panic(err) } + start = end + } +} + +fn issue_16557_expected_response() string { + return issue_16557_response_chunk.repeat(issue_16557_response_repeat) +} + +fn test_io_read_all_reads_ssl_conn_until_eof() { + mut port_listener := net.listen_tcp(.ip, '127.0.0.1:0') or { panic(err) } + port := port_listener.addr() or { panic(err) }.port() or { panic(err) } + port_listener.close() or { panic(err) } + + mut listener := mbedtls.new_ssl_listener('127.0.0.1:${port}', mbedtls.SSLConnectConfig{ + cert: issue_16557_server_cert + cert_key: issue_16557_server_key + validate: false + }) or { panic(err) } + server := spawn issue_16557_serve_once(mut listener) + + mut client := new_ssl_conn(validate: false) or { panic(err) } + defer { + client.shutdown() or {} + } + client.dial('127.0.0.1', port) or { panic(err) } + client.write_string(issue_16557_request) or { panic(err) } + + bytes := io.read_all(reader: client) or { panic(err) } + server.wait() + + assert bytes == issue_16557_expected_response().bytes() +} -- 2.39.5