Test that close_notify state does not impair SSL_ERROR_SYSCALL. This works correctly, but part of implementing SSL_write_ex will, if not done correctly, regress this. Specifically, if the read_shutdown check in SSL_get_error were not conditioned on ret == 0, the last SSL_get_error in the test would mistakenly classify the write error as SSL_ERROR_ZERO_RETURN. Add a regression test in advance. Bug: 507 Change-Id: I8ddb4606e291977506ee81f4ed11427e5b1636d8 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/53626 Reviewed-by: Bob Beck <bbe@google.com> Commit-Queue: David Benjamin <davidben@google.com>
diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc index 55b7051..1ea0893 100644 --- a/ssl/ssl_test.cc +++ b/ssl/ssl_test.cc
@@ -8390,5 +8390,65 @@ ASSERT_EQ(Bytes(buf, ret), Bytes(kData)); } +// Test that |SSL_ERROR_SYSCALL| continues to work after a close_notify. +TEST(SSLTest, ErrorSyscallAfterCloseNotify) { + // Make a custom |BIO| where writes fail, but without pushing to the error + // queue. + bssl::UniquePtr<BIO_METHOD> method(BIO_meth_new(0, nullptr)); + ASSERT_TRUE(method); + BIO_meth_set_create(method.get(), [](BIO *b) -> int { + BIO_set_init(b, 1); + return 1; + }); + static bool write_failed = false; + BIO_meth_set_write(method.get(), [](BIO *, const char *, int) -> int { + // Fail the operation and don't add to the error queue. + write_failed = true; + return -1; + }); + bssl::UniquePtr<BIO> wbio_silent_error(BIO_new(method.get())); + ASSERT_TRUE(wbio_silent_error); + + bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method())); + bssl::UniquePtr<SSL_CTX> server_ctx = + CreateContextWithTestCertificate(TLS_method()); + ASSERT_TRUE(client_ctx); + ASSERT_TRUE(server_ctx); + bssl::UniquePtr<SSL> client, server; + EXPECT_TRUE(ConnectClientAndServer(&client, &server, client_ctx.get(), + server_ctx.get())); + + // Replace the write |BIO| with |wbio_silent_error|. + SSL_set0_wbio(client.get(), wbio_silent_error.release()); + + // Writes should fail. There is nothing in the error queue, so + // |SSL_ERROR_SYSCALL| indicates the caller needs to check out-of-band. + const uint8_t data[1] = {0}; + int ret = SSL_write(client.get(), data, sizeof(data)); + EXPECT_EQ(ret, -1); + EXPECT_EQ(SSL_get_error(client.get(), ret), SSL_ERROR_SYSCALL); + EXPECT_TRUE(write_failed); + write_failed = false; + + // Send a close_notify from the server. It should return 0 because + // close_notify was sent, but not received. Confusingly, this is a success + // output for |SSL_shutdown|'s API. + EXPECT_EQ(SSL_shutdown(server.get()), 0); + + // Read the close_notify on the client. + uint8_t buf[1]; + ret = SSL_read(client.get(), buf, sizeof(buf)); + EXPECT_EQ(ret, 0); + EXPECT_EQ(SSL_get_error(client.get(), ret), SSL_ERROR_ZERO_RETURN); + + // Although the client has seen close_notify, it should continue to report + // |SSL_ERROR_SYSCALL| when its writes fail. + ret = SSL_write(client.get(), data, sizeof(data)); + EXPECT_EQ(ret, -1); + EXPECT_EQ(SSL_get_error(client.get(), ret), SSL_ERROR_SYSCALL); + EXPECT_TRUE(write_failed); + write_failed = false; +} + } // namespace BSSL_NAMESPACE_END