Rename MPS files library/mps/xxx.[ch] to library/mps_xxx.[ch]
Signed-off-by: Hanno Becker <hanno.becker@arm.com>
diff --git a/library/mps_reader.c b/library/mps_reader.c
new file mode 100644
index 0000000..e6fbb07
--- /dev/null
+++ b/library/mps_reader.c
@@ -0,0 +1,513 @@
+/*
+ * Message Processing Stack, Reader implementation
+ *
+ * Copyright The Mbed TLS Contributors
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * This file is part of Mbed TLS (https://tls.mbed.org)
+ */
+
+#include "mps_reader.h"
+#include "mps_common.h"
+#include "mps_trace.h"
+
+#include <string.h>
+
+#if ( defined(__ARMCC_VERSION) || defined(_MSC_VER) ) && \
+ !defined(inline) && !defined(__cplusplus)
+#define inline __inline
+#endif
+
+#if defined(MBEDTLS_MPS_TRACE)
+static int trace_id = TRACE_BIT_READER;
+#endif /* MBEDTLS_MPS_TRACE */
+
+/*
+ * GENERAL NOTE ON CODING STYLE
+ *
+ * The following code intentionally separates memory loads
+ * and stores from other operations (arithmetic or branches).
+ * This leads to the introduction of many local variables
+ * and significantly increases the C-code line count, but
+ * should not increase the size of generated assembly.
+ *
+ * This reason for this is twofold:
+ * (1) It will ease verification efforts using the VST
+ * whose program logic cannot directly reason
+ * about instructions containing a load or store in
+ * addition to other operations (e.g. *p = *q or
+ * tmp = *p + 42).
+ * (2) Operating on local variables and writing the results
+ * back to the target contexts on success only
+ * allows to maintain structure invariants even
+ * on failure - this in turn has two benefits:
+ * (2.a) If for some reason an error code is not caught
+ * and operation continues, functions are nonetheless
+ * called with sane contexts, reducing the risk
+ * of dangerous behavior.
+ * (2.b) Randomized testing is easier if structures
+ * remain intact even in the face of failing
+ * and/or non-sensical calls.
+ * Moreover, it might even reduce code-size because
+ * the compiler need not write back temporary results
+ * to memory in case of failure.
+ *
+ */
+
+static inline void mps_reader_zero( mbedtls_reader *rd )
+{
+ /* A plain memset() would likely be more efficient,
+ * but the current way of zeroing makes it harder
+ * to overlook fields which should not be zero-initialized.
+ * It's also more suitable for VF efforts since it
+ * doesn't require reasoning about structs being
+ * interpreted as unstructured binary blobs. */
+ static mbedtls_reader const zero =
+ { .frag = NULL,
+ .frag_len = 0,
+ .commit = 0,
+ .end = 0,
+ .pending = 0,
+ .acc = NULL,
+ .acc_len = 0,
+ .acc_avail = 0,
+ .acc_share = { .acc_remaining = 0 }
+ };
+ *rd = zero;
+}
+
+int mbedtls_reader_init( mbedtls_reader *rd,
+ unsigned char *acc,
+ mbedtls_mps_size_t acc_len )
+{
+ TRACE_INIT( "reader_init, acc len %u", (unsigned) acc_len );
+ mps_reader_zero( rd );
+ rd->acc = acc;
+ rd->acc_len = acc_len;
+ RETURN( 0 );
+}
+
+int mbedtls_reader_free( mbedtls_reader *rd )
+{
+ TRACE_INIT( "reader_free" );
+ mps_reader_zero( rd );
+ RETURN( 0 );
+}
+
+int mbedtls_reader_feed( mbedtls_reader *rd,
+ unsigned char *new_frag,
+ mbedtls_mps_size_t new_frag_len )
+{
+ unsigned char *acc;
+ mbedtls_mps_size_t copy_to_acc;
+ TRACE_INIT( "reader_feed, frag %p, len %u",
+ (void*) new_frag, (unsigned) new_frag_len );
+
+ if( new_frag == NULL )
+ RETURN( MBEDTLS_ERR_MPS_READER_INVALID_ARG );
+
+ MBEDTLS_MPS_STATE_VALIDATE_RAW( rd->frag == NULL,
+ "mbedtls_reader_feed() requires reader to be in producing mode" );
+
+ acc = rd->acc;
+ if( acc != NULL )
+ {
+ mbedtls_mps_size_t aa, ar;
+
+ ar = rd->acc_share.acc_remaining;
+ aa = rd->acc_avail;
+
+ copy_to_acc = ar;
+ if( copy_to_acc > new_frag_len )
+ copy_to_acc = new_frag_len;
+
+ acc += aa;
+
+ if( copy_to_acc > 0 )
+ memcpy( acc, new_frag, copy_to_acc );
+
+ TRACE( trace_comment, "Copy new data of size %u of %u into accumulator at offset %u",
+ (unsigned) copy_to_acc, (unsigned) new_frag_len, (unsigned) aa );
+
+ /* Check if, with the new fragment, we have enough data. */
+ ar -= copy_to_acc;
+ if( ar > 0 )
+ {
+ /* Need more data */
+ aa += copy_to_acc;
+ rd->acc_share.acc_remaining = ar;
+ rd->acc_avail = aa;
+ RETURN( MBEDTLS_ERR_MPS_READER_NEED_MORE );
+ }
+
+ TRACE( trace_comment, "Enough data available to serve user request" );
+
+ rd->acc_share.frag_offset = aa;
+ aa += copy_to_acc;
+ rd->acc_avail = aa;
+ }
+ else
+ {
+ rd->acc_share.frag_offset = 0;
+ }
+
+ rd->frag = new_frag;
+ rd->frag_len = new_frag_len;
+ rd->commit = 0;
+ rd->end = 0;
+ RETURN( 0 );
+}
+
+
+int mbedtls_reader_get( mbedtls_reader *rd,
+ mbedtls_mps_size_t desired,
+ unsigned char **buffer,
+ mbedtls_mps_size_t *buflen )
+{
+ unsigned char *frag, *acc;
+ mbedtls_mps_size_t end, fo, fl, frag_fetched, frag_remaining;
+ TRACE_INIT( "reader_get %p, desired %u", (void*) rd, (unsigned) desired );
+
+ frag = rd->frag;
+ MBEDTLS_MPS_STATE_VALIDATE_RAW( frag != NULL,
+ "mbedtls_reader_get() requires reader to be in consuming mode" );
+
+ /* The fragment offset indicates the offset of the fragment
+ * from the accmulator, if the latter is present. Use a offset
+ * of \c 0 if no accumulator is used. */
+ acc = rd->acc;
+ if( acc == NULL )
+ fo = 0;
+ else
+ fo = rd->acc_share.frag_offset;
+
+ TRACE( trace_comment, "frag_off %u, end %u, acc_avail %d",
+ (unsigned) fo, (unsigned) rd->end,
+ acc == NULL ? -1 : (int) rd->acc_avail );
+
+ /* Check if we're still serving from the accumulator. */
+ end = rd->end;
+ if( end < fo )
+ {
+ TRACE( trace_comment, "Serve the request from the accumulator" );
+ if( fo - end < desired )
+ {
+ /* Illustration of supported and unsupported cases:
+ *
+ * - Allowed #1
+ *
+ * +-----------------------------------+
+ * | frag |
+ * +-----------------------------------+
+ *
+ * end end+desired
+ * | |
+ * +-----v-------v-------------+
+ * | acc |
+ * +---------------------------+
+ * | |
+ * fo/frag_offset aa/acc_avail
+ *
+ * - Allowed #2
+ *
+ * +-----------------------------------+
+ * | frag |
+ * +-----------------------------------+
+ *
+ * end end+desired
+ * | |
+ * +----------v----------------v
+ * | acc |
+ * +---------------------------+
+ * | |
+ * fo/frag_offset aa/acc_avail
+ *
+ * - Not allowed #1 (could be served, but we don't actually use it):
+ *
+ * +-----------------------------------+
+ * | frag |
+ * +-----------------------------------+
+ *
+ * end end+desired
+ * | |
+ * +------v-------------v------+
+ * | acc |
+ * +---------------------------+
+ * | |
+ * fo/frag_offset aa/acc_avail
+ *
+ *
+ * - Not allowed #2 (can't be served with a contiguous buffer):
+ *
+ * +-----------------------------------+
+ * | frag |
+ * +-----------------------------------+
+ *
+ * end end + desired
+ * | |
+ * +------v--------------------+ v
+ * | acc |
+ * +---------------------------+
+ * | |
+ * fo/frag_offset aa/acc_avail
+ *
+ * In case of Allowed #1 and #2 we're switching to serve from
+ * `frag` starting from the next call to mbedtls_reader_get().
+ */
+
+ mbedtls_mps_size_t aa;
+ aa = rd->acc_avail;
+ if( aa - end != desired )
+ {
+ /* It might be possible to serve some of these situations by
+ * making additional space in the accumulator, removing those
+ * parts that have already been committed.
+ * On the other hand, this brings additional complexity and
+ * enlarges the code size, while there doesn't seem to be a use
+ * case where we don't attempt exactly the same `get` calls when
+ * resuming on a reader than what we tried before pausing it.
+ * If we believe we adhere to this restricted usage throughout
+ * the library, this check is a good opportunity to
+ * validate this. */
+ RETURN( MBEDTLS_ERR_MPS_READER_INCONSISTENT_REQUESTS );
+ }
+ }
+
+ acc += end;
+ *buffer = acc;
+ if( buflen != NULL )
+ *buflen = desired;
+
+ end += desired;
+ rd->end = end;
+ rd->pending = 0;
+
+ RETURN( 0 );
+ }
+
+ /* Attempt to serve the request from the current fragment */
+ TRACE( trace_comment, "Serve the request from the current fragment." );
+
+ fl = rd->frag_len;
+ frag_fetched = end - fo; /* The amount of data from the current fragment
+ * that has already been passed to the user. */
+ frag += frag_fetched;
+ frag_remaining = fl - frag_fetched; /* Remaining data in fragment */
+
+ /* Check if we can serve the read request from the fragment. */
+ if( frag_remaining < desired )
+ {
+ TRACE( trace_comment, "There's not enough data in the current fragment to serve the request." );
+ /* There's not enough data in the current fragment,
+ * so either just RETURN what we have or fail. */
+ if( buflen == NULL )
+ {
+ if( frag_remaining > 0 )
+ {
+ rd->pending = desired - frag_remaining;
+ TRACE( trace_comment, "Remember to collect %u bytes before re-opening",
+ (unsigned) rd->pending );
+ }
+ RETURN( MBEDTLS_ERR_MPS_READER_OUT_OF_DATA );
+ }
+
+ desired = frag_remaining;
+ }
+
+ /* There's enough data in the current fragment to serve the
+ * (potentially modified) read request. */
+ *buffer = frag;
+ if( buflen != NULL )
+ *buflen = desired;
+
+ end += desired;
+ rd->end = end;
+ rd->pending = 0;
+ RETURN( 0 );
+}
+
+int mbedtls_reader_commit( mbedtls_reader *rd )
+{
+ unsigned char *acc;
+ mbedtls_mps_size_t aa, end, fo, shift;
+ TRACE_INIT( "reader_commit" );
+
+ MBEDTLS_MPS_STATE_VALIDATE_RAW( rd->frag != NULL,
+ "mbedtls_reader_commit() requires reader to be in consuming mode" );
+
+ acc = rd->acc;
+ end = rd->end;
+
+ if( acc == NULL )
+ {
+ TRACE( trace_comment, "No accumulator, just shift end" );
+ rd->commit = end;
+ RETURN( 0 );
+ }
+
+ fo = rd->acc_share.frag_offset;
+ if( end >= fo )
+ {
+ TRACE( trace_comment, "Started to serve fragment, get rid of accumulator" );
+ shift = fo;
+ aa = 0;
+ }
+ else
+ {
+ TRACE( trace_comment, "Still serving from accumulator" );
+ aa = rd->acc_avail;
+ shift = end;
+ memmove( acc, acc + shift, aa - shift );
+ aa -= shift;
+ }
+
+ end -= shift;
+ fo -= shift;
+
+ rd->acc_share.frag_offset = fo;
+ rd->acc_avail = aa;
+ rd->commit = end;
+ rd->end = end;
+
+ TRACE( trace_comment, "Final state: (end=commit,fo,avail) = (%u,%u,%u)",
+ (unsigned) end, (unsigned) fo, (unsigned) aa );
+ RETURN( 0 );
+}
+
+int mbedtls_reader_reclaim( mbedtls_reader *rd,
+ mbedtls_mps_size_t *paused )
+{
+ unsigned char *frag, *acc;
+ mbedtls_mps_size_t pending, commit;
+ mbedtls_mps_size_t al, fo, fl;
+ TRACE_INIT( "reader_reclaim" );
+
+ if( paused != NULL )
+ *paused = 0;
+
+ frag = rd->frag;
+ MBEDTLS_MPS_STATE_VALIDATE_RAW( frag != NULL,
+ "mbedtls_reader_reclaim() requires reader to be in consuming mode" );
+
+ acc = rd->acc;
+ pending = rd->pending;
+ commit = rd->commit;
+ fl = rd->frag_len;
+
+ if( acc == NULL )
+ fo = 0;
+ else
+ fo = rd->acc_share.frag_offset;
+
+ if( pending == 0 )
+ {
+ TRACE( trace_comment, "No unsatisfied read-request has been logged." );
+ /* Check if there's data left to be consumed. */
+ if( commit < fo || commit - fo < fl )
+ {
+ TRACE( trace_comment, "There is data left to be consumed." );
+ rd->end = commit;
+ RETURN( MBEDTLS_ERR_MPS_READER_DATA_LEFT );
+ }
+ TRACE( trace_comment, "The fragment has been completely processed and committed." );
+ }
+ else
+ {
+ mbedtls_mps_size_t frag_backup_offset;
+ mbedtls_mps_size_t frag_backup_len;
+ TRACE( trace_comment, "There has been an unsatisfied read-request with %u bytes overhead.",
+ (unsigned) pending );
+
+ if( acc == NULL )
+ {
+ TRACE( trace_comment, "No accumulator present" );
+ RETURN( MBEDTLS_ERR_MPS_READER_NEED_ACCUMULATOR );
+ }
+ al = rd->acc_len;
+
+ /* Check if the upper layer has already fetched
+ * and committed the contents of the accumulator. */
+ if( commit < fo )
+ {
+ /* No, accumulator is still being processed. */
+ int overflow;
+ TRACE( trace_comment, "Still processing data from the accumulator" );
+
+ overflow =
+ ( fo + fl < fo ) || ( fo + fl + pending < fo + fl );
+ if( overflow || al < fo + fl + pending )
+ {
+ rd->end = commit;
+ rd->pending = 0;
+ TRACE( trace_error, "The accumulator is too small to handle the backup." );
+ TRACE( trace_error, "* Remaining size: %u", (unsigned) al );
+ TRACE( trace_error, "* Needed: %u (%u + %u + %u)",
+ (unsigned) ( fo + fl + pending ),
+ (unsigned) fo, (unsigned) fl, (unsigned) pending );
+ RETURN( MBEDTLS_ERR_MPS_READER_ACCUMULATOR_TOO_SMALL );
+ }
+ frag_backup_offset = 0;
+ frag_backup_len = fl;
+ }
+ else
+ {
+ /* Yes, the accumulator is already processed. */
+ int overflow;
+ TRACE( trace_comment, "The accumulator has already been processed" );
+
+ frag_backup_offset = commit;
+ frag_backup_len = fl - commit;
+ overflow = ( frag_backup_len + pending < pending );
+
+ if( overflow ||
+ al - fo < frag_backup_len + pending )
+ {
+ rd->end = commit;
+ rd->pending = 0;
+ TRACE( trace_error, "The accumulator is too small to handle the backup." );
+ TRACE( trace_error, "* Remaining size: %u", (unsigned) ( al - fo ) );
+ TRACE( trace_error, "* Needed: %u (%u + %u)",
+ (unsigned) ( frag_backup_len + pending ),
+ (unsigned) frag_backup_len, (unsigned) pending );
+ RETURN( MBEDTLS_ERR_MPS_READER_ACCUMULATOR_TOO_SMALL );
+ }
+ }
+
+ frag += frag_backup_offset;
+ acc += fo;
+ memcpy( acc, frag, frag_backup_len );
+
+ TRACE( trace_comment, "Backup %u bytes into accumulator",
+ (unsigned) frag_backup_len );
+
+ rd->acc_avail = fo + frag_backup_len;
+ rd->acc_share.acc_remaining = pending;
+
+ if( paused != NULL )
+ *paused = 1;
+ }
+
+ rd->frag = NULL;
+ rd->frag_len = 0;
+
+ rd->commit = 0;
+ rd->end = 0;
+ rd->pending = 0;
+
+ TRACE( trace_comment, "Final state: aa %u, al %u, ar %u",
+ (unsigned) rd->acc_avail, (unsigned) rd->acc_len,
+ (unsigned) rd->acc_share.acc_remaining );
+ RETURN( 0 );
+}