LCOV - code coverage report
Current view: top level - tls - s2n_recv.c (source / functions) Hit Total Coverage
Test: unit_test_coverage.info Lines: 182 185 98.4 %
Date: 2026-08-22 07:27:53 Functions: 8 8 100.0 %
Branches: 147 202 72.8 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
       3                 :            :  *
       4                 :            :  * Licensed under the Apache License, Version 2.0 (the "License").
       5                 :            :  * You may not use this file except in compliance with the License.
       6                 :            :  * A copy of the License is located at
       7                 :            :  *
       8                 :            :  *  http://aws.amazon.com/apache2.0
       9                 :            :  *
      10                 :            :  * or in the "license" file accompanying this file. This file is distributed
      11                 :            :  * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
      12                 :            :  * express or implied. See the License for the specific language governing
      13                 :            :  * permissions and limitations under the License.
      14                 :            :  */
      15                 :            : 
      16                 :            : /* Use usleep */
      17                 :            : #define _XOPEN_SOURCE 500
      18                 :            : #include <errno.h>
      19                 :            : #include <unistd.h>
      20                 :            : 
      21                 :            : #include "api/s2n.h"
      22                 :            : #include "error/s2n_errno.h"
      23                 :            : #include "stuffer/s2n_stuffer.h"
      24                 :            : #include "tls/s2n_alerts.h"
      25                 :            : #include "tls/s2n_connection.h"
      26                 :            : #include "tls/s2n_handshake.h"
      27                 :            : #include "tls/s2n_ktls.h"
      28                 :            : #include "tls/s2n_post_handshake.h"
      29                 :            : #include "tls/s2n_record.h"
      30                 :            : #include "tls/s2n_resume.h"
      31                 :            : #include "tls/s2n_tls.h"
      32                 :            : #include "utils/s2n_blob.h"
      33                 :            : #include "utils/s2n_io.h"
      34                 :            : #include "utils/s2n_safety.h"
      35                 :            : 
      36                 :            : S2N_RESULT s2n_recv_in_init(struct s2n_connection *conn, uint32_t written, uint32_t total)
      37                 :     360550 : {
      38 [ #  # ][ -  + ]:     360550 :     RESULT_ENSURE_REF(conn);
      39                 :            : 
      40                 :            :     /* If we're going to initialize conn->in to point to more memory than
      41                 :            :      * is actually readable, make sure that the additional memory exists.
      42                 :            :      */
      43 [ #  # ][ -  + ]:     360550 :     RESULT_ENSURE_LTE(written, total);
      44                 :     360550 :     uint32_t remaining = total - written;
      45 [ -  + ][ #  # ]:     360550 :     RESULT_ENSURE_LTE(remaining, s2n_stuffer_space_remaining(&conn->buffer_in));
      46                 :            : 
      47                 :     360550 :     uint8_t *data = s2n_stuffer_raw_read(&conn->buffer_in, written);
      48 [ -  + ][ #  # ]:     360550 :     RESULT_ENSURE_REF(data);
      49         [ -  + ]:     360550 :     RESULT_GUARD_POSIX(s2n_stuffer_free(&conn->in));
      50         [ -  + ]:     360550 :     RESULT_GUARD_POSIX(s2n_blob_init(&conn->in.blob, data, total));
      51         [ -  + ]:     360550 :     RESULT_GUARD_POSIX(s2n_stuffer_skip_write(&conn->in, written));
      52                 :     360550 :     return S2N_RESULT_OK;
      53                 :     360550 : }
      54                 :            : 
      55                 :            : S2N_RESULT s2n_read_in_bytes(struct s2n_connection *conn, struct s2n_stuffer *output, uint32_t length)
      56                 :     794918 : {
      57         [ +  + ]:    1382770 :     while (s2n_stuffer_data_available(output) < length) {
      58                 :     893713 :         uint32_t remaining = length - s2n_stuffer_data_available(output);
      59         [ +  + ]:     893713 :         if (conn->recv_buffering) {
      60         [ +  + ]:        606 :             remaining = S2N_MAX(remaining, s2n_stuffer_space_remaining(output));
      61                 :        606 :         }
      62                 :     893713 :         errno = 0;
      63                 :     893713 :         int r = s2n_connection_recv_stuffer(output, conn, remaining);
      64         [ +  + ]:     893713 :         if (r == 0) {
      65                 :          6 :             s2n_atomic_flag_set(&conn->read_closed);
      66                 :          6 :         }
      67         [ +  + ]:     893713 :         RESULT_GUARD(s2n_io_check_read_result(r));
      68                 :     587852 :         conn->wire_bytes_in += r;
      69                 :     587852 :     }
      70                 :            : 
      71                 :     489057 :     return S2N_RESULT_OK;
      72                 :     794918 : }
      73                 :            : 
      74                 :            : static S2N_RESULT s2n_recv_buffer_in(struct s2n_connection *conn, size_t min_size)
      75                 :     795668 : {
      76         [ -  + ]:     795668 :     RESULT_GUARD_POSIX(s2n_stuffer_resize_if_empty(&conn->buffer_in, S2N_LARGE_FRAGMENT_LENGTH));
      77                 :     795668 :     uint32_t buffer_in_available = s2n_stuffer_data_available(&conn->buffer_in);
      78         [ +  + ]:     795668 :     if (buffer_in_available < min_size) {
      79                 :     794453 :         uint32_t remaining = min_size - buffer_in_available;
      80         [ +  + ]:     794453 :         if (s2n_stuffer_space_remaining(&conn->buffer_in) < remaining) {
      81         [ -  + ]:        566 :             RESULT_GUARD_POSIX(s2n_stuffer_shift(&conn->buffer_in));
      82                 :        566 :         }
      83         [ +  + ]:     794453 :         RESULT_GUARD(s2n_read_in_bytes(conn, &conn->buffer_in, min_size));
      84                 :     794453 :     }
      85                 :     489864 :     return S2N_RESULT_OK;
      86                 :     795668 : }
      87                 :            : 
      88                 :            : /**
      89                 :            :  * An S2N_SUCCESS return from this method means that a full record is available
      90                 :            :  * in plaintext in conn->in.
      91                 :            :  */
      92                 :            : int s2n_read_full_record(struct s2n_connection *conn, uint8_t *record_type, int *isSSLv2)
      93                 :     589175 : {
      94                 :     589175 :     *isSSLv2 = 0;
      95                 :            : 
      96         [ +  + ]:     589175 :     if (conn->ktls_recv_enabled) {
      97                 :         21 :         return s2n_ktls_read_full_record(conn, record_type);
      98                 :         21 :     }
      99                 :            : 
     100                 :            :     /* If the record has already been decrypted, then leave it alone */
     101         [ +  + ]:     589154 :     if (conn->in_status == PLAINTEXT) {
     102                 :            :         /* Only application data packets count as plaintext */
     103                 :      35237 :         *record_type = TLS_APPLICATION_DATA;
     104                 :      35237 :         return S2N_SUCCESS;
     105                 :      35237 :     }
     106                 :            : 
     107                 :            :     /* Read the record until we at least have a header */
     108         [ -  + ]:     553917 :     POSIX_GUARD(s2n_stuffer_reread(&conn->header_in));
     109                 :     553917 :     uint32_t header_available = s2n_stuffer_data_available(&conn->header_in);
     110         [ +  + ]:     553917 :     if (header_available < S2N_TLS_RECORD_HEADER_LENGTH) {
     111                 :     435427 :         uint32_t header_remaining = S2N_TLS_RECORD_HEADER_LENGTH - header_available;
     112                 :     435427 :         s2n_result ret = s2n_recv_buffer_in(conn, header_remaining);
     113         [ +  + ]:     435427 :         uint32_t header_read = S2N_MIN(header_remaining, s2n_stuffer_data_available(&conn->buffer_in));
     114         [ -  + ]:     435427 :         POSIX_GUARD(s2n_stuffer_copy(&conn->buffer_in, &conn->header_in, header_read));
     115         [ +  + ]:     435427 :         POSIX_GUARD_RESULT(ret);
     116                 :     435427 :     }
     117                 :            : 
     118                 :     363548 :     uint16_t fragment_length = 0;
     119                 :            : 
     120                 :            :     /* If the first bit is set then this is an SSLv2 record */
     121         [ +  + ]:     363548 :     if (conn->header_in.blob.data[0] & S2N_TLS_SSLV2_HEADER_FLAG) {
     122                 :        435 :         *isSSLv2 = 1;
     123 [ -  + ][ +  - ]:        435 :         WITH_ERROR_BLINDING(conn, POSIX_GUARD(s2n_sslv2_record_header_parse(conn, record_type, &conn->client_hello.legacy_version, &fragment_length)));
     124                 :     363113 :     } else {
     125                 :     363113 :         struct s2n_record_header header = { 0 };
     126 [ +  + ][ +  - ]:     363113 :         WITH_ERROR_BLINDING(conn, POSIX_GUARD(s2n_record_header_parse(conn, &header)));
     127                 :     362987 :         *record_type = header.content_type;
     128                 :     362987 :         fragment_length = header.length;
     129                 :     362987 :     }
     130                 :            : 
     131                 :            :     /* Read enough to have the whole record */
     132                 :     363422 :     uint32_t fragment_available = s2n_stuffer_data_available(&conn->in);
     133 [ +  + ][ +  + ]:     363422 :     if (fragment_available < fragment_length || fragment_length == 0) {
     134         [ -  + ]:     360241 :         POSIX_GUARD(s2n_stuffer_rewind_read(&conn->buffer_in, fragment_available));
     135                 :     360241 :         s2n_result ret = s2n_recv_buffer_in(conn, fragment_length);
     136         [ +  + ]:     360241 :         uint32_t fragment_read = S2N_MIN(fragment_length, s2n_stuffer_data_available(&conn->buffer_in));
     137         [ -  + ]:     360241 :         POSIX_GUARD_RESULT(s2n_recv_in_init(conn, fragment_read, fragment_length));
     138         [ +  + ]:     360241 :         POSIX_GUARD_RESULT(ret);
     139                 :     360241 :     }
     140                 :            : 
     141         [ +  + ]:     247987 :     if (*isSSLv2) {
     142                 :          5 :         return 0;
     143                 :          5 :     }
     144                 :            : 
     145                 :            :     /* Decrypt and parse the record */
     146         [ +  + ]:     247982 :     if (s2n_early_data_is_trial_decryption_allowed(conn, *record_type)) {
     147 [ +  + ][ +  - ]:        987 :         POSIX_ENSURE(s2n_record_parse(conn) >= S2N_SUCCESS, S2N_ERR_EARLY_DATA_TRIAL_DECRYPT);
     148                 :     246995 :     } else {
     149 [ +  + ][ +  - ]:     246995 :         WITH_ERROR_BLINDING(conn, POSIX_GUARD(s2n_record_parse(conn)));
     150                 :     246995 :     }
     151                 :            : 
     152                 :            :     /* In TLS 1.3, encrypted handshake records would appear to be of record type
     153                 :            :     * TLS_APPLICATION_DATA. The actual record content type is found after the encrypted
     154                 :            :     * is decrypted.
     155                 :            :     */
     156 [ +  + ][ +  + ]:     246560 :     if (conn->actual_protocol_version == S2N_TLS13 && *record_type == TLS_APPLICATION_DATA) {
     157         [ -  + ]:      69988 :         POSIX_GUARD(s2n_tls13_parse_record_type(&conn->in, record_type));
     158                 :      69988 :     }
     159                 :            : 
     160                 :     246560 :     return 0;
     161                 :     246560 : }
     162                 :            : 
     163                 :            : ssize_t s2n_recv_impl(struct s2n_connection *conn, void *buf, ssize_t size_signed, s2n_blocked_status *blocked)
     164                 :     371553 : {
     165 [ -  + ][ #  # ]:     371553 :     POSIX_ENSURE_GTE(size_signed, 0);
     166                 :     371553 :     size_t size = size_signed;
     167                 :     371553 :     ssize_t bytes_read = 0;
     168                 :     371553 :     struct s2n_blob out = { 0 };
     169         [ -  + ]:     371553 :     POSIX_GUARD(s2n_blob_init(&out, (uint8_t *) buf, 0));
     170                 :            : 
     171                 :            :     /*
     172                 :            :      * Set the `blocked` status to BLOCKED_ON_READ by default
     173                 :            :      *
     174                 :            :      * The only case in which it should be updated is on a successful read into the provided buffer.
     175                 :            :      *
     176                 :            :      * Unfortunately, the current `blocked` behavior has become ossified by buggy applications that ignore
     177                 :            :      * error types and only read `blocked`. As such, it's very important to avoid changing how this value is updated
     178                 :            :      * as it could break applications.
     179                 :            :      */
     180                 :     371553 :     *blocked = S2N_BLOCKED_ON_READ;
     181                 :            : 
     182         [ +  + ]:     371553 :     if (!s2n_connection_check_io_status(conn, S2N_IO_READABLE)) {
     183                 :            :         /*
     184                 :            :          *= https://www.rfc-editor.org/rfc/rfc8446#6.1
     185                 :            :          *# If a transport-level close
     186                 :            :          *# is received prior to a "close_notify", the receiver cannot know that
     187                 :            :          *# all the data that was sent has been received.
     188                 :            :          *
     189                 :            :          *= https://www.rfc-editor.org/rfc/rfc8446#6.1
     190                 :            :          *# If the application protocol using TLS provides that any data may be
     191                 :            :          *# carried over the underlying transport after the TLS connection is
     192                 :            :          *# closed, the TLS implementation MUST receive a "close_notify" alert
     193                 :            :          *# before indicating end-of-data to the application layer.
     194                 :            :          */
     195 [ +  + ][ +  - ]:       1683 :         POSIX_ENSURE(s2n_atomic_flag_test(&conn->close_notify_received), S2N_ERR_CLOSED);
     196                 :         29 :         *blocked = S2N_NOT_BLOCKED;
     197                 :         29 :         return 0;
     198                 :       1683 :     }
     199                 :            : 
     200 [ +  - ][ +  + ]:     369870 :     POSIX_ENSURE(!s2n_connection_is_quic_enabled(conn), S2N_ERR_UNSUPPORTED_WITH_QUIC);
     201         [ +  + ]:     369869 :     POSIX_GUARD_RESULT(s2n_early_data_validate_recv(conn));
     202                 :            : 
     203 [ +  + ][ +  + ]:     397185 :     while (size && s2n_connection_check_io_status(conn, S2N_IO_READABLE)) {
     204                 :     389395 :         int isSSLv2 = 0;
     205                 :     389395 :         uint8_t record_type = 0;
     206                 :     389395 :         int r = s2n_read_full_record(conn, &record_type, &isSSLv2);
     207         [ +  + ]:     389395 :         if (r < 0) {
     208                 :            :             /* Don't propagate the error if we already read some bytes. */
     209 [ +  + ][ +  + ]:     181280 :             if (bytes_read && (s2n_errno == S2N_ERR_CLOSED || s2n_errno == S2N_ERR_IO_BLOCKED)) {
                 [ +  - ]
     210                 :        285 :                 break;
     211                 :        285 :             }
     212                 :            : 
     213                 :            :             /* If we get here, it's an error condition.
     214                 :            :              * For stateful resumption, invalidate the session on error to prevent resumption with
     215                 :            :              * potentially corrupted session state. This ensures that a bad session state does not
     216                 :            :              * lead to repeated failures during resumption attempts.
     217                 :            :              */
     218 [ +  + ][ -  + ]:     180995 :             if (s2n_errno != S2N_ERR_IO_BLOCKED && s2n_allowed_to_cache_connection(conn) && conn->session_id_len) {
                 [ #  # ]
     219                 :          0 :                 conn->config->cache_delete(conn, conn->config->cache_delete_data, conn->session_id, conn->session_id_len);
     220                 :          0 :             }
     221                 :            : 
     222                 :     180995 :             S2N_ERROR_PRESERVE_ERRNO();
     223                 :     180995 :         }
     224                 :            : 
     225 [ +  + ][ +  - ]:     208115 :         S2N_ERROR_IF(isSSLv2, S2N_ERR_BAD_MESSAGE);
     226                 :            : 
     227         [ +  + ]:     208113 :         if (record_type != TLS_HANDSHAKE) {
     228                 :            :             /*
     229                 :            :              *= https://www.rfc-editor.org/rfc/rfc8446#section-5.1
     230                 :            :              *#    -  Handshake messages MUST NOT be interleaved with other record
     231                 :            :              *#       types.  That is, if a handshake message is split over two or more
     232                 :            :              *#       records, there MUST NOT be any other records between them.
     233                 :            :              */
     234 [ +  + ][ +  - ]:     183425 :             POSIX_ENSURE(s2n_stuffer_is_wiped(&conn->post_handshake.in), S2N_ERR_BAD_MESSAGE);
     235                 :            : 
     236                 :            :             /* If not handling a handshake message, free the post-handshake memory.
     237                 :            :              * Post-handshake messages are infrequent enough that we don't want to
     238                 :            :              * keep a potentially large buffer around unnecessarily.
     239                 :            :              */
     240         [ +  + ]:     183423 :             if (!s2n_stuffer_is_freed(&conn->post_handshake.in)) {
     241         [ -  + ]:       1012 :                 POSIX_GUARD(s2n_stuffer_free(&conn->post_handshake.in));
     242                 :       1012 :             }
     243                 :     183423 :         }
     244                 :            : 
     245         [ +  + ]:     208111 :         if (record_type != TLS_APPLICATION_DATA) {
     246                 :      26209 :             switch (record_type) {
     247         [ +  + ]:          1 :                 case TLS_CHANGE_CIPHER_SPEC:
     248                 :            :                     /* CCS records are discarded. In TLS 1.3, CCS is a no-op
     249                 :            :                      * for middlebox compatibility (RFC 8446 Section 5) and is
     250                 :            :                      * only expected during the handshake. Post-handshake CCS
     251                 :            :                      * is now rejected by s2n_record_parse, so this case is
     252                 :            :                      * only reachable during the handshake or for TLS 1.2. */
     253                 :          1 :                     break;
     254         [ +  + ]:       1520 :                 case TLS_ALERT:
     255         [ +  + ]:       1520 :                     POSIX_GUARD(s2n_process_alert_fragment(conn));
     256                 :         13 :                     break;
     257         [ +  + ]:      24688 :                 case TLS_HANDSHAKE: {
     258                 :      24688 :                     s2n_result result = s2n_post_handshake_recv(conn);
     259                 :            :                     /* Ignore any errors due to insufficient input data from io.
     260                 :            :                      * The next iteration of this loop will attempt to read more input data.
     261                 :            :                      */
     262 [ +  + ][ +  + ]:      24688 :                     if (s2n_result_is_error(result) && s2n_errno != S2N_ERR_IO_BLOCKED) {
     263 [ +  - ][ #  # ]:         41 :                         WITH_ERROR_BLINDING(conn, POSIX_GUARD_RESULT(result));
     264                 :         41 :                     }
     265                 :      24647 :                     break;
     266                 :      24688 :                 }
     267         [ -  + ]:      24647 :                 default:
     268         [ #  # ]:          0 :                     POSIX_BAIL(S2N_ERR_BAD_MESSAGE);
     269                 :      26209 :             }
     270         [ -  + ]:      24661 :             POSIX_GUARD_RESULT(s2n_record_wipe(conn));
     271                 :      24661 :             continue;
     272                 :      24661 :         }
     273                 :            : 
     274         [ +  + ]:     181902 :         out.size = S2N_MIN(size, s2n_stuffer_data_available(&conn->in));
     275                 :            : 
     276         [ -  + ]:     181902 :         POSIX_GUARD(s2n_stuffer_erase_and_read(&conn->in, &out));
     277                 :     181902 :         bytes_read += out.size;
     278                 :            : 
     279                 :     181902 :         out.data += out.size;
     280                 :     181902 :         size -= out.size;
     281                 :            : 
     282                 :            :         /* Are we ready for more encrypted data? */
     283         [ +  + ]:     181902 :         if (s2n_stuffer_data_available(&conn->in) == 0) {
     284         [ -  + ]:     146742 :             POSIX_GUARD_RESULT(s2n_record_wipe(conn));
     285                 :     146742 :         }
     286                 :            : 
     287                 :            :         /* If we've read some data, return it in legacy mode */
     288 [ +  - ][ +  + ]:     181902 :         if (bytes_read && !conn->config->recv_multi_record) {
     289                 :     179243 :             break;
     290                 :     179243 :         }
     291                 :     181902 :     }
     292                 :            : 
     293                 :            :     /* Due to the history of this API, some applications depend on the blocked status to know if
     294                 :            :      * the connection's `in` stuffer was completely cleared. This behavior needs to be preserved.
     295                 :            :      *
     296                 :            :      * Moving forward, applications should instead use `s2n_peek`, which accomplishes the same thing
     297                 :            :      * without conflating being blocked on reading from the OS socket vs blocked on the application's
     298                 :            :      * buffer size.
     299                 :            :      */
     300         [ +  + ]:     187318 :     if (s2n_stuffer_data_available(&conn->in) == 0) {
     301                 :     152156 :         *blocked = S2N_NOT_BLOCKED;
     302                 :     152156 :     }
     303                 :            : 
     304                 :     187318 :     return bytes_read;
     305                 :     369865 : }
     306                 :            : 
     307                 :            : ssize_t s2n_recv(struct s2n_connection *conn, void *buf, ssize_t size, s2n_blocked_status *blocked)
     308                 :     371555 : {
     309 [ #  # ][ -  + ]:     371555 :     POSIX_ENSURE_REF(conn);
     310 [ +  + ][ +  - ]:     371555 :     POSIX_ENSURE(!conn->recv_in_use, S2N_ERR_REENTRANCY);
     311                 :     371553 :     conn->recv_in_use = true;
     312                 :            : 
     313                 :     371553 :     ssize_t result = s2n_recv_impl(conn, buf, size, blocked);
     314         [ +  + ]:     371553 :     POSIX_GUARD_RESULT(s2n_early_data_record_bytes(conn, result));
     315                 :            : 
     316                 :            :     /* finish the recv call */
     317         [ -  + ]:     371552 :     POSIX_GUARD_RESULT(s2n_connection_dynamic_free_in_buffer(conn));
     318                 :            : 
     319                 :     371552 :     conn->recv_in_use = false;
     320                 :     371552 :     return result;
     321                 :     371552 : }
     322                 :            : 
     323                 :            : uint32_t s2n_peek(struct s2n_connection *conn)
     324                 :       4459 : {
     325         [ +  + ]:       4459 :     if (conn == NULL) {
     326                 :          1 :         return 0;
     327                 :          1 :     }
     328                 :            : 
     329                 :            :     /* If we have partially buffered an encrypted record,
     330                 :            :      * we should not report those bytes as available to read.
     331                 :            :      */
     332         [ +  + ]:       4458 :     if (conn->in_status != PLAINTEXT) {
     333                 :       1305 :         return 0;
     334                 :       1305 :     }
     335                 :            : 
     336                 :       3153 :     return s2n_stuffer_data_available(&conn->in);
     337                 :       4458 : }
     338                 :            : 
     339                 :            : uint32_t s2n_peek_buffered(struct s2n_connection *conn)
     340                 :          5 : {
     341         [ +  + ]:          5 :     if (conn == NULL) {
     342                 :          1 :         return 0;
     343                 :          1 :     }
     344                 :          4 :     return s2n_stuffer_data_available(&conn->buffer_in);
     345                 :          5 : }

Generated by: LCOV version 1.14