blob: c9527e178a66ad5d8caa4961809680065feb8261 [file] [log] [blame]
/* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */
#ifndef PLDM_MSGBUF_H
#define PLDM_MSGBUF_H
#ifdef __cplusplus
/*
* Fix up C11's _Static_assert() vs C++'s static_assert().
*
* Can we please have nice things for once.
*/
// NOLINTNEXTLINE(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
#define _Static_assert(...) static_assert(__VA_ARGS__)
extern "C" {
#endif
#include <libpldm/base.h>
#include <libpldm/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 {
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)
static inline int pldm_msgbuf_insert_uint32(struct pldm_msgbuf *ctx,
const uint32_t src)
{
uint32_t val = htole32(src);
if (!ctx || !ctx->cursor) {
return PLDM_ERROR_INVALID_DATA;
}
ctx->remaining -= sizeof(src);
assert(ctx->remaining >= 0);
if (ctx->remaining < 0) {
return PLDM_ERROR_INVALID_LENGTH;
}
memcpy(ctx->cursor, &val, sizeof(val));
ctx->cursor += sizeof(src);
return PLDM_SUCCESS;
}
static inline int pldm_msgbuf_insert_uint16(struct pldm_msgbuf *ctx,
const uint16_t src)
{
uint16_t val = htole16(src);
if (!ctx || !ctx->cursor) {
return PLDM_ERROR_INVALID_DATA;
}
ctx->remaining -= sizeof(src);
assert(ctx->remaining >= 0);
if (ctx->remaining < 0) {
return PLDM_ERROR_INVALID_LENGTH;
}
memcpy(ctx->cursor, &val, sizeof(val));
ctx->cursor += sizeof(src);
return PLDM_SUCCESS;
}
static inline int pldm_msgbuf_insert_uint8(struct pldm_msgbuf *ctx,
const uint8_t src)
{
if (!ctx || !ctx->cursor) {
return PLDM_ERROR_INVALID_DATA;
}
ctx->remaining -= sizeof(src);
assert(ctx->remaining >= 0);
if (ctx->remaining < 0) {
return PLDM_ERROR_INVALID_LENGTH;
}
memcpy(ctx->cursor, &src, sizeof(src));
ctx->cursor += sizeof(src);
return PLDM_SUCCESS;
}
static inline int pldm_msgbuf_insert_int32(struct pldm_msgbuf *ctx,
const int32_t src)
{
int32_t val = htole32(src);
if (!ctx || !ctx->cursor) {
return PLDM_ERROR_INVALID_DATA;
}
ctx->remaining -= sizeof(src);
assert(ctx->remaining >= 0);
if (ctx->remaining < 0) {
return PLDM_ERROR_INVALID_LENGTH;
}
memcpy(ctx->cursor, &val, sizeof(val));
ctx->cursor += sizeof(src);
return PLDM_SUCCESS;
}
static inline int pldm_msgbuf_insert_int16(struct pldm_msgbuf *ctx,
const int16_t src)
{
int16_t val = htole16(src);
if (!ctx || !ctx->cursor) {
return PLDM_ERROR_INVALID_DATA;
}
ctx->remaining -= sizeof(src);
assert(ctx->remaining >= 0);
if (ctx->remaining < 0) {
return PLDM_ERROR_INVALID_LENGTH;
}
memcpy(ctx->cursor, &val, sizeof(val));
ctx->cursor += sizeof(src);
return PLDM_SUCCESS;
}
static inline int pldm_msgbuf_insert_int8(struct pldm_msgbuf *ctx,
const int8_t src)
{
if (!ctx || !ctx->cursor) {
return PLDM_ERROR_INVALID_DATA;
}
ctx->remaining -= sizeof(src);
assert(ctx->remaining >= 0);
if (ctx->remaining < 0) {
return PLDM_ERROR_INVALID_LENGTH;
}
memcpy(ctx->cursor, &src, sizeof(src));
ctx->cursor += sizeof(src);
return PLDM_SUCCESS;
}
#define pldm_msgbuf_insert(dst, src) \
_Generic((src), \
uint8_t: pldm_msgbuf_insert_uint8, \
int8_t: pldm_msgbuf_insert_int8, \
uint16_t: pldm_msgbuf_insert_uint16, \
int16_t: pldm_msgbuf_insert_int16, \
uint32_t: pldm_msgbuf_insert_uint32, \
int32_t: pldm_msgbuf_insert_int32)(dst, src)
static inline int pldm_msgbuf_insert_array_uint8(struct pldm_msgbuf *ctx,
const uint8_t *src,
size_t count)
{
size_t len;
if (!ctx || !ctx->cursor || !src) {
return PLDM_ERROR_INVALID_DATA;
}
if (!count) {
return PLDM_SUCCESS;
}
len = sizeof(*src) * 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(ctx->cursor, src, len);
ctx->cursor += len;
return PLDM_SUCCESS;
}
#define pldm_msgbuf_insert_array(dst, src, count) \
_Generic((*(src)), uint8_t: pldm_msgbuf_insert_array_uint8)(dst, src, \
count)
static inline int pldm_msgbuf_span_required(struct pldm_msgbuf *ctx,
size_t required, void **cursor)
{
if (!ctx || !ctx->cursor || !cursor || *cursor) {
return PLDM_ERROR_INVALID_DATA;
}
if (required > SSIZE_MAX) {
return PLDM_ERROR_INVALID_LENGTH;
}
ctx->remaining -= (ssize_t)required;
assert(ctx->remaining >= 0);
if (ctx->remaining < 0) {
return PLDM_ERROR_INVALID_LENGTH;
}
*cursor = ctx->cursor;
ctx->cursor += required;
return PLDM_SUCCESS;
}
static inline int pldm_msgbuf_span_remaining(struct pldm_msgbuf *ctx,
void **cursor, size_t *len)
{
if (!ctx || !ctx->cursor || !cursor || *cursor || !len) {
return PLDM_ERROR_INVALID_DATA;
}
assert(ctx->remaining >= 0);
if (ctx->remaining < 0) {
return PLDM_ERROR_INVALID_LENGTH;
}
*cursor = ctx->cursor;
ctx->cursor += ctx->remaining;
*len = ctx->remaining;
ctx->remaining = 0;
return PLDM_SUCCESS;
}
#ifdef __cplusplus
}
#endif
#endif /* BUF_H */