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

Generated by: LCOV version 1.14