blob: 662ba4626d06d8338868672dddad464dd049062d [file] [log] [blame]
#ifndef PLDM_MSGBUF_H
#define PLDM_MSGBUF_H
#ifdef __cplusplus
extern "C" {
#endif
#include "base.h"
#include "pldm_types.h"
#include <assert.h>
#include <endian.h>
#include <limits.h>
#include <stdbool.h>
#include <string.h>
#include <sys/types.h>
struct pldm_msgbuf {
const uint8_t *cursor;
ssize_t remaining;
};
/**
* @brief Initialize pldm buf struct for buf extractor
*
* @param[out] ctx - pldm_msgbuf context for extractor
* @param[in] minsize - The minimum required length of buffer `buf`
* @param[in] buf - buffer to be extracted
* @param[in] len - size of buffer
*
* @return PLDM_SUCCESS if all buffer accesses were in-bounds,
* PLDM_ERROR_INVALID_DATA if pointer parameters are invalid, or
* PLDM_ERROR_INVALID_LENGTH if length constraints are violated.
*/
__attribute__((no_sanitize("pointer-overflow"))) static inline int
pldm_msgbuf_init(struct pldm_msgbuf *ctx, size_t minsize, const void *buf,
size_t len)
{
uint8_t *end;
if (!ctx || !buf) {
return PLDM_ERROR_INVALID_DATA;
}
if ((minsize > len) || (len > SSIZE_MAX)) {
return PLDM_ERROR_INVALID_LENGTH;
}
end = (uint8_t *)buf + len;
if (end && end < (uint8_t *)buf) {
return PLDM_ERROR_INVALID_LENGTH;
}
ctx->cursor = (uint8_t *)buf;
ctx->remaining = (ssize_t)len;
return PLDM_SUCCESS;
}
/**
* @brief Validate buffer overflow state
*
* @param[in] ctx - pldm_msgbuf context for extractor
*
* @return PLDM_SUCCESS if there are zero or more bytes of data that remain
* unread from the buffer. Otherwise, PLDM_ERROR_INVALID_LENGTH indicates that a
* prior accesses would have occurred beyond the bounds of the buffer, and
* PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid
* pointer.
*/
static inline int pldm_msgbuf_validate(struct pldm_msgbuf *ctx)
{
if (!ctx) {
return PLDM_ERROR_INVALID_DATA;
}
return ctx->remaining >= 0 ? PLDM_SUCCESS : PLDM_ERROR_INVALID_LENGTH;
}
/**
* @brief Test whether a message buffer has been exactly consumed
*
* @param[in] ctx - pldm_msgbuf context for extractor
*
* @return PLDM_SUCCESS iff there are zero bytes of data that remain unread from
* the buffer and no overflow has occurred. Otherwise, PLDM_ERROR_INVALID_LENGTH
* indicates that an incorrect sequence of accesses have occurred, and
* PLDM_ERROR_INVALID_DATA indicates that the provided context was not a valid
* pointer.
*/
static inline int pldm_msgbuf_consumed(struct pldm_msgbuf *ctx)
{
if (!ctx) {
return PLDM_ERROR_INVALID_DATA;
}
return ctx->remaining == 0 ? PLDM_SUCCESS : PLDM_ERROR_INVALID_LENGTH;
}
/**
* @brief Destroy the pldm buf
*
* @param[in] ctx - pldm_msgbuf context for extractor
*
* @return PLDM_SUCCESS if all buffer accesses were in-bounds,
* PLDM_ERROR_INVALID_DATA if the ctx parameter is invalid, or
* PLDM_ERROR_INVALID_LENGTH if prior accesses would have occurred beyond the
* bounds of the buffer.
*/
static inline int pldm_msgbuf_destroy(struct pldm_msgbuf *ctx)
{
int valid;
if (!ctx) {
return PLDM_ERROR_INVALID_DATA;
}
valid = pldm_msgbuf_validate(ctx);
ctx->cursor = NULL;
ctx->remaining = 0;
return valid;
}
/**
* @brief Destroy the pldm_msgbuf instance, and check that the underlying buffer
* has been completely consumed without overflow
*
* @param[in] ctx - pldm_msgbuf context
*
* @return PLDM_SUCCESS if all buffer access were in-bounds and completely
* consume the underlying buffer. Otherwise, PLDM_ERROR_INVALID_DATA if the ctx
* parameter is invalid, or PLDM_ERROR_INVALID_LENGTH if prior accesses would
* have occurred byond the bounds of the buffer
*/
static inline int pldm_msgbuf_destroy_consumed(struct pldm_msgbuf *ctx)
{
int consumed;
if (!ctx) {
return PLDM_ERROR_INVALID_DATA;
}
consumed = pldm_msgbuf_consumed(ctx);
ctx->cursor = NULL;
ctx->remaining = 0;
return consumed;
}
/**
* @brief pldm_msgbuf extractor for a uint8_t
*
* @param[inout] ctx - pldm_msgbuf context for extractor
* @param[out] dst - destination of extracted value
*
* @return PLDM_SUCCESS if buffer accesses were in-bounds,
* PLDM_ERROR_INVALID_LENGTH otherwise.
* PLDM_ERROR_INVALID_DATA if input a invalid ctx
*/
static inline int pldm_msgbuf_extract_uint8(struct pldm_msgbuf *ctx,
uint8_t *dst)
{
if (!ctx || !ctx->cursor || !dst) {
return PLDM_ERROR_INVALID_DATA;
}
ctx->remaining -= sizeof(*dst);
assert(ctx->remaining >= 0);
if (ctx->remaining < 0) {
return PLDM_ERROR_INVALID_LENGTH;
}
*dst = *((uint8_t *)(ctx->cursor));
ctx->cursor++;
return PLDM_SUCCESS;
}
static inline int pldm_msgbuf_extract_int8(struct pldm_msgbuf *ctx, int8_t *dst)
{
if (!ctx || !ctx->cursor || !dst) {
return PLDM_ERROR_INVALID_DATA;
}
ctx->remaining -= sizeof(*dst);
assert(ctx->remaining >= 0);
if (ctx->remaining < 0) {
return PLDM_ERROR_INVALID_LENGTH;
}
*dst = *((int8_t *)(ctx->cursor));
ctx->cursor++;
return PLDM_SUCCESS;
}
static inline int pldm_msgbuf_extract_uint16(struct pldm_msgbuf *ctx,
uint16_t *dst)
{
uint16_t ldst;
if (!ctx || !ctx->cursor || !dst) {
return PLDM_ERROR_INVALID_DATA;
}
// Check for buffer overflow. If we overflow, account for the request as
// negative values in ctx->remaining. This way we can debug how far
// we've overflowed.
ctx->remaining -= sizeof(ldst);
// Prevent the access if it would overflow. First, assert so we blow up
// the test suite right at the point of failure. However, cater to
// -DNDEBUG by explicitly testing that the access is valid.
assert(ctx->remaining >= 0);
if (ctx->remaining < 0) {
return PLDM_ERROR_INVALID_LENGTH;
}
// Use memcpy() to have the compiler deal with any alignment
// issues on the target architecture
memcpy(&ldst, ctx->cursor, sizeof(ldst));
// Only assign the target value once it's correctly decoded
*dst = le16toh(ldst);
ctx->cursor += sizeof(ldst);
return PLDM_SUCCESS;
}
static inline int pldm_msgbuf_extract_int16(struct pldm_msgbuf *ctx,
int16_t *dst)
{
int16_t ldst;
if (!ctx || !ctx->cursor || !dst) {
return PLDM_ERROR_INVALID_DATA;
}
ctx->remaining -= sizeof(ldst);
assert(ctx->remaining >= 0);
if (ctx->remaining < 0) {
return PLDM_ERROR_INVALID_LENGTH;
}
memcpy(&ldst, ctx->cursor, sizeof(ldst));
*dst = le16toh(ldst);
ctx->cursor += sizeof(ldst);
return PLDM_SUCCESS;
}
static inline int pldm_msgbuf_extract_uint32(struct pldm_msgbuf *ctx,
uint32_t *dst)
{
uint32_t ldst;
if (!ctx || !ctx->cursor || !dst) {
return PLDM_ERROR_INVALID_DATA;
}
ctx->remaining -= sizeof(ldst);
assert(ctx->remaining >= 0);
if (ctx->remaining < 0) {
return PLDM_ERROR_INVALID_LENGTH;
}
memcpy(&ldst, ctx->cursor, sizeof(ldst));
*dst = le32toh(ldst);
ctx->cursor += sizeof(ldst);
return PLDM_SUCCESS;
}
static inline int pldm_msgbuf_extract_int32(struct pldm_msgbuf *ctx,
int32_t *dst)
{
int32_t ldst;
if (!ctx || !ctx->cursor || !dst) {
return PLDM_ERROR_INVALID_DATA;
}
ctx->remaining -= sizeof(ldst);
assert(ctx->remaining >= 0);
if (ctx->remaining < 0) {
return PLDM_ERROR_INVALID_LENGTH;
}
memcpy(&ldst, ctx->cursor, sizeof(ldst));
*dst = le32toh(ldst);
ctx->cursor += sizeof(ldst);
return PLDM_SUCCESS;
}
static inline int pldm_msgbuf_extract_real32(struct pldm_msgbuf *ctx,
real32_t *dst)
{
uint32_t ldst;
if (!ctx || !ctx->cursor || !dst) {
return PLDM_ERROR_INVALID_DATA;
}
ctx->remaining -= sizeof(ldst);
assert(ctx->remaining >= 0);
if (ctx->remaining < 0) {
return PLDM_ERROR_INVALID_LENGTH;
}
_Static_assert(sizeof(*dst) == sizeof(ldst),
"Mismatched type sizes for dst and ldst");
memcpy(&ldst, ctx->cursor, sizeof(ldst));
ldst = le32toh(ldst);
memcpy(dst, &ldst, sizeof(*dst));
ctx->cursor += sizeof(*dst);
return PLDM_SUCCESS;
}
#define pldm_msgbuf_extract(ctx, dst) \
_Generic((*(dst)), uint8_t \
: pldm_msgbuf_extract_uint8, int8_t \
: pldm_msgbuf_extract_int8, uint16_t \
: pldm_msgbuf_extract_uint16, int16_t \
: pldm_msgbuf_extract_int16, uint32_t \
: pldm_msgbuf_extract_uint32, int32_t \
: pldm_msgbuf_extract_int32, real32_t \
: pldm_msgbuf_extract_real32)(ctx, dst)
static inline int pldm_msgbuf_extract_array_uint8(struct pldm_msgbuf *ctx,
uint8_t *dst, size_t count)
{
size_t len;
if (!ctx || !ctx->cursor || !dst) {
return PLDM_ERROR_INVALID_DATA;
}
if (!count) {
return PLDM_SUCCESS;
}
len = sizeof(*dst) * count;
if (len > SSIZE_MAX) {
return PLDM_ERROR_INVALID_LENGTH;
}
ctx->remaining -= (ssize_t)len;
assert(ctx->remaining >= 0);
if (ctx->remaining < 0) {
return PLDM_ERROR_INVALID_LENGTH;
}
memcpy(dst, ctx->cursor, len);
ctx->cursor += len;
return PLDM_SUCCESS;
}
#define pldm_msgbuf_extract_array(ctx, dst, count) \
_Generic((*(dst)), uint8_t \
: pldm_msgbuf_extract_array_uint8)(ctx, dst, count)
#ifdef __cplusplus
}
#endif
#endif /* BUF_H */