Bluetooth: audio: ascs: Move metadata validation to stack

As the metadata has to be validated anyway, let the initial vaidation of
metadata to be done in ASCS. The application can still reject the
metadata, but the length validation and supported type validation can be
performed by the stack.

Signed-off-by: Mariusz Skamra <mariusz.skamra@codecoup.pl>
This commit is contained in:
Mariusz Skamra 2023-08-01 15:49:19 +02:00 committed by Carles Cufí
commit d28f180473
10 changed files with 102 additions and 308 deletions

View file

@ -173,6 +173,14 @@ enum bt_audio_metadata_type {
BT_AUDIO_METADATA_TYPE_VENDOR = 0xFF,
};
/**
* Helper to check whether metadata type is known by the stack.
*/
#define BT_AUDIO_METADATA_TYPE_IS_KNOWN(_type) \
(IN_RANGE(_type, BT_AUDIO_METADATA_TYPE_PREF_CONTEXT, \
BT_AUDIO_METADATA_TYPE_BROADCAST_IMMEDIATE) || \
IN_RANGE(_type, BT_AUDIO_METADATA_TYPE_EXTENDED, BT_AUDIO_METADATA_TYPE_VENDOR))
/* Unicast Announcement Type, Generic Audio */
#define BT_AUDIO_UNICAST_ANNOUNCEMENT_GENERAL 0x00
#define BT_AUDIO_UNICAST_ANNOUNCEMENT_TARGETED 0x01

View file

@ -264,45 +264,6 @@ static int lc3_start(struct bt_bap_stream *stream, struct bt_bap_ascs_rsp *rsp)
return 0;
}
static bool valid_metadata_type(uint8_t type, uint8_t len)
{
switch (type) {
case BT_AUDIO_METADATA_TYPE_PREF_CONTEXT:
case BT_AUDIO_METADATA_TYPE_STREAM_CONTEXT:
if (len != 2) {
return false;
}
return true;
case BT_AUDIO_METADATA_TYPE_STREAM_LANG:
if (len != 3) {
return false;
}
return true;
case BT_AUDIO_METADATA_TYPE_PARENTAL_RATING:
if (len != 1) {
return false;
}
return true;
case BT_AUDIO_METADATA_TYPE_EXTENDED: /* 2 - 255 octets */
case BT_AUDIO_METADATA_TYPE_VENDOR: /* 2 - 255 octets */
/* At least Extended Metadata Type / Company_ID should be there */
if (len < 2) {
return false;
}
return true;
case BT_AUDIO_METADATA_TYPE_CCID_LIST:
case BT_AUDIO_METADATA_TYPE_PROGRAM_INFO: /* 0 - 255 octets */
case BT_AUDIO_METADATA_TYPE_PROGRAM_INFO_URI: /* 0 - 255 octets */
return true;
default:
return false;
}
}
static int lc3_metadata(struct bt_bap_stream *stream, const struct bt_audio_codec_data *meta,
size_t meta_count, struct bt_bap_ascs_rsp *rsp)
{
@ -311,7 +272,7 @@ static int lc3_metadata(struct bt_bap_stream *stream, const struct bt_audio_code
for (size_t i = 0; i < meta_count; i++) {
const struct bt_audio_codec_data *data = &meta[i];
if (!valid_metadata_type(data->data.type, data->data.data_len)) {
if (!BT_AUDIO_METADATA_TYPE_IS_KNOWN(data->data.type)) {
printk("Invalid metadata type %u or length %u\n",
data->data.type, data->data.data_len);
*rsp = BT_BAP_ASCS_RSP(BT_BAP_ASCS_RSP_CODE_METADATA_REJECTED,

View file

@ -173,45 +173,6 @@ static int lc3_start(struct bt_bap_stream *stream, struct bt_bap_ascs_rsp *rsp)
return 0;
}
static bool valid_metadata_type(uint8_t type, uint8_t len)
{
switch (type) {
case BT_AUDIO_METADATA_TYPE_PREF_CONTEXT:
case BT_AUDIO_METADATA_TYPE_STREAM_CONTEXT:
if (len != 2) {
return false;
}
return true;
case BT_AUDIO_METADATA_TYPE_STREAM_LANG:
if (len != 3) {
return false;
}
return true;
case BT_AUDIO_METADATA_TYPE_PARENTAL_RATING:
if (len != 1) {
return false;
}
return true;
case BT_AUDIO_METADATA_TYPE_EXTENDED: /* 2 - 255 octets */
case BT_AUDIO_METADATA_TYPE_VENDOR: /* 2 - 255 octets */
/* At least Extended Metadata Type / Company_ID should be there */
if (len < 2) {
return false;
}
return true;
case BT_AUDIO_METADATA_TYPE_CCID_LIST:
case BT_AUDIO_METADATA_TYPE_PROGRAM_INFO: /* 0 - 255 octets */
case BT_AUDIO_METADATA_TYPE_PROGRAM_INFO_URI: /* 0 - 255 octets */
return true;
default:
return false;
}
}
static int lc3_metadata(struct bt_bap_stream *stream, const struct bt_audio_codec_data *meta,
size_t meta_count, struct bt_bap_ascs_rsp *rsp)
{
@ -221,7 +182,7 @@ static int lc3_metadata(struct bt_bap_stream *stream, const struct bt_audio_code
for (size_t i = 0; i < meta_count; i++) {
const struct bt_audio_codec_data *data = &meta[i];
if (!valid_metadata_type(data->data.type, data->data.data_len)) {
if (!BT_AUDIO_METADATA_TYPE_IS_KNOWN(data->data.type)) {
printk("Invalid metadata type %u or length %u\n",
data->data.type, data->data.data_len);
*rsp = BT_BAP_ASCS_RSP(BT_BAP_ASCS_RSP_CODE_METADATA_REJECTED,

View file

@ -408,45 +408,6 @@ static int lc3_start(struct bt_bap_stream *stream, struct bt_bap_ascs_rsp *rsp)
return 0;
}
static bool valid_metadata_type(uint8_t type, uint8_t len)
{
switch (type) {
case BT_AUDIO_METADATA_TYPE_PREF_CONTEXT:
case BT_AUDIO_METADATA_TYPE_STREAM_CONTEXT:
if (len != 2) {
return false;
}
return true;
case BT_AUDIO_METADATA_TYPE_STREAM_LANG:
if (len != 3) {
return false;
}
return true;
case BT_AUDIO_METADATA_TYPE_PARENTAL_RATING:
if (len != 1) {
return false;
}
return true;
case BT_AUDIO_METADATA_TYPE_EXTENDED: /* 2 - 255 octets */
case BT_AUDIO_METADATA_TYPE_VENDOR: /* 2 - 255 octets */
/* At least Extended Metadata Type / Company_ID should be there */
if (len < 2) {
return false;
}
return true;
case BT_AUDIO_METADATA_TYPE_CCID_LIST:
case BT_AUDIO_METADATA_TYPE_PROGRAM_INFO: /* 0 - 255 octets */
case BT_AUDIO_METADATA_TYPE_PROGRAM_INFO_URI: /* 0 - 255 octets */
return true;
default:
return false;
}
}
static int lc3_metadata(struct bt_bap_stream *stream, const struct bt_audio_codec_data *meta,
size_t meta_count, struct bt_bap_ascs_rsp *rsp)
{
@ -455,7 +416,7 @@ static int lc3_metadata(struct bt_bap_stream *stream, const struct bt_audio_code
for (size_t i = 0; i < meta_count; i++) {
const struct bt_audio_codec_data *data = &meta[i];
if (!valid_metadata_type(data->data.type, data->data.data_len)) {
if (!BT_AUDIO_METADATA_TYPE_IS_KNOWN(data->data.type)) {
printk("Invalid metadata type %u or length %u\n",
data->data.type, data->data.data_len);
*rsp = BT_BAP_ASCS_RSP(BT_BAP_ASCS_RSP_CODE_METADATA_REJECTED,

View file

@ -2038,30 +2038,62 @@ static bool ascs_parse_metadata(struct bt_data *data, void *user_data)
return false;
}
/* The CAP acceptor shall not accept metadata with
* unsupported stream context.
*/
if (IS_ENABLED(CONFIG_BT_CAP_ACCEPTOR)) {
if (data_type == BT_AUDIO_METADATA_TYPE_STREAM_CONTEXT) {
const uint16_t context = sys_get_le16(data_value);
if (!BT_AUDIO_METADATA_TYPE_IS_KNOWN(data_type)) {
LOG_WRN("Unknown metadata type 0x%02x", data_type);
return true;
}
switch (data_type) {
/* TODO: Consider rejecting BT_AUDIO_METADATA_TYPE_PREF_CONTEXT type */
case BT_AUDIO_METADATA_TYPE_PREF_CONTEXT:
case BT_AUDIO_METADATA_TYPE_STREAM_CONTEXT: {
uint16_t context;
if (data_len != sizeof(context)) {
*result->rsp = BT_BAP_ASCS_RSP(BT_BAP_ASCS_RSP_CODE_METADATA_INVALID,
data_type);
result->err = -EBADMSG;
return false;
}
context = sys_get_le16(data_value);
if (context == BT_AUDIO_CONTEXT_TYPE_PROHIBITED) {
*result->rsp = BT_BAP_ASCS_RSP(BT_BAP_ASCS_RSP_CODE_METADATA_INVALID,
data_type);
result->err = -EINVAL;
return false;
}
/* The CAP acceptor shall not accept metadata with unsupported stream context. */
if (IS_ENABLED(CONFIG_BT_CAP_ACCEPTOR) &&
data_type == BT_AUDIO_METADATA_TYPE_STREAM_CONTEXT) {
if (!bt_pacs_context_available(ep->dir, context)) {
LOG_WRN("Context 0x%04x is unavailable", context);
*result->rsp = BT_BAP_ASCS_RSP(
BT_BAP_ASCS_RSP_CODE_METADATA_REJECTED, data_type);
result->err = -EACCES;
return false;
}
} else if (data_type == BT_AUDIO_METADATA_TYPE_CCID_LIST) {
/* Verify that the CCID is a known CCID on the
* writing device
*/
}
break;
}
case BT_AUDIO_METADATA_TYPE_STREAM_LANG:
if (data_len != 3) {
*result->rsp = BT_BAP_ASCS_RSP(BT_BAP_ASCS_RSP_CODE_METADATA_INVALID,
data_type);
result->err = -EBADMSG;
return false;
}
break;
case BT_AUDIO_METADATA_TYPE_CCID_LIST: {
/* Verify that the CCID is a known CCID on the writing device */
if (IS_ENABLED(CONFIG_BT_CAP_ACCEPTOR)) {
for (uint8_t i = 0; i < data_len; i++) {
const uint8_t ccid = data_value[i];
if (!bt_cap_acceptor_ccid_exist(ep->stream->conn,
ccid)) {
if (!bt_cap_acceptor_ccid_exist(ep->stream->conn, ccid)) {
LOG_WRN("CCID %u is unknown", ccid);
/* TBD:
@ -2076,6 +2108,42 @@ static bool ascs_parse_metadata(struct bt_data *data, void *user_data)
}
}
}
break;
}
case BT_AUDIO_METADATA_TYPE_PARENTAL_RATING:
if (data_len != 1) {
*result->rsp = BT_BAP_ASCS_RSP(BT_BAP_ASCS_RSP_CODE_METADATA_INVALID,
data_type);
result->err = -EBADMSG;
return false;
}
break;
case BT_AUDIO_METADATA_TYPE_AUDIO_STATE: {
uint8_t state;
if (data_len != sizeof(state)) {
*result->rsp = BT_BAP_ASCS_RSP(BT_BAP_ASCS_RSP_CODE_METADATA_INVALID,
data_type);
result->err = -EBADMSG;
return false;
}
break;
}
/* TODO: Consider rejecting BT_AUDIO_METADATA_TYPE_BROADCAST_IMMEDIATE type */
case BT_AUDIO_METADATA_TYPE_BROADCAST_IMMEDIATE:
if (data_len != 0) {
*result->rsp = BT_BAP_ASCS_RSP(BT_BAP_ASCS_RSP_CODE_METADATA_INVALID,
data_type);
result->err = -EBADMSG;
return false;
}
break;
default:
break;
}
return true;
@ -2098,22 +2166,6 @@ static int ascs_verify_metadata(const struct net_buf_simple *buf, struct bt_bap_
/* Parse LTV entries */
bt_data_parse(&meta_ltv, ascs_parse_metadata, &result);
/* Check if all entries could be parsed */
if (meta_ltv.len != 0) {
LOG_ERR("Unable to parse Metadata: len %u", meta_ltv.len);
if (meta_ltv.len > 2) {
/* Value of the Metadata Type field in error */
*rsp = BT_BAP_ASCS_RSP(BT_BAP_ASCS_RSP_CODE_METADATA_INVALID,
meta_ltv.data[2]);
return meta_ltv.data[2];
}
*rsp = BT_BAP_ASCS_RSP(BT_BAP_ASCS_RSP_CODE_METADATA_INVALID,
BT_BAP_ASCS_REASON_NONE);
return -EINVAL;
}
return result.err;
}

View file

@ -54,10 +54,6 @@ static bool cap_initiator_valid_metadata(const struct bt_audio_codec_data meta[]
bt_hex(metadata->data, metadata->data_len));
if (metadata->type == BT_AUDIO_METADATA_TYPE_STREAM_CONTEXT) {
if (metadata->data_len != 2) { /* Stream context size */
return false;
}
stream_context_found = true;
break;
}

View file

@ -522,46 +522,6 @@ static int lc3_start(struct bt_bap_stream *stream, struct bt_bap_ascs_rsp *rsp)
return 0;
}
static bool valid_metadata_type(uint8_t type, uint8_t len)
{
switch (type) {
case BT_AUDIO_METADATA_TYPE_PREF_CONTEXT:
case BT_AUDIO_METADATA_TYPE_STREAM_CONTEXT:
if (len != 2) {
return false;
}
return true;
case BT_AUDIO_METADATA_TYPE_STREAM_LANG:
if (len != 3) {
return false;
}
return true;
case BT_AUDIO_METADATA_TYPE_PARENTAL_RATING:
if (len != 1) {
return false;
}
return true;
case BT_AUDIO_METADATA_TYPE_EXTENDED: /* 2 - 255 octets */
case BT_AUDIO_METADATA_TYPE_VENDOR: /* 2 - 255 octets */
/* At least Extended Metadata Type / Company_ID should be there */
if (len < 2) {
return false;
}
return true;
case BT_AUDIO_METADATA_TYPE_CCID_LIST:
case BT_AUDIO_METADATA_TYPE_PROGRAM_INFO: /* 0 - 255 octets */
case BT_AUDIO_METADATA_TYPE_PROGRAM_INFO_URI: /* 0 - 255 octets */
return true;
default:
return false;
}
}
static int lc3_metadata(struct bt_bap_stream *stream, const struct bt_audio_codec_data *meta,
size_t meta_count, struct bt_bap_ascs_rsp *rsp)
{
@ -571,7 +531,7 @@ static int lc3_metadata(struct bt_bap_stream *stream, const struct bt_audio_code
for (size_t i = 0; i < meta_count; i++) {
const struct bt_audio_codec_data *data = &meta[i];
if (!valid_metadata_type(data->data.type, data->data.data_len)) {
if (!BT_AUDIO_METADATA_TYPE_IS_KNOWN(data->data.type)) {
shell_print(ctx_shell,
"Invalid metadata type %u or length %u",
data->data.type, data->data.data_len);

View file

@ -374,46 +374,19 @@ static bool valid_metadata_type(uint8_t type, uint8_t len, const uint8_t *data)
/* PTS checks if we are able to reject unsupported metadata type or RFU vale.
* The only RFU value PTS seems to check for now is the streaming context.
*/
switch (type) {
case BT_AUDIO_METADATA_TYPE_PREF_CONTEXT:
case BT_AUDIO_METADATA_TYPE_STREAM_CONTEXT:
if (len != 2) {
return false;
}
if (!BT_AUDIO_METADATA_TYPE_IS_KNOWN(type)) {
return false;
}
if (type == BT_AUDIO_METADATA_TYPE_PREF_CONTEXT ||
type == BT_AUDIO_METADATA_TYPE_STREAM_CONTEXT) {
/* PTS wants us to reject the parameter if reserved bits are set */
if ((sys_get_le16(data) & ~(uint16_t)(BT_AUDIO_CONTEXT_TYPE_ANY)) > 0) {
return false;
}
return true;
case BT_AUDIO_METADATA_TYPE_STREAM_LANG:
if (len != 3) {
return false;
}
return true;
case BT_AUDIO_METADATA_TYPE_PARENTAL_RATING:
if (len != 1) {
return false;
}
return true;
case BT_AUDIO_METADATA_TYPE_EXTENDED: /* 2 - 255 octets */
case BT_AUDIO_METADATA_TYPE_VENDOR: /* 2 - 255 octets */
/* At least Extended Metadata Type / Company_ID should be there */
if (len < 2) {
return false;
}
return true;
case BT_AUDIO_METADATA_TYPE_CCID_LIST:
case BT_AUDIO_METADATA_TYPE_PROGRAM_INFO: /* 0 - 255 octets */
case BT_AUDIO_METADATA_TYPE_PROGRAM_INFO_URI: /* 0 - 255 octets */
return true;
default:
return false;
}
return true;
}
static int lc3_metadata(struct bt_bap_stream *stream, const struct bt_audio_codec_data *meta,

View file

@ -158,45 +158,6 @@ static int lc3_start(struct bt_bap_stream *stream, struct bt_bap_ascs_rsp *rsp)
return 0;
}
static bool valid_metadata_type(uint8_t type, uint8_t len)
{
switch (type) {
case BT_AUDIO_METADATA_TYPE_PREF_CONTEXT:
case BT_AUDIO_METADATA_TYPE_STREAM_CONTEXT:
if (len != 2) {
return false;
}
return true;
case BT_AUDIO_METADATA_TYPE_STREAM_LANG:
if (len != 3) {
return false;
}
return true;
case BT_AUDIO_METADATA_TYPE_PARENTAL_RATING:
if (len != 1) {
return false;
}
return true;
case BT_AUDIO_METADATA_TYPE_EXTENDED: /* 2 - 255 octets */
case BT_AUDIO_METADATA_TYPE_VENDOR: /* 2 - 255 octets */
/* At least Extended Metadata Type / Company_ID should be there */
if (len < 2) {
return false;
}
return true;
case BT_AUDIO_METADATA_TYPE_CCID_LIST:
case BT_AUDIO_METADATA_TYPE_PROGRAM_INFO: /* 0 - 255 octets */
case BT_AUDIO_METADATA_TYPE_PROGRAM_INFO_URI: /* 0 - 255 octets */
return true;
default:
return false;
}
}
static int lc3_metadata(struct bt_bap_stream *stream, const struct bt_audio_codec_data *meta,
size_t meta_count, struct bt_bap_ascs_rsp *rsp)
{
@ -205,7 +166,7 @@ static int lc3_metadata(struct bt_bap_stream *stream, const struct bt_audio_code
for (size_t i = 0; i < meta_count; i++) {
const struct bt_audio_codec_data *data = &meta[i];
if (!valid_metadata_type(data->data.type, data->data.data_len)) {
if (!BT_AUDIO_METADATA_TYPE_IS_KNOWN(data->data.type)) {
printk("Invalid metadata type %u or length %u\n", data->data.type,
data->data.data_len);
*rsp = BT_BAP_ASCS_RSP(BT_BAP_ASCS_RSP_CODE_METADATA_REJECTED,

View file

@ -363,45 +363,6 @@ static int unicast_server_start(struct bt_bap_stream *stream, struct bt_bap_ascs
return 0;
}
static bool valid_metadata_type(uint8_t type, uint8_t len)
{
switch (type) {
case BT_AUDIO_METADATA_TYPE_PREF_CONTEXT:
case BT_AUDIO_METADATA_TYPE_STREAM_CONTEXT:
if (len != 2) {
return false;
}
return true;
case BT_AUDIO_METADATA_TYPE_STREAM_LANG:
if (len != 3) {
return false;
}
return true;
case BT_AUDIO_METADATA_TYPE_PARENTAL_RATING:
if (len != 1) {
return false;
}
return true;
case BT_AUDIO_METADATA_TYPE_EXTENDED: /* 2 - 255 octets */
case BT_AUDIO_METADATA_TYPE_VENDOR: /* 2 - 255 octets */
/* At least Extended Metadata Type / Company_ID should be there */
if (len < 2) {
return false;
}
return true;
case BT_AUDIO_METADATA_TYPE_CCID_LIST:
case BT_AUDIO_METADATA_TYPE_PROGRAM_INFO: /* 0 - 255 octets */
case BT_AUDIO_METADATA_TYPE_PROGRAM_INFO_URI: /* 0 - 255 octets */
return true;
default:
return false;
}
}
static int unicast_server_metadata(struct bt_bap_stream *stream,
const struct bt_audio_codec_data *meta, size_t meta_count,
struct bt_bap_ascs_rsp *rsp)
@ -411,7 +372,7 @@ static int unicast_server_metadata(struct bt_bap_stream *stream,
for (size_t i = 0; i < meta_count; i++) {
const struct bt_audio_codec_data *data = &meta[i];
if (!valid_metadata_type(data->data.type, data->data.data_len)) {
if (!BT_AUDIO_METADATA_TYPE_IS_KNOWN(data->data.type)) {
printk("Invalid metadata type %u or length %u\n", data->data.type,
data->data.data_len);
*rsp = BT_BAP_ASCS_RSP(BT_BAP_ASCS_RSP_CODE_METADATA_REJECTED,