LCOV - code coverage report
Current view: top level - stuffer - s2n_stuffer_text.c (source / functions) Hit Total Coverage
Test: unit_test_coverage.info Lines: 173 180 96.1 %
Date: 2026-07-04 07:27:58 Functions: 14 14 100.0 %
Branches: 127 228 55.7 %

           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                 :            : #include <string.h>
      17                 :            : 
      18                 :            : #include "stuffer/s2n_stuffer.h"
      19                 :            : #include "utils/s2n_mem.h"
      20                 :            : #include "utils/s2n_safety.h"
      21                 :            : 
      22                 :            : int s2n_stuffer_peek_char(struct s2n_stuffer *s2n_stuffer, char *c)
      23                 :      16669 : {
      24                 :      16669 :     int r = s2n_stuffer_read_uint8(s2n_stuffer, (uint8_t *) c);
      25         [ +  + ]:      16669 :     if (r == S2N_SUCCESS) {
      26                 :        161 :         s2n_stuffer->read_cursor--;
      27                 :        161 :     }
      28 [ -  + ][ +  - ]:      16669 :     POSIX_POSTCONDITION(s2n_stuffer_validate(s2n_stuffer));
      29                 :      16669 :     return r;
      30                 :      16669 : }
      31                 :            : 
      32                 :            : /* Peeks in stuffer to see if expected string is present. */
      33                 :            : int s2n_stuffer_peek_check_for_str(struct s2n_stuffer *s2n_stuffer, const char *expected)
      34                 :       3310 : {
      35 [ -  + ][ +  - ]:       3310 :     POSIX_PRECONDITION(s2n_stuffer_validate(s2n_stuffer));
      36                 :       3310 :     uint32_t orig_read_pos = s2n_stuffer->read_cursor;
      37                 :       3310 :     int rc = s2n_stuffer_read_expected_str(s2n_stuffer, expected);
      38                 :       3310 :     s2n_stuffer->read_cursor = orig_read_pos;
      39 [ -  + ][ +  - ]:       3310 :     POSIX_POSTCONDITION(s2n_stuffer_validate(s2n_stuffer));
      40                 :       3310 :     return rc;
      41                 :       3310 : }
      42                 :            : 
      43                 :            : int s2n_stuffer_skip_whitespace(struct s2n_stuffer *s2n_stuffer, uint32_t *skipped)
      44                 :       6636 : {
      45 [ -  + ][ +  - ]:       6636 :     POSIX_PRECONDITION(s2n_stuffer_validate(s2n_stuffer));
      46                 :       6636 :     uint32_t initial_read_cursor = s2n_stuffer->read_cursor;
      47         [ +  + ]:      15062 :     while (s2n_stuffer_data_available(s2n_stuffer)) {
      48                 :      13273 :         uint8_t c = s2n_stuffer->blob.data[s2n_stuffer->read_cursor];
      49                 :            :         /* We don't use isspace, because it changes under locales. */
      50 [ +  + ][ +  + ]:      13273 :         if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
         [ +  + ][ +  + ]
      51                 :       8426 :             s2n_stuffer->read_cursor += 1;
      52                 :       8426 :         } else {
      53                 :       4847 :             break;
      54                 :       4847 :         }
      55                 :      13273 :     }
      56         [ +  + ]:       6636 :     if (skipped != NULL) {
      57                 :          2 :         *skipped = s2n_stuffer->read_cursor - initial_read_cursor;
      58                 :          2 :     }
      59 [ -  + ][ +  - ]:       6636 :     POSIX_POSTCONDITION(s2n_stuffer_validate(s2n_stuffer));
      60                 :       6636 :     return S2N_SUCCESS;
      61                 :       6636 : }
      62                 :            : 
      63                 :            : int s2n_stuffer_read_expected_str(struct s2n_stuffer *stuffer, const char *expected)
      64                 :      20151 : {
      65 [ -  + ][ +  - ]:      20151 :     POSIX_PRECONDITION(s2n_stuffer_validate(stuffer));
      66 [ #  # ][ -  + ]:      20151 :     POSIX_ENSURE_REF(expected);
      67                 :      20151 :     size_t expected_length = strlen(expected);
      68         [ -  + ]:      20151 :     if (expected_length == 0) {
      69                 :          0 :         return S2N_SUCCESS;
      70                 :          0 :     }
      71 [ +  + ][ +  - ]:      20151 :     POSIX_ENSURE(s2n_stuffer_data_available(stuffer) >= expected_length, S2N_ERR_STUFFER_OUT_OF_DATA);
      72                 :      18361 :     uint8_t *actual = stuffer->blob.data + stuffer->read_cursor;
      73 [ #  # ][ -  + ]:      18361 :     POSIX_ENSURE_REF(actual);
      74 [ +  - ][ +  + ]:      18361 :     POSIX_ENSURE(!memcmp(actual, expected, expected_length), S2N_ERR_STUFFER_NOT_FOUND);
      75                 :      15059 :     stuffer->read_cursor += expected_length;
      76 [ -  + ][ +  - ]:      15059 :     POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer));
      77                 :      15059 :     return S2N_SUCCESS;
      78                 :      15059 : }
      79                 :            : 
      80                 :            : /* Read from stuffer until the target string is found, or until there is no more data. */
      81                 :            : int s2n_stuffer_skip_read_until(struct s2n_stuffer *stuffer, const char *target)
      82                 :      10496 : {
      83 [ -  + ][ +  - ]:      10496 :     POSIX_PRECONDITION(s2n_stuffer_validate(stuffer));
      84 [ -  + ][ #  # ]:      10496 :     POSIX_ENSURE_REF(target);
      85                 :      10496 :     const uint32_t len = strlen(target);
      86         [ -  + ]:      10496 :     if (len == 0) {
      87                 :          0 :         return S2N_SUCCESS;
      88                 :          0 :     }
      89         [ +  + ]:      10706 :     while (s2n_stuffer_data_available(stuffer) >= len) {
      90         [ -  + ]:      10705 :         POSIX_GUARD(s2n_stuffer_skip_to_char(stuffer, target[0]));
      91         [ +  + ]:      10705 :         POSIX_GUARD(s2n_stuffer_skip_read(stuffer, len));
      92                 :      10703 :         uint8_t *actual = stuffer->blob.data + stuffer->read_cursor - len;
      93 [ -  + ][ #  # ]:      10703 :         POSIX_ENSURE_REF(actual);
      94                 :            : 
      95         [ +  + ]:      10703 :         if (strncmp((char *) actual, target, len) == 0) {
      96                 :      10493 :             return S2N_SUCCESS;
      97                 :      10493 :         } else {
      98                 :            :             /* If string doesn't match, rewind stuffer to 1 byte after last read */
      99         [ -  + ]:        210 :             POSIX_GUARD(s2n_stuffer_rewind_read(stuffer, len - 1));
     100                 :        210 :             continue;
     101                 :        210 :         }
     102                 :      10703 :     }
     103 [ -  + ][ +  - ]:          1 :     POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer));
     104                 :          1 :     return S2N_SUCCESS;
     105                 :          1 : }
     106                 :            : 
     107                 :            : /* Skips the stuffer until the first instance of the target character or until there is no more data. */
     108                 :            : int s2n_stuffer_skip_to_char(struct s2n_stuffer *stuffer, const char target)
     109                 :      10816 : {
     110 [ -  + ][ +  - ]:      10816 :     POSIX_PRECONDITION(s2n_stuffer_validate(stuffer));
     111         [ +  + ]:      35183 :     while (s2n_stuffer_data_available(stuffer) > 0) {
     112         [ +  + ]:      35157 :         if (stuffer->blob.data[stuffer->read_cursor] == target) {
     113                 :      10790 :             break;
     114                 :      10790 :         }
     115                 :      24367 :         stuffer->read_cursor += 1;
     116                 :      24367 :     }
     117 [ -  + ][ +  - ]:      10816 :     POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer));
     118                 :      10816 :     return S2N_SUCCESS;
     119                 :      10816 : }
     120                 :            : 
     121                 :            : /* Skips an expected character in the stuffer between min and max times */
     122                 :            : int s2n_stuffer_skip_expected_char(struct s2n_stuffer *stuffer, const char expected, const uint32_t min,
     123                 :            :         const uint32_t max, uint32_t *skipped)
     124                 :      17170 : {
     125 [ -  + ][ +  - ]:      17170 :     POSIX_PRECONDITION(s2n_stuffer_validate(stuffer));
     126 [ #  # ][ -  + ]:      17170 :     POSIX_ENSURE(min <= max, S2N_ERR_SAFETY);
     127                 :            : 
     128                 :      17170 :     uint32_t skip = 0;
     129 [ +  + ][ +  + ]:     103427 :     while (stuffer->read_cursor < stuffer->write_cursor && skip < max) {
     130         [ +  + ]:     103372 :         if (stuffer->blob.data[stuffer->read_cursor] == expected) {
     131                 :      86257 :             stuffer->read_cursor += 1;
     132                 :      86257 :             skip += 1;
     133                 :      86257 :         } else {
     134                 :      17115 :             break;
     135                 :      17115 :         }
     136                 :     103372 :     }
     137 [ +  - ][ +  + ]:      17170 :     POSIX_ENSURE(skip >= min, S2N_ERR_STUFFER_NOT_FOUND);
     138         [ +  + ]:      17166 :     if (skipped != NULL) {
     139                 :         44 :         *skipped = skip;
     140                 :         44 :     }
     141 [ -  + ][ +  - ]:      17166 :     POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer));
     142                 :      17166 :     return S2N_SUCCESS;
     143                 :      17166 : }
     144                 :            : 
     145                 :            : /* Read a line of text. Agnostic to LF or CR+LF line endings. */
     146                 :            : int s2n_stuffer_read_line(struct s2n_stuffer *stuffer, struct s2n_stuffer *token)
     147                 :          4 : {
     148 [ -  + ][ +  - ]:          4 :     POSIX_PRECONDITION(s2n_stuffer_validate(stuffer));
     149 [ -  + ][ +  - ]:          4 :     POSIX_PRECONDITION(s2n_stuffer_validate(token));
     150                 :            :     /* Consume an LF terminated line */
     151         [ -  + ]:          4 :     POSIX_GUARD(s2n_stuffer_read_token(stuffer, token, '\n'));
     152                 :            : 
     153                 :            :     /* Snip off the carriage return if it's present */
     154 [ +  - ][ +  + ]:          4 :     if ((s2n_stuffer_data_available(token) > 0) && (token->blob.data[(token->write_cursor - 1)] == '\r')) {
     155                 :          1 :         token->write_cursor--;
     156                 :          1 :     }
     157 [ -  + ][ +  - ]:          4 :     POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer));
     158 [ -  + ][ +  - ]:          4 :     POSIX_POSTCONDITION(s2n_stuffer_validate(token));
     159                 :          4 :     return S2N_SUCCESS;
     160                 :          4 : }
     161                 :            : 
     162                 :            : int s2n_stuffer_read_token(struct s2n_stuffer *stuffer, struct s2n_stuffer *token, char delim)
     163                 :         10 : {
     164 [ -  + ][ +  - ]:         10 :     POSIX_PRECONDITION(s2n_stuffer_validate(stuffer));
     165 [ -  + ][ +  - ]:         10 :     POSIX_PRECONDITION(s2n_stuffer_validate(token));
     166                 :         10 :     uint32_t token_size = 0;
     167                 :            : 
     168         [ +  + ]:        302 :     while ((stuffer->read_cursor + token_size) < stuffer->write_cursor) {
     169         [ +  + ]:        300 :         if (stuffer->blob.data[stuffer->read_cursor + token_size] == delim) {
     170                 :          8 :             break;
     171                 :          8 :         }
     172                 :        292 :         token_size++;
     173                 :        292 :     }
     174                 :            : 
     175         [ -  + ]:         10 :     POSIX_GUARD(s2n_stuffer_copy(stuffer, token, token_size));
     176                 :            : 
     177                 :            :     /* Consume the delimiter too */
     178         [ +  + ]:         10 :     if (stuffer->read_cursor < stuffer->write_cursor) {
     179                 :          8 :         stuffer->read_cursor++;
     180                 :          8 :     }
     181                 :            : 
     182 [ -  + ][ +  - ]:         10 :     POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer));
     183 [ -  + ][ +  - ]:         10 :     POSIX_POSTCONDITION(s2n_stuffer_validate(token));
     184                 :         10 :     return S2N_SUCCESS;
     185                 :         10 : }
     186                 :            : 
     187                 :            : int s2n_stuffer_alloc_ro_from_string(struct s2n_stuffer *stuffer, const char *str)
     188                 :       1654 : {
     189 [ -  + ][ +  - ]:       1654 :     POSIX_PRECONDITION(s2n_stuffer_validate(stuffer));
     190 [ -  + ][ #  # ]:       1654 :     POSIX_ENSURE_REF(str);
     191                 :       1654 :     uint32_t length = strlen(str);
     192         [ -  + ]:       1654 :     POSIX_GUARD(s2n_stuffer_alloc(stuffer, length + 1));
     193                 :       1654 :     return s2n_stuffer_write_bytes(stuffer, (const uint8_t *) str, length);
     194                 :       1654 : }
     195                 :            : 
     196                 :            : int s2n_stuffer_init_ro_from_string(struct s2n_stuffer *stuffer, uint8_t *data, uint32_t length)
     197                 :        105 : {
     198 [ -  + ][ +  - ]:        105 :     POSIX_PRECONDITION(s2n_stuffer_validate(stuffer));
     199 [ #  # ][ -  + ]:        105 :     POSIX_ENSURE_REF(data);
     200                 :            : 
     201                 :        105 :     struct s2n_blob data_blob = { 0 };
     202         [ -  + ]:        105 :     POSIX_GUARD(s2n_blob_init(&data_blob, data, length));
     203                 :            : 
     204         [ -  + ]:        105 :     POSIX_GUARD(s2n_stuffer_init(stuffer, &data_blob));
     205         [ -  + ]:        105 :     POSIX_GUARD(s2n_stuffer_skip_write(stuffer, length));
     206                 :            : 
     207                 :        105 :     return S2N_SUCCESS;
     208                 :        105 : }
     209                 :            : 
     210                 :            : /* If we call va_start or va_copy there MUST be a matching call to va_end,
     211                 :            :  * so we should use DEFER_CLEANUP with our va_lists.
     212                 :            :  * Unfortunately, some environments implement va_list in ways that don't
     213                 :            :  * act as expected when passed by reference. For example, because va_end is
     214                 :            :  * a macro it may expect va_list to be an array (maybe to call sizeof),
     215                 :            :  * but passing va_list by reference will cause it to decay to a pointer instead.
     216                 :            :  * To avoid any surprises, just wrap the va_list in our own struct.
     217                 :            :  */
     218                 :            : struct s2n_va_list {
     219                 :            :     va_list va_list;
     220                 :            : };
     221                 :            : 
     222                 :            : static void s2n_va_list_cleanup(struct s2n_va_list *list)
     223                 :       4447 : {
     224         [ +  - ]:       4447 :     if (list) {
     225                 :       4447 :         va_end(list->va_list);
     226                 :       4447 :     }
     227                 :       4447 : }
     228                 :            : 
     229                 :            : int s2n_stuffer_vprintf(struct s2n_stuffer *stuffer, const char *format, va_list vargs_in)
     230                 :       1505 : {
     231 [ -  + ][ +  - ]:       1505 :     POSIX_PRECONDITION(s2n_stuffer_validate(stuffer));
     232 [ -  + ][ #  # ]:       1505 :     POSIX_ENSURE_REF(format);
     233                 :            : 
     234                 :            :     /* vsnprintf consumes the va_list, so copy it first */
     235                 :       1505 :     DEFER_CLEANUP(struct s2n_va_list vargs_1 = { 0 }, s2n_va_list_cleanup);
     236                 :       1505 :     va_copy(vargs_1.va_list, vargs_in);
     237                 :            : 
     238                 :            :     /* The first call to vsnprintf calculates the size of the formatted string.
     239                 :            :      * str_len does not include the one byte vsnprintf requires for a trailing '\0',
     240                 :            :      * so we need one more byte.
     241                 :            :      */
     242                 :       1505 :     int str_len = vsnprintf(NULL, 0, format, vargs_1.va_list);
     243 [ #  # ][ -  + ]:       1505 :     POSIX_ENSURE_GTE(str_len, 0);
     244 [ -  + ][ #  # ]:       1505 :     POSIX_ENSURE_LT(str_len, INT_MAX);
     245                 :       1505 :     int mem_size = str_len + 1;
     246                 :            : 
     247                 :            :     /* 'tainted' indicates that pointers to the contents of the stuffer exist,
     248                 :            :      * so resizing / reallocated the stuffer will invalidate those pointers.
     249                 :            :      * However, we do not resize the stuffer in this method after creating `str`
     250                 :            :      * and `str` does not live beyond this method, so ignore `str` for the
     251                 :            :      * purposes of tracking 'tainted'.
     252                 :            :      */
     253                 :       1505 :     bool previously_tainted = stuffer->tainted;
     254                 :       1505 :     char *str = s2n_stuffer_raw_write(stuffer, mem_size);
     255                 :       1505 :     stuffer->tainted = previously_tainted;
     256         [ +  + ]:       1505 :     POSIX_GUARD_PTR(str);
     257                 :            : 
     258                 :            :     /* vsnprintf again consumes the va_list, so copy it first */
     259                 :       1504 :     DEFER_CLEANUP(struct s2n_va_list vargs_2 = { 0 }, s2n_va_list_cleanup);
     260                 :       1504 :     va_copy(vargs_2.va_list, vargs_in);
     261                 :            : 
     262                 :            :     /* This time, vsnprintf actually writes the formatted string */
     263                 :       1504 :     int written = vsnprintf(str, mem_size, format, vargs_2.va_list);
     264         [ -  + ]:       1504 :     if (written != str_len) {
     265                 :            :         /* If the write fails, undo our raw write */
     266         [ #  # ]:          0 :         POSIX_GUARD(s2n_stuffer_wipe_n(stuffer, mem_size));
     267         [ #  # ]:          0 :         POSIX_BAIL(S2N_ERR_SAFETY);
     268                 :          0 :     }
     269                 :            : 
     270                 :            :     /* We don't actually use c-strings, so erase the final '\0' */
     271         [ -  + ]:       1504 :     POSIX_GUARD(s2n_stuffer_wipe_n(stuffer, 1));
     272                 :            : 
     273 [ -  + ][ +  - ]:       1504 :     POSIX_POSTCONDITION(s2n_stuffer_validate(stuffer));
     274                 :       1504 :     return S2N_SUCCESS;
     275                 :       1504 : }
     276                 :            : 
     277                 :            : int s2n_stuffer_printf(struct s2n_stuffer *stuffer, const char *format, ...)
     278                 :       1438 : {
     279                 :       1438 :     DEFER_CLEANUP(struct s2n_va_list vargs = { 0 }, s2n_va_list_cleanup);
     280                 :       1438 :     va_start(vargs.va_list, format);
     281         [ +  + ]:       1438 :     POSIX_GUARD(s2n_stuffer_vprintf(stuffer, format, vargs.va_list));
     282                 :       1437 :     return S2N_SUCCESS;
     283                 :       1438 : }

Generated by: LCOV version 1.14