Kitura / Kitura/BlueSSLService

Socket.read(...) unexpectedly returns 0

Open
#79 3 comments 0 reactions 2 assignees Claimed by @dannys42 View on GitHub
Dominant language
Swift
Stars
101
Forks
50
PR merge metrics
No merged PRs in 30d

Description

On a "blocking" client socket, `Socket.read(into data: inout Data)` sometimes unexpectedly returns 0, even when the remote connection has not closed (`Socket.remoteConnectionClosed == false`).

This only reproduces on macOS.

This only happens using SSL/TLS (BlueSSLService).

**Environment**

- Client on macOS (10.14.5), using Swift 5.0.1 (Xcode 10.2.1), BlueSocket 1.0.47, BlueSSLService 1.0.47. (The client software is a Swift database driver for the PostgreSQL database.)

- Server is a PostgreSQL database server (PostgreSQL 10.9) running on Ubuntu 18.04 LTS.

The issue reproduces in multiple client and server environments. The issue reproduces more consistently on "distant" servers (e.g. running on AWS EC2 or Linode) than on "nearby" servers (e.g. on the same LAN as the client).

(I tried to reproduce the problem with the client running against a simple standalone Swift server, but was not able to reproduce the problem that way. So I'm not able to provide code for a standalone reproducible test case.)

**Analysis**

The Xcode debugger was used to analyze why `Socket.read(...)` is returning 0.

In SSLService.swift, the function `sslReadCallback(...)` is the callback function for `SSLRead`. In line 1406, `sslReadCallback(...)` invokes the `read(...)` system call, which sometimes returns fewer bytes than requested.

```
(lldb) po bytesRequested
8216

(lldb) po bytesRead
4339
```

When this occurs, `sslReadCallback(...)` returns `errSSLWouldBlock`. When control returns to the `SSLRead` call site (SSLService.swift, line 687), we see:

```
(lldb) po status
-9803

(lldb) po errSSLWouldBlock
-9803

(lldb) po processed
0

```

(I suspect `processed == 0` because `SSLRead` requires its callback to return all requested bytes before `SSLRead` decrypts the received message.)

`SSLService.recv(...)` then returns -1 with `errno` set to `EWOULDBLOCK` (line 695).

`Socket.readDataIntoStore()` detects this error and returns 0 (Socket.swift, line 3589).

`Socket.read(...)` then returns 0 (line 2772).

**Potential fix**

In the `sslReadCallback(...)` function, I changed the "read" system call to request `MSG_WAITALL`.

Line 1406 changed from:

```
let bytesRead = read(socketfd, data, bytesRequested)
```
to:
```
let bytesRead = recv(socketfd, data, bytesRequested, MSG_WAITALL)
```

`MSG_WAITALL` forces the socket read to block until the requested number of bytes are available (or an error occurs).

This one-line change appears to resolve this issue. However, I don't have sufficient familiarity with BlueSocket/BlueSSLService internals (or low level network programming, for that matter) to assess whether it would cause other problems.

Please let me know if there is other information I can provide.

[edited to correct typo]

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.