Don't invalidate MPS reader buffers upon commit call

Previously, the semantics of mbedtls_mps_reader_commit() was to invalidate
all buffers previously fetched via mbedtls_mps_reader_get(), forbidding
any further use by the 'consumer'. This was in fact a necessary constraint
for the current implementation, which did some memory moving in
mbedtls_mps_reader_commit().

This commit simplifies the reader's semantics and implementation in
the following way:

- API: A call to mbedtls_mps_reader_commit() does no longer invalidate
       the buffers previously obtained via mbedtls_mps_reader_get().
       Instead, they can continue to be used until
       mbedtls_mps_reader_reclaim() is called.

       Calling mbedtls_mps_reader_commit() now only sets a marker
       indicating which parts of the data received through
       mbedtls_mps_reader_get() need not be backed up once
       mbedtls_mps_reader_reclaim() is called. Allowing the user
       to call mbedtls_mbedtls_reader_commit() multiple times
       before mbedtls_mps_reader_reclaim() is mere convenience:
       We'd get exactly the same functionality if instead of
       mbedtls_mps_reader_commit(), there was an additional argument
       to mbedtls_mps_reader_reclaim() indicating how much data
       to retain. However, the present design is more convenient
       for the user and doesn't appear to introduce any unnecessary
       complexity (anymore), so we stick with it for now.

- Implementation: mbedtls_mps_reader_commit() is now a 1-liner,
                  setting the 'commit-marker', but doing nothing else.

                  Instead, the complexity of mbedtls_mp_reader_reclaim()
                  slightly increases because it has to deal with creating
                  backups from both the accumulator and the current
                  fragment. In the previous implementation, which shifted
                  the accumulator content with every call to
                  mbedtls_mps_reader_commit(), only the backup from the
                  fragment was necessary; with the new implementation
                  which doesn't shift anything in
                  mbedtls_mps_reader_commit(), we need to do the
                  accumulator shift in mbedtls_mps_reader_reclaim().

Signed-off-by: Hanno Becker <hanno.becker@arm.com>
This commit is contained in:
Hanno Becker 2021-02-08 06:54:30 +00:00
parent 014f683ca9
commit 4f84e20eb0
2 changed files with 118 additions and 144 deletions

View File

@ -350,53 +350,14 @@ int mbedtls_mps_reader_get( mbedtls_mps_reader *rd,
int mbedtls_mps_reader_commit( mbedtls_mps_reader *rd ) int mbedtls_mps_reader_commit( mbedtls_mps_reader *rd )
{ {
unsigned char *acc; mbedtls_mps_size_t end;
mbedtls_mps_size_t aa, end, fo, shift;
MBEDTLS_MPS_TRACE_INIT( "reader_commit" ); MBEDTLS_MPS_TRACE_INIT( "reader_commit" );
MBEDTLS_MPS_STATE_VALIDATE_RAW( rd->frag != NULL, MBEDTLS_MPS_STATE_VALIDATE_RAW( rd->frag != NULL,
"mbedtls_mps_reader_commit() requires reader to be in consuming mode" ); "mbedtls_mps_reader_commit() requires reader to be in consuming mode" );
acc = rd->acc;
end = rd->end; end = rd->end;
if( acc == NULL )
{
MBEDTLS_MPS_TRACE( mbedtls_mps_trace_comment,
"No accumulator, just shift end" );
rd->commit = end;
MBEDTLS_MPS_TRACE_RETURN( 0 );
}
fo = rd->acc_share.frag_offset;
if( end >= fo )
{
MBEDTLS_MPS_TRACE( mbedtls_mps_trace_comment,
"Started to serve fragment, get rid of accumulator" );
shift = fo;
aa = 0;
}
else
{
MBEDTLS_MPS_TRACE( mbedtls_mps_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->commit = end;
rd->end = end;
MBEDTLS_MPS_TRACE( mbedtls_mps_trace_comment,
"Final state: (end=commit,fo,avail) = (%u,%u,%u)",
(unsigned) end, (unsigned) fo, (unsigned) aa );
MBEDTLS_MPS_TRACE_RETURN( 0 ); MBEDTLS_MPS_TRACE_RETURN( 0 );
} }
@ -406,7 +367,7 @@ int mbedtls_mps_reader_reclaim( mbedtls_mps_reader *rd,
unsigned char *frag, *acc; unsigned char *frag, *acc;
mbedtls_mps_size_t pending, commit; mbedtls_mps_size_t pending, commit;
mbedtls_mps_size_t al, fo, fl; mbedtls_mps_size_t al, fo, fl;
MBEDTLS_MPS_TRACE_INIT( "reader_reclaim" ); MBEDTLS_MPS_TRACE_INIT( "mbedtls_mps_reader_reclaim" );
if( paused != NULL ) if( paused != NULL )
*paused = 0; *paused = 0;
@ -429,6 +390,7 @@ int mbedtls_mps_reader_reclaim( mbedtls_mps_reader *rd,
{ {
MBEDTLS_MPS_TRACE( mbedtls_mps_trace_comment, MBEDTLS_MPS_TRACE( mbedtls_mps_trace_comment,
"No unsatisfied read-request has been logged." ); "No unsatisfied read-request has been logged." );
/* Check if there's data left to be consumed. */ /* Check if there's data left to be consumed. */
if( commit < fo || commit - fo < fl ) if( commit < fo || commit - fo < fl )
{ {
@ -437,16 +399,28 @@ int mbedtls_mps_reader_reclaim( mbedtls_mps_reader *rd,
rd->end = commit; rd->end = commit;
MBEDTLS_MPS_TRACE_RETURN( MBEDTLS_ERR_MPS_READER_DATA_LEFT ); MBEDTLS_MPS_TRACE_RETURN( MBEDTLS_ERR_MPS_READER_DATA_LEFT );
} }
rd->acc_avail = 0;
rd->acc_share.acc_remaining = 0;
MBEDTLS_MPS_TRACE( mbedtls_mps_trace_comment, MBEDTLS_MPS_TRACE( mbedtls_mps_trace_comment,
"The fragment has been completely processed and committed." ); "Fragment has been fully processed and committed." );
} }
else else
{ {
int overflow;
mbedtls_mps_size_t acc_backup_offset;
mbedtls_mps_size_t acc_backup_len;
mbedtls_mps_size_t frag_backup_offset; mbedtls_mps_size_t frag_backup_offset;
mbedtls_mps_size_t frag_backup_len; mbedtls_mps_size_t frag_backup_len;
mbedtls_mps_size_t backup_len;
mbedtls_mps_size_t acc_len_needed;
MBEDTLS_MPS_TRACE( mbedtls_mps_trace_comment, MBEDTLS_MPS_TRACE( mbedtls_mps_trace_comment,
"There has been an unsatisfied read-request with %u bytes overhead.", "There has been an unsatisfied read with %u bytes overhead.",
(unsigned) pending ); (unsigned) pending );
if( acc == NULL ) if( acc == NULL )
{ {
@ -462,69 +436,61 @@ int mbedtls_mps_reader_reclaim( mbedtls_mps_reader *rd,
if( commit < fo ) if( commit < fo )
{ {
/* No, accumulator is still being processed. */ /* No, accumulator is still being processed. */
int overflow;
MBEDTLS_MPS_TRACE( mbedtls_mps_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;
MBEDTLS_MPS_TRACE( mbedtls_mps_trace_error,
"The accumulator is too small to handle the backup." );
MBEDTLS_MPS_TRACE( mbedtls_mps_trace_error,
"* Remaining size: %u", (unsigned) al );
MBEDTLS_MPS_TRACE( mbedtls_mps_trace_error,
"* Needed: %u (%u + %u + %u)",
(unsigned) ( fo + fl + pending ),
(unsigned) fo, (unsigned) fl, (unsigned) pending );
MBEDTLS_MPS_TRACE_RETURN(
MBEDTLS_ERR_MPS_READER_ACCUMULATOR_TOO_SMALL );
}
frag_backup_offset = 0; frag_backup_offset = 0;
frag_backup_len = fl; frag_backup_len = fl;
acc_backup_offset = commit;
acc_backup_len = fo - commit;
} }
else else
{ {
/* Yes, the accumulator is already processed. */ /* Yes, the accumulator is already processed. */
int overflow; frag_backup_offset = commit - fo;
MBEDTLS_MPS_TRACE( mbedtls_mps_trace_comment, frag_backup_len = fl - frag_backup_offset;
"The accumulator has already been processed" ); acc_backup_offset = 0;
acc_backup_len = 0;
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;
MBEDTLS_MPS_TRACE( mbedtls_mps_trace_error,
"The accumulator is too small to handle the backup." );
MBEDTLS_MPS_TRACE( mbedtls_mps_trace_error,
"* Remaining size: %u", (unsigned) ( al - fo ) );
MBEDTLS_MPS_TRACE( mbedtls_mps_trace_error,
"* Needed: %u (%u + %u)",
(unsigned) ( frag_backup_len + pending ),
(unsigned) frag_backup_len, (unsigned) pending );
MBEDTLS_MPS_TRACE_RETURN(
MBEDTLS_ERR_MPS_READER_ACCUMULATOR_TOO_SMALL );
}
} }
frag += frag_backup_offset; backup_len = acc_backup_len + frag_backup_len;
acc += fo; acc_len_needed = backup_len + pending;
memcpy( acc, frag, frag_backup_len );
overflow = 0;
overflow |= ( backup_len < acc_backup_len );
overflow |= ( acc_len_needed < backup_len );
if( overflow || al < acc_len_needed )
{
/* Except for the different return code, we behave as if
* there hadn't been a call to mbedtls_mps_reader_get()
* since the last commit. */
rd->end = commit;
rd->pending = 0;
MBEDTLS_MPS_TRACE( mbedtls_mps_trace_error,
"The accumulator is too small to handle the backup." );
MBEDTLS_MPS_TRACE( mbedtls_mps_trace_error,
"* Size: %u", (unsigned) al );
MBEDTLS_MPS_TRACE( mbedtls_mps_trace_error,
"* Needed: %u (%u + %u)",
(unsigned) acc_len_needed,
(unsigned) backup_len, (unsigned) pending );
MBEDTLS_MPS_TRACE_RETURN(
MBEDTLS_ERR_MPS_READER_ACCUMULATOR_TOO_SMALL );
}
MBEDTLS_MPS_TRACE( mbedtls_mps_trace_comment, MBEDTLS_MPS_TRACE( mbedtls_mps_trace_comment,
"Backup %u bytes into accumulator", "Fragment backup: %u", (unsigned) frag_backup_len );
(unsigned) frag_backup_len ); MBEDTLS_MPS_TRACE( mbedtls_mps_trace_comment,
"Accumulator backup: %u", (unsigned) acc_backup_len );
rd->acc_avail = fo + frag_backup_len; /* Move uncommitted parts from the accumulator to the front
* of the accumulator. */
memmove( acc, acc + acc_backup_offset, acc_backup_len );
/* Copy uncmmitted parts of the current fragment to the
* accumulator. */
memcpy( acc + acc_backup_len,
frag + frag_backup_offset, frag_backup_len );
rd->acc_avail = backup_len;
rd->acc_share.acc_remaining = pending; rd->acc_share.acc_remaining = pending;
if( paused != NULL ) if( paused != NULL )
@ -534,14 +500,13 @@ int mbedtls_mps_reader_reclaim( mbedtls_mps_reader *rd,
rd->frag = NULL; rd->frag = NULL;
rd->frag_len = 0; rd->frag_len = 0;
rd->commit = 0; rd->commit = 0;
rd->end = 0; rd->end = 0;
rd->pending = 0; rd->pending = 0;
MBEDTLS_MPS_TRACE( mbedtls_mps_trace_comment, MBEDTLS_MPS_TRACE( mbedtls_mps_trace_comment,
"Final state: aa %u, al %u, ar %u", "Final state: aa %u, al %u, ar %u",
(unsigned) rd->acc_avail, (unsigned) rd->acc_len, (unsigned) rd->acc_avail, (unsigned) rd->acc_len,
(unsigned) rd->acc_share.acc_remaining ); (unsigned) rd->acc_share.acc_remaining );
MBEDTLS_MPS_TRACE_RETURN( 0 ); MBEDTLS_MPS_TRACE_RETURN( 0 );
} }

View File

@ -31,7 +31,7 @@
* a 'consumer' which fetches and processes it in chunks of * a 'consumer' which fetches and processes it in chunks of
* again arbitrary, and potentially different, size. * again arbitrary, and potentially different, size.
* *
* Readers can be seen as datagram-to-stream converters, * Readers can thus be seen as datagram-to-stream converters,
* and they abstract away the following two tasks from the user: * and they abstract away the following two tasks from the user:
* 1. The pointer arithmetic of stepping through a producer- * 1. The pointer arithmetic of stepping through a producer-
* provided chunk in smaller chunks. * provided chunk in smaller chunks.
@ -54,36 +54,39 @@
* be satisfiable. * be satisfiable.
* - Repeat the above. * - Repeat the above.
* *
* From the perspective of the consumer, the state of the * The abstract states of the reader from the producer's and
* reader is a potentially empty list of input buffers that * consumer's perspective are as follows:
* the reader has provided to the consumer.
* New buffers can be requested through calls to mbedtls_mps_reader_get(),
* while previously obtained input buffers can be marked processed
* through calls to mbedtls_mps_reader_consume(), emptying the list of
* input buffers and invalidating them from the consumer's perspective.
* The consumer need not be aware of the distinction between consumer
* and producer mode, because he only interfaces with the reader
* when the latter is in consuming mode.
* *
* From the perspective of the producer, the state of the reader * - From the perspective of the consumer, the state of the
* is one of the following: * reader consists of the following:
* - Attached: An incoming data buffer is currently * - A byte stream representing (concatenation of) the data
* being managed by the reader, and * received through calls to mbedtls_mps_reader_get(),
* - Unset: No incoming data buffer is currently * - A marker within that byte stream indicating which data
* managed by the reader, and all previously * need not be retained when the reader is passed back to
* handed incoming data buffers have been * the producer via mbedtls_mps_reader_reclaim().
* fully processed. * The marker can be set via mbedtls_mps_reader_commit()
* - Accumulating: No incoming data buffer is currently * which places it at the end of the current byte stream.
* managed by the reader, but some data * The consumer need not be aware of the distinction between consumer
* from the previous incoming data buffer * and producer mode, because he only interfaces with the reader
* hasn't been processed yet and is internally * when the latter is in consuming mode.
* held back.
* The Unset and Accumulating states belong to producing mode,
* while the Attached state belongs to consuming mode.
* *
* Transitioning from Unset or Accumulating to Attached is * - From the perspective of the producer, the reader's state is one of:
* done via calls to mbedtls_mps_reader_feed(), while transitioning * - Attached: The reader is in consuming mode.
* from Consuming to either Unset or Accumulating (depending * - Unset: No incoming data buffer is currently managed by the reader,
* and all previously handed incoming data buffers have been
* fully processed. More data needs to be fed into the reader
* via mbedtls_mps_reader_feed().
*
* - Accumulating: No incoming data buffer is currently managed by the
* reader, but some data from the previous incoming data
* buffer hasn't been processed yet and is internally
* held back.
* The Attached state belongs to consuming mode, while the Unset and
* Accumulating states belong to producing mode.
*
* Transitioning from the Unset or Accumulating state to Attached is
* done via successful calls to mbedtls_mps_reader_feed(), while
* transitioning from Consuming to either Unset or Accumulating (depending
* on what has been processed) is done via mbedtls_mps_reader_reclaim(). * on what has been processed) is done via mbedtls_mps_reader_reclaim().
* *
* The following diagram depicts the producer-state progression: * The following diagram depicts the producer-state progression:
@ -94,9 +97,9 @@
* | | | | * | | | |
* | | | | * | | | |
* | feed +---------+---+--+ | * | feed +---------+---+--+ |
* +--------------------------------------> Attached <---+ * +--------------------------------------> <---+
* | / | * | Attached |
* +--------------------------------------> Consuming <---+ * +--------------------------------------> <---+
* | feed, enough data available +---------+---+--+ | * | feed, enough data available +---------+---+--+ |
* | to serve previous consumer request | | | * | to serve previous consumer request | | |
* | | | | * | | | |
@ -108,6 +111,10 @@
* +--------+ * +--------+
* feed, need more data to serve * feed, need more data to serve
* previous consumer request * previous consumer request
* |
* |
* producing mode | consuming mode
* |
* *
*/ */
@ -141,7 +148,7 @@ struct mbedtls_mps_reader
/*!< The offset of the last commit, relative /*!< The offset of the last commit, relative
* to the first byte in the accumulator. * to the first byte in the accumulator.
* This is only used when the reader is in * This is only used when the reader is in
* consuming mode, i.e. frag != NULL; * consuming mode, i.e. \c frag != \c NULL;
* otherwise, its value is \c 0. */ * otherwise, its value is \c 0. */
mbedtls_mps_stored_size_t end; mbedtls_mps_stored_size_t end;
/*!< The offset of the end of the last chunk /*!< The offset of the end of the last chunk
@ -306,8 +313,7 @@ int mbedtls_mps_reader_reclaim( mbedtls_mps_reader *reader,
* address of a buffer of size \c *buflen * address of a buffer of size \c *buflen
* (if \c buflen != \c NULL) or \c desired * (if \c buflen != \c NULL) or \c desired
* (if \c buflen == \c NULL). The user hass ownership * (if \c buflen == \c NULL). The user hass ownership
* of the buffer until the next call to mbedtls_mps_reader_commit(). * of the buffer until the next call mbedtls_mps_reader_reclaim().
* or mbedtls_mps_reader_reclaim().
* \return #MBEDTLS_ERR_MPS_READER_OUT_OF_DATA if there is not enough * \return #MBEDTLS_ERR_MPS_READER_OUT_OF_DATA if there is not enough
* data available to serve the read request. In this case, * data available to serve the read request. In this case,
* the reader remains intact, and additional data can be * the reader remains intact, and additional data can be
@ -329,19 +335,22 @@ int mbedtls_mps_reader_get( mbedtls_mps_reader *reader,
mbedtls_mps_size_t *buflen ); mbedtls_mps_size_t *buflen );
/** /**
* \brief Signal that all input buffers previously obtained * \brief Mark data obtained from mbedtls_writer_get() as processed.
* from mbedtls_writer_get() are fully processed.
* *
* This function marks the previously fetched data as fully * This call indicates that all data received from prior calls to
* processed and invalidates their respective buffers. * mbedtls_mps_reader_fetch() has been or will have been
* processed when mbedtls_mps_reader_reclaim() is called,
* and thus need not be backed up.
* *
* \param reader The reader context to use. * This function has no user observable effect until
* mbedtls_mps_reader_reclaim() is called. In particular,
* buffers received from mbedtls_mps_reader_fetch() remain
* valid until mbedtls_mps_reader_reclaim() is called.
* *
* \return \c 0 on success. * \param reader The reader context to use.
* \return A negative \c MBEDTLS_ERR_READER_XXX error code on failure.
* *
* \warning Once this function is called, you must not use the * \return \c 0 on success.
* pointers corresponding to the committed data anymore. * \return A negative \c MBEDTLS_ERR_READER_XXX error code on failure.
* *
*/ */
int mbedtls_mps_reader_commit( mbedtls_mps_reader *reader ); int mbedtls_mps_reader_commit( mbedtls_mps_reader *reader );