Skip to content

Commit 9eba49a

Browse files
committed
Fix SSL_OP_IGNORE_UNEXPECTED_EOF for socket BIOs
The guard added in #3294 gated the unexpected-EOF handling on BIO_eof(rbio). Socket BIOs (BIO_s_socket, used by SSL_set_fd) do not implement BIO_CTRL_EOF, so BIO_eof() returns 0 and the condition was always false. As a result SSL_OP_IGNORE_UNEXPECTED_EOF had no effect on real TCP connections and callers such as CPython still saw SSL_ERROR_SYSCALL on an unexpected EOF. Remove the BIO_eof() term so the option works for any transport that returns 0 on a read with no close_notify. Rework the test to use a socket-mimicking read BIO whose ctrl handler returns 0 for all commands, so BIO_eof() reports 0 like a real socket. The prior test used BIO_pair, which implements BIO_CTRL_EOF and therefore passed even with the buggy guard; the new test fails with the guard present and passes without it, covering both the client and server roles.
1 parent 5df320b commit 9eba49a

2 files changed

Lines changed: 34 additions & 32 deletions

File tree

ssl/ssl_buffer.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -584,7 +584,7 @@ static int tls_read_buffer_extend_to(SSL *ssl, size_t len) {
584584
// SSL_ERROR_ZERO_RETURN.
585585
if (ret == 0 && (ssl->options & SSL_OP_IGNORE_UNEXPECTED_EOF) &&
586586
ssl->s3->read_shutdown == ssl_shutdown_none &&
587-
!SSL_in_init(ssl) && BIO_eof(ssl->rbio.get())) {
587+
!SSL_in_init(ssl)) {
588588
ssl->s3->read_shutdown = ssl_shutdown_close_notify;
589589
ssl->s3->rwstate = SSL_ERROR_ZERO_RETURN;
590590
return ret;

ssl/ssl_misc_test.cc

Lines changed: 33 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "../crypto/test/file_util.h"
77
#include "../crypto/test/test_util.h"
88
#include "internal.h"
9+
#include "openssl/ssl.h"
910
#include "ssl_common_test.h"
1011

1112
BSSL_NAMESPACE_BEGIN
@@ -336,53 +337,54 @@ TEST(SSLTest, QuietShutdown) {
336337
}
337338

338339
// Test that |SSL_OP_IGNORE_UNEXPECTED_EOF| causes an unexpected transport EOF
339-
// when the server closes without a close_notify to be reported as a clean shutdown,
340-
// |SSL_ERROR_ZERO_RETURN|, instead of |SSL_ERROR_SYSCALL|.
341-
TEST(SSLTest, IgnoreUnexpectedEOFClient) {
340+
// (the peer closing without a close_notify) to be reported as a clean shutdown,
341+
// |SSL_ERROR_ZERO_RETURN|, instead of |SSL_ERROR_SYSCALL|, for both the client
342+
// and the server.
343+
TEST(SSLTest, IgnoreUnexpectedEOF) {
342344
bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method()));
343345
bssl::UniquePtr<SSL_CTX> server_ctx =
344346
CreateContextWithTestCertificate(TLS_method());
345347
ASSERT_TRUE(client_ctx);
346348
ASSERT_TRUE(server_ctx);
347349
SSL_CTX_set_options(client_ctx.get(), SSL_OP_IGNORE_UNEXPECTED_EOF);
350+
SSL_CTX_set_options(server_ctx.get(), SSL_OP_IGNORE_UNEXPECTED_EOF);
348351
bssl::UniquePtr<SSL> client, server;
349352
ASSERT_TRUE(ConnectClientAndServer(&client, &server, client_ctx.get(),
350353
server_ctx.get()));
351354

352-
// Close the server's write side without sending a close_notify, so the client
353-
// sees an unexpected transport EOF.
354-
EXPECT_TRUE(BIO_shutdown_wr(SSL_get_wbio(server.get())));
355+
// Create a fake read BIO that mimics a socket: it returns 0 on read to
356+
// signal a transport EOF.
357+
bssl::UniquePtr<BIO_METHOD> method(BIO_meth_new(0, nullptr));
358+
ASSERT_TRUE(method);
359+
ASSERT_TRUE(BIO_meth_set_create(method.get(), [](BIO *b) -> int {
360+
BIO_set_init(b, 1);
361+
return 1;
362+
}));
363+
ASSERT_TRUE(BIO_meth_set_read(method.get(),
364+
[](BIO *, char *, int) -> int { return 0; }));
365+
ASSERT_TRUE(BIO_meth_set_ctrl(
366+
method.get(), [](BIO *, int, long, void *) -> long { return 0; }));
367+
368+
BIO *eof_bio_client = BIO_new(method.get());
369+
BIO *eof_bio_server = BIO_new(method.get());
370+
ASSERT_TRUE(eof_bio_client);
371+
ASSERT_TRUE(eof_bio_server);
355372

356-
// With the option set, the client reports the EOF as a clean shutdown rather
357-
// than |SSL_ERROR_SYSCALL|.
373+
// Dummy BIOs must mimic a socket BIO (BIO_eof == 0)
374+
EXPECT_EQ(BIO_eof(eof_bio_client), 0);
375+
EXPECT_EQ(BIO_eof(eof_bio_server), 0);
376+
377+
SSL_set0_rbio(client.get(), eof_bio_client);
378+
SSL_set0_rbio(server.get(), eof_bio_server);
379+
380+
// With the option set, an unexpected transport EOF is reported as a clean
381+
// shutdown, |SSL_ERROR_ZERO_RETURN|, rather than |SSL_ERROR_SYSCALL|.
358382
char buf[1];
359383
int ret = SSL_read(client.get(), buf, sizeof(buf));
360384
EXPECT_EQ(ret, 0);
361385
EXPECT_EQ(SSL_get_error(client.get(), ret), SSL_ERROR_ZERO_RETURN);
362-
}
363386

364-
// Test that |SSL_OP_IGNORE_UNEXPECTED_EOF| behaves symmetrically on the server:
365-
// when the client closes without a close_notify, the server's |SSL_read| reports
366-
// the unexpected transport EOF as a clean shutdown with |SSL_ERROR_ZERO_RETURN|.
367-
TEST(SSLTest, IgnoreUnexpectedEOFServer) {
368-
bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method()));
369-
bssl::UniquePtr<SSL_CTX> server_ctx =
370-
CreateContextWithTestCertificate(TLS_method());
371-
ASSERT_TRUE(client_ctx);
372-
ASSERT_TRUE(server_ctx);
373-
SSL_CTX_set_options(server_ctx.get(), SSL_OP_IGNORE_UNEXPECTED_EOF);
374-
bssl::UniquePtr<SSL> client, server;
375-
ASSERT_TRUE(ConnectClientAndServer(&client, &server, client_ctx.get(),
376-
server_ctx.get()));
377-
378-
// Close the client's write side without sending a close_notify, so the server
379-
// sees an unexpected transport EOF.
380-
EXPECT_TRUE(BIO_shutdown_wr(SSL_get_wbio(client.get())));
381-
382-
// With the option set, the server reports the EOF as a clean shutdown rather
383-
// than |SSL_ERROR_SYSCALL|.
384-
char buf[1];
385-
int ret = SSL_read(server.get(), buf, sizeof(buf));
387+
ret = SSL_read(server.get(), buf, sizeof(buf));
386388
EXPECT_EQ(ret, 0);
387389
EXPECT_EQ(SSL_get_error(server.get(), ret), SSL_ERROR_ZERO_RETURN);
388390
}

0 commit comments

Comments
 (0)