pipewire/src/modules/module-avb/aecp-aem-cmds-resps/cmd-get-set-configuration.c
Wim Taymans 710414730d security: validate packet length in AVB AECP AEM command handlers
Memory Safety: High

Multiple AVB AECP AEM command handler functions copied network packet
data into stack buffers via memcpy(buf, m, len) without validating
that len fits within the destination buffer. A crafted AVB packet with
an oversized length could overflow the stack buffer.

Added bounds validation before each memcpy in:
- cmd-available.c: handle_cmd_entity_available_milan_v12
- cmd-get-set-configuration.c: set and get configuration handlers
- cmd-get-set-sampling-rate.c: unsolicited, invalid response, and get handlers
- cmd-get-set-stream-format.c: get and set stream format handlers
- cmd-lock-entity.c: handle_cmd_lock_entity_milan_v12

This matches the bounds checking pattern already used in
cmd-get-set-control.c, cmd-get-set-clock-source.c, and
cmd-get-set-name.c.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-27 11:35:41 +02:00

202 lines
5.8 KiB
C

#include <stdint.h>
#include <stdbool.h>
#include "../aecp-aem.h"
#include "../aecp-aem-state.h"
#include "../aecp-aem-descriptors.h"
#include "../aecp-aem-types.h"
#include "cmd-get-set-configuration.h"
#include "cmd-resp-helpers.h"
#if 0
static int handle_unsol_set_configuration_milan_v12(struct aecp *aecp, struct descriptor *desc,
uint64_t ctrler_id)
{
/* Reply */
uint8_t buf[512];
void *m = buf;
struct avb_aem_desc_entity *entity_desc;
struct avb_ethernet_header *h = m;
struct avb_packet_aecp_aem *p = SPA_PTROFF(h, sizeof(*h), void);
struct avb_packet_aecp_aem_setget_configuration *cfg;
size_t len = sizeof (*h) + sizeof(*p) + sizeof(*cfg);
int rc;
memset(buf, 0, sizeof(buf));
entity_desc = (struct avb_aem_desc_entity*) desc->ptr;
cfg = (struct avb_packet_aecp_aem_setget_configuration *) p->payload;
cfg->configuration_index = htons(entity_desc->current_configuration);
p->aecp.target_guid = htobe64(aecp->server->entity_id);
AVB_PACKET_AEM_SET_COMMAND_TYPE(p, AVB_AECP_AEM_CMD_SET_CONFIGURATION);
rc = reply_unsolicited_notifications_ctrler_id(aecp, ctrler_id,
buf, len, false);
if (rc) {
pw_log_error("unsol notif failed");
}
return rc;
}
#endif
/**
* Common handler for SET_CONFIGURATION command
*
* Milan v1.2, Sec. 5.4.2.5
* IEEE 1722.1-2021, Sec. 7.4.7
*/
int handle_cmd_set_configuration_milan_v12(struct aecp *aecp, int64_t now,
const void *m, int len)
{
uint8_t buf[2048];
struct server *server = aecp->server;
const struct avb_ethernet_header *h = m;
const struct avb_packet_aecp_aem *p = SPA_PTROFF(h, sizeof(*h), void);
/* Reply */
struct avb_ethernet_header *h_reply;
struct avb_packet_aecp_aem *p_reply;
struct avb_packet_aecp_aem_setget_configuration *cfg;
/* Information about the current entity */
struct avb_aem_desc_entity *entity_desc;
uint16_t req_cfg_id, cur_cfg_id, cfg_count;
struct descriptor *desc;
int rc;
bool has_failed;
/* FIXME ACMP: IMPORTANT!!!! find the stream connection information
* whether they are running or not. */
/* Milan v1.2, Sec. 5.4.2.5
* The PAAD-AE shall not accept a SET_CONFIGURATION command if one of
* the Stream Input is bound or one of the Stream Output is streaming.
* In this case, the STREAM_IS_RUNNING error code shall be returned.
*
* If the PAAD-AE is locked by a controller, it shall not accept a
* SET_CONFIGURATION command from a different controller, and it shall
* also not change its current configuration by non-ATDECC
* means (proprietary remote control software, front-panel, ...).
*/
/** WARNING! Milan forces only one entity */
desc = server_find_descriptor(server, AVB_AEM_DESC_ENTITY, 0);
if (desc == NULL)
return reply_status(aecp,
AVB_AECP_AEM_STATUS_NO_SUCH_DESCRIPTOR, p, len);
if (len < 0 || (size_t)len > sizeof(buf))
return reply_status(aecp,
AVB_AECP_AEM_STATUS_BAD_ARGUMENTS, p, len);
// TODO maybe avoid copy here
memcpy(buf, m, len);
h_reply = (struct avb_ethernet_header *)buf;
p_reply = SPA_PTROFF(h_reply, sizeof(*h_reply), void);
cfg = (struct avb_packet_aecp_aem_setget_configuration*) p_reply->payload;
entity_desc = (struct avb_aem_desc_entity*) desc->ptr;
cur_cfg_id = ntohs(entity_desc->current_configuration);
req_cfg_id = ntohs(cfg->configuration_index);
cfg_count = ntohs(entity_desc->configurations_count);
if (entity_desc->entity_id != p->aecp.target_guid) {
pw_log_error("Invalid entity id");
has_failed = true;
/* TODO: req_cfg_id is zero based, cfg_count is not.
* Should be req_cfg_id >= cfg_count */
} else if (req_cfg_id >= cfg_count) {
pw_log_error("Requested %u, but has max %u id",
req_cfg_id, cfg_count);
has_failed = true;
} else if (req_cfg_id == cur_cfg_id) {
pw_log_warn("requested %u and same current %u id", req_cfg_id,
cur_cfg_id);
has_failed = true;
} else {
entity_desc->current_configuration = cfg->configuration_index;
has_failed = false;
}
/*
* Always contains the current value,
* that is itcontains the new value if the command succeeds or the old
* value if it fails.
*/
if (has_failed) {
cfg->configuration_index = entity_desc->current_configuration;
}
rc = reply_success(aecp, buf, len);
if (rc) {
pw_log_error("Reply Failed");
return rc;
}
#if 0
if(!has_failed) {
return handle_unsol_set_configuration_milan_v12(aecp, desc,
tobe64(p->aecp.controller_guid));
}
#endif
return 0;
}
/**
* Common handler for GET_CONFIGURATION command
* Milan v1.2, Sec. 5.4.2.6
* IEEE 1722.1-2021, Sec. 7.4.8
*/
int handle_cmd_get_configuration_common(struct aecp *aecp, int64_t now,
const void *m, int len)
{
uint8_t buf[2048];
struct server *server = aecp->server;
const struct avb_ethernet_header *h = m;
const struct avb_packet_aecp_aem *p = SPA_PTROFF(h, sizeof(*h), void);
/* Reply */
struct avb_ethernet_header *h_reply;
struct avb_packet_aecp_aem *p_reply;
struct avb_packet_aecp_aem_setget_configuration *cfg;
/* Information about the current entity */
struct avb_aem_desc_entity *entity_desc;
struct descriptor *desc;
int rc;
desc = server_find_descriptor(server, AVB_AEM_DESC_ENTITY, 0);
if (desc == NULL)
return reply_status(aecp,
AVB_AECP_AEM_STATUS_NO_SUCH_DESCRIPTOR, p, len);
if (len < 0 || (size_t)len > sizeof(buf))
return reply_status(aecp,
AVB_AECP_AEM_STATUS_BAD_ARGUMENTS, p, len);
memcpy(buf, m, len);
h_reply = (struct avb_ethernet_header *)buf;
p_reply = SPA_PTROFF(h_reply, sizeof(*h_reply), void);
cfg = (struct avb_packet_aecp_aem_setget_configuration*) p_reply->payload;
entity_desc = (struct avb_aem_desc_entity*) desc->ptr;
if (entity_desc->entity_id != p->aecp.target_guid) {
pw_log_error("Invalid entity id");
return reply_status(aecp,
AVB_AECP_AEM_STATUS_NO_SUCH_DESCRIPTOR, p, len);
}
cfg->configuration_index = entity_desc->current_configuration;
rc = reply_success(aecp, buf, len);
if (rc) {
pw_log_error("Reply Failed");
return rc;
}
return 0;
}