blob: f90790b435fa60c1826978d315612c71ecc8a1fb [file] [log] [blame]
/* ==========================================================================
* Copyright (c) 2016-2018, The Linux Foundation.
* Copyright (c) 2018-2024, Laurence Lundblade.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation nor the names of its
* contributors, nor the name "Laurence Lundblade" may be used to
* endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* ========================================================================= */
/*=============================================================================
FILE: UsefulBuf.c
DESCRIPTION: General purpose input and output buffers
EDIT HISTORY FOR FILE:
This section contains comments describing changes made to the module.
Notice that changes are listed in reverse chronological order.
when who what, where, why
-------- ---- ---------------------------------------------------
21/05/2024 llundblade Comment formatting and some code tidiness.
19/12/2022 llundblade Don't pass NULL to memmove when adding empty data.
4/11/2022 llundblade Add GetOutPlace and Advance to UsefulOutBuf
3/6/2021 mcr/llundblade Fix warnings related to --Wcast-qual
01/28/2020 llundblade Refine integer signedness to quiet static analysis.
01/08/2020 llundblade Documentation corrections & improved code formatting.
11/08/2019 llundblade Re check pointer math and update comments
3/6/2019 llundblade Add UsefulBuf_IsValue()
09/07/17 llundbla Fix critical bug in UsefulBuf_Find() -- a read off
the end of memory when the bytes to find is longer
than the bytes to search.
06/27/17 llundbla Fix UsefulBuf_Compare() bug. Only affected comparison
for < or > for unequal length buffers. Added
UsefulBuf_Set() function.
05/30/17 llundbla Functions for NULL UsefulBufs and const / unconst
11/13/16 llundbla Initial Version.
============================================================================*/
#include "UsefulBuf.h"
/* used to catch use of uninitialized or corrupted UsefulOutBuf */
#define USEFUL_OUT_BUF_MAGIC (0x0B0F)
/*
* Public function -- see UsefulBuf.h
*/
UsefulBufC UsefulBuf_CopyOffset(UsefulBuf Dest, size_t uOffset, const UsefulBufC Src)
{
/* Do this with subtraction so it doesn't give an erroneous
* result if uOffset + Src.len overflows. Right side is equivalent to
* uOffset + Src.len > Dest.len
*/
if(uOffset > Dest.len || Src.len > Dest.len - uOffset) {
return NULLUsefulBufC;
}
memcpy((uint8_t *)Dest.ptr + uOffset, Src.ptr, Src.len);
return (UsefulBufC){Dest.ptr, Src.len + uOffset};
}
/*
* Public function -- see UsefulBuf.h
*/
int UsefulBuf_Compare(const UsefulBufC UB1, const UsefulBufC UB2)
{
/* Use comparisons rather than subtracting lengths to
* return an int instead of a size_t
*/
if(UB1.len < UB2.len) {
return -1;
} else if (UB1.len > UB2.len) {
return 1;
} /* else UB1.len == UB2.len */
return memcmp(UB1.ptr, UB2.ptr, UB1.len);
}
/*
* Public function -- see UsefulBuf.h
*/
size_t UsefulBuf_IsValue(const UsefulBufC UB, uint8_t uValue)
{
if(UsefulBuf_IsNULLOrEmptyC(UB)) {
/* Not a match */
return 0;
}
const uint8_t * const pEnd = (const uint8_t *)UB.ptr + UB.len;
for(const uint8_t *p = UB.ptr; p < pEnd; p++) {
if(*p != uValue) {
/* Byte didn't match */
/* Cast from signed to unsigned. Safe because the loop increments.*/
return (size_t)(p - (const uint8_t *)UB.ptr);
}
}
/* Success. All bytes matched */
return SIZE_MAX;
}
/*
* Public function -- see UsefulBuf.h
*/
size_t UsefulBuf_FindBytes(UsefulBufC BytesToSearch, UsefulBufC BytesToFind)
{
if(BytesToSearch.len < BytesToFind.len) {
return SIZE_MAX;
}
for(size_t uPos = 0; uPos <= BytesToSearch.len - BytesToFind.len; uPos++) {
UsefulBufC SearchNext;
SearchNext.ptr = ((const uint8_t *)BytesToSearch.ptr) + uPos;
SearchNext.len = BytesToFind.len;
if(!UsefulBuf_Compare(SearchNext, BytesToFind)) {
return uPos;
}
}
return SIZE_MAX;
}
/*
* Public function -- see UsefulBuf.h
*
* Code Reviewers: THIS FUNCTION DOES POINTER MATH
*/
void UsefulOutBuf_Init(UsefulOutBuf *pMe, UsefulBuf Storage)
{
pMe->magic = USEFUL_OUT_BUF_MAGIC;
UsefulOutBuf_Reset(pMe);
pMe->UB = Storage;
#if 0
/* This check is off by default.
*
* The following check fails on ThreadX
*
* Sanity check on the pointer and size to be sure we are not
* passed a buffer that goes off the end of the address space.
* Given this test, we know that all unsigned lengths less than
* me->size are valid and won't wrap in any pointer additions
* based off of pStorage in the rest of this code.
*/
const uintptr_t ptrM = UINTPTR_MAX - Storage.len;
if(Storage.ptr && (uintptr_t)Storage.ptr > ptrM) /* Check #0 */
me->err = 1;
#endif
}
/*
* Public function -- see UsefulBuf.h
*
* The core of UsefulOutBuf -- put some bytes in the buffer without writing off
* the end of it.
*
* Code Reviewers: THIS FUNCTION DOES POINTER MATH
*
* This function inserts the source buffer, NewData, into the destination
* buffer, me->UB.ptr.
*
* Destination is represented as:
* me->UB.ptr -- start of the buffer
* me->UB.len -- size of the buffer UB.ptr
* me->data_len -- length of value data in UB
*
* Source is data:
* NewData.ptr -- start of source buffer
* NewData.len -- length of source buffer
*
* Insertion point:
* uInsertionPos.
*
* Steps:
*
* 0. Corruption checks on UsefulOutBuf
*
* 1. Figure out if the new data will fit or not
*
* 2. Is insertion position in the range of valid data?
*
* 3. If insertion point is not at the end, slide data to the right of the
* insertion point to the right
*
* 4. Put the new data in at the insertion position.
*
*/
void UsefulOutBuf_InsertUsefulBuf(UsefulOutBuf *pMe, UsefulBufC NewData, size_t uInsertionPos)
{
if(pMe->err) {
/* Already in error state. */
return;
}
/* 0. Sanity check the UsefulOutBuf structure
* A "counter measure". If magic number is not the right number it
* probably means pMe was not initialized or it was corrupted. Attackers
* can defeat this, but it is a hurdle and does good with very
* little code.
*/
if(pMe->magic != USEFUL_OUT_BUF_MAGIC) {
pMe->err = 1;
return; /* Magic number is wrong due to uninitalization or corrption */
}
/* Make sure valid data is less than buffer size. This would only occur
* if there was corruption of me, but it is also part of the checks to
* be sure there is no pointer arithmatic under/overflow.
*/
if(pMe->data_len > pMe->UB.len) { /* Check #1 */
pMe->err = 1;
/* Offset of valid data is off the end of the UsefulOutBuf due to
* uninitialization or corruption
*/
return;
}
/* 1. Will it fit?
* WillItFit() is the same as: NewData.len <= (me->UB.len - me->data_len)
* Check #1 makes sure subtraction in RoomLeft will not wrap around
*/
if(! UsefulOutBuf_WillItFit(pMe, NewData.len)) { /* Check #2 */
/* The new data will not fit into the the buffer. */
pMe->err = 1;
return;
}
/* 2. Check the Insertion Position
* This, with Check #1, also confirms that uInsertionPos <= me->data_len and
* that uInsertionPos + pMe->UB.ptr will not wrap around the end of the
* address space.
*/
if(uInsertionPos > pMe->data_len) { /* Check #3 */
/* Off the end of the valid data in the buffer. */
pMe->err = 1;
return;
}
/* 3. Slide existing data to the right */
if (!UsefulOutBuf_IsBufferNULL(pMe)) {
uint8_t *pSourceOfMove = ((uint8_t *)pMe->UB.ptr) + uInsertionPos; /* PtrMath #1 */
size_t uNumBytesToMove = pMe->data_len - uInsertionPos; /* PtrMath #2 */
uint8_t *pDestinationOfMove = pSourceOfMove + NewData.len; /* PtrMath #3*/
/* To know memmove won't go off end of destination, see PtrMath #4.
* Use memove because it handles overlapping buffers
*/
memmove(pDestinationOfMove, pSourceOfMove, uNumBytesToMove);
/* 4. Put the new data in */
uint8_t *pInsertionPoint = pSourceOfMove;
/* To know memmove won't go off end of destination, see PtrMath #5 */
if(NewData.ptr != NULL) {
memmove(pInsertionPoint, NewData.ptr, NewData.len);
}
}
pMe->data_len += NewData.len;
}
/*
* Rationale that describes why the above pointer math is safe
*
* PtrMath #1 will never wrap around over because
* Check #0 in UsefulOutBuf_Init that me->UB.ptr + me->UB.len doesn't wrap
* Check #1 makes sure me->data_len is less than me->UB.len
* Check #3 makes sure uInsertionPos is less than me->data_len
*
* PtrMath #2 will never wrap around under because
* Check #3 makes sure uInsertionPos is less than me->data_len
*
* PtrMath #3 will never wrap around over because
* PtrMath #1 is checked resulting in pSourceOfMove being between me->UB.ptr and me->UB.ptr + me->data_len
* Check #2 that NewData.len will fit in the unused space left in me->UB
*
* PtrMath #4 will never wrap under because
* Calculation for extent or memmove is uRoomInDestination = me->UB.len - (uInsertionPos + NewData.len)
* Check #3 makes sure uInsertionPos is less than me->data_len
* Check #3 allows Check #2 to be refactored as NewData.Len > (me->size - uInsertionPos)
* This algebraically rearranges to me->size > uInsertionPos + NewData.len
*
* PtrMath #5 will never wrap under because
* Calculation for extent of memove is uRoomInDestination = me->UB.len - uInsertionPos;
* Check #1 makes sure me->data_len is less than me->size
* Check #3 makes sure uInsertionPos is less than me->data_len
*/
/*
* Public function for advancing data length. See qcbor/UsefulBuf.h
*/
void UsefulOutBuf_Advance(UsefulOutBuf *pMe, size_t uAmount)
{
/* This function is a trimmed down version of
* UsefulOutBuf_InsertUsefulBuf(). This could be combined with the
* code in UsefulOutBuf_InsertUsefulBuf(), but that would make
* UsefulOutBuf_InsertUsefulBuf() bigger and this will be very
* rarely used.
*/
if(pMe->err) {
/* Already in error state. */
return;
}
/* 0. Sanity check the UsefulOutBuf structure
*
* A "counter measure". If magic number is not the right number it
* probably means me was not initialized or it was
* corrupted. Attackers can defeat this, but it is a hurdle and
* does good with very little code.
*/
if(pMe->magic != USEFUL_OUT_BUF_MAGIC) {
pMe->err = 1;
return; /* Magic number is wrong due to uninitalization or corrption */
}
/* Make sure valid data is less than buffer size. This would only
* occur if there was corruption of me, but it is also part of the
* checks to be sure there is no pointer arithmatic
* under/overflow.
*/
if(pMe->data_len > pMe->UB.len) { /* Check #1 */
pMe->err = 1;
/* Offset of valid data is off the end of the UsefulOutBuf due
* to uninitialization or corruption.
*/
return;
}
/* 1. Will it fit?
*
* WillItFit() is the same as: NewData.len <= (me->UB.len -
* me->data_len) Check #1 makes sure subtraction in RoomLeft will
* not wrap around
*/
if(! UsefulOutBuf_WillItFit(pMe, uAmount)) { /* Check #2 */
/* The new data will not fit into the the buffer. */
pMe->err = 1;
return;
}
pMe->data_len += uAmount;
}
/*
* Public function -- see UsefulBuf.h
*/
UsefulBufC UsefulOutBuf_OutUBuf(UsefulOutBuf *pMe)
{
if(pMe->err) {
return NULLUsefulBufC;
}
if(pMe->magic != USEFUL_OUT_BUF_MAGIC) {
pMe->err = 1;
return NULLUsefulBufC;
}
return (UsefulBufC){pMe->UB.ptr, pMe->data_len};
}
/*
* Public function -- see UsefulBuf.h
*
* Copy out the data accumulated in to the output buffer.
*/
UsefulBufC UsefulOutBuf_CopyOut(UsefulOutBuf *pMe, UsefulBuf pDest)
{
const UsefulBufC Tmp = UsefulOutBuf_OutUBuf(pMe);
if(UsefulBuf_IsNULLC(Tmp)) {
return NULLUsefulBufC;
}
return UsefulBuf_Copy(pDest, Tmp);
}
/*
* Public function -- see UsefulBuf.h
*
* The core of UsefulInputBuf -- consume bytes without going off end of buffer.
*
* Code Reviewers: THIS FUNCTION DOES POINTER MATH
*/
const void * UsefulInputBuf_GetBytes(UsefulInputBuf *pMe, size_t uAmount)
{
/* Already in error state. Do nothing. */
if(pMe->err) {
return NULL;
}
if(!UsefulInputBuf_BytesAvailable(pMe, uAmount)) {
/* Number of bytes asked for is more than available */
pMe->err = 1;
return NULL;
}
/* This is going to succeed */
const void * const result = ((const uint8_t *)pMe->UB.ptr) + pMe->cursor;
/* Won't overflow because of check using UsefulInputBuf_BytesAvailable() */
pMe->cursor += uAmount;
return result;
}