ALSA: firewire-lib: add buffer-over-run protection at receiving more data blocks than expected
In IEC 61883-6, the number of data blocks in a packet is limited up to the value of SYT_INTERVAL. Current implementation is compliant to the limitation, while it can cause buffer-over-run when the value of dbs field in received packet is illegally large. This commit adds a validator to detect such illegal packets to prevent the buffer-over-run. Actually, the buffer is aligned to the size of memory page, thus this issue hardly causes system errors due to the room to page alignment, as long as a few packets includes such jumbo payload; i.e. a packet to several received packets. Here, Behringer F-Control Audio 202 (based on OXFW 960) has a quirk to postpone transferring isochronous packet till finish handling any asynchronous packets. In this case, this model is lazy, transfers no packets according to several cycle-start packets. After finishing, this model pushes required data in next isochronous packet. As a result, the packet include more data blocks than IEC 61883-6 defines. To continue to support this model, this commit adds a new flag to extend the length of calculated payload. This flag allows the size of payload 5 times as large as IEC 61883-6 defines. As a result, packets from this model passed the validator successfully. Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp> Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
parent
181a152a09
commit
a2064710ba
@ -251,7 +251,12 @@ EXPORT_SYMBOL(amdtp_stream_set_parameters);
|
|||||||
*/
|
*/
|
||||||
unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s)
|
unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s)
|
||||||
{
|
{
|
||||||
return 8 + s->syt_interval * s->data_block_quadlets * 4;
|
unsigned int multiplier = 1;
|
||||||
|
|
||||||
|
if (s->flags & CIP_JUMBO_PAYLOAD)
|
||||||
|
multiplier = 5;
|
||||||
|
|
||||||
|
return 8 + s->syt_interval * s->data_block_quadlets * 4 * multiplier;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(amdtp_stream_get_max_payload);
|
EXPORT_SYMBOL(amdtp_stream_get_max_payload);
|
||||||
|
|
||||||
@ -807,12 +812,16 @@ static void in_stream_callback(struct fw_iso_context *context, u32 cycle,
|
|||||||
void *private_data)
|
void *private_data)
|
||||||
{
|
{
|
||||||
struct amdtp_stream *s = private_data;
|
struct amdtp_stream *s = private_data;
|
||||||
unsigned int p, syt, packets, payload_quadlets;
|
unsigned int p, syt, packets;
|
||||||
|
unsigned int payload_quadlets, max_payload_quadlets;
|
||||||
__be32 *buffer, *headers = header;
|
__be32 *buffer, *headers = header;
|
||||||
|
|
||||||
/* The number of packets in buffer */
|
/* The number of packets in buffer */
|
||||||
packets = header_length / IN_PACKET_HEADER_SIZE;
|
packets = header_length / IN_PACKET_HEADER_SIZE;
|
||||||
|
|
||||||
|
/* For buffer-over-run prevention. */
|
||||||
|
max_payload_quadlets = amdtp_stream_get_max_payload(s) / 4;
|
||||||
|
|
||||||
for (p = 0; p < packets; p++) {
|
for (p = 0; p < packets; p++) {
|
||||||
if (s->packet_index < 0)
|
if (s->packet_index < 0)
|
||||||
break;
|
break;
|
||||||
@ -828,6 +837,14 @@ static void in_stream_callback(struct fw_iso_context *context, u32 cycle,
|
|||||||
/* The number of quadlets in this packet */
|
/* The number of quadlets in this packet */
|
||||||
payload_quadlets =
|
payload_quadlets =
|
||||||
(be32_to_cpu(headers[p]) >> ISO_DATA_LENGTH_SHIFT) / 4;
|
(be32_to_cpu(headers[p]) >> ISO_DATA_LENGTH_SHIFT) / 4;
|
||||||
|
if (payload_quadlets > max_payload_quadlets) {
|
||||||
|
dev_err(&s->unit->device,
|
||||||
|
"Detect jumbo payload: %02x %02x\n",
|
||||||
|
payload_quadlets, max_payload_quadlets);
|
||||||
|
s->packet_index = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
handle_in_packet(s, payload_quadlets, buffer);
|
handle_in_packet(s, payload_quadlets, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,6 +29,9 @@
|
|||||||
* packet is not continuous from an initial value.
|
* packet is not continuous from an initial value.
|
||||||
* @CIP_EMPTY_HAS_WRONG_DBC: Only for in-stream. The value of dbc in empty
|
* @CIP_EMPTY_HAS_WRONG_DBC: Only for in-stream. The value of dbc in empty
|
||||||
* packet is wrong but the others are correct.
|
* packet is wrong but the others are correct.
|
||||||
|
* @CIP_JUMBO_PAYLOAD: Only for in-stream. The number of data blocks in an
|
||||||
|
* packet is larger than IEC 61883-6 defines. Current implementation
|
||||||
|
* allows 5 times as large as IEC 61883-6 defines.
|
||||||
*/
|
*/
|
||||||
enum cip_flags {
|
enum cip_flags {
|
||||||
CIP_NONBLOCKING = 0x00,
|
CIP_NONBLOCKING = 0x00,
|
||||||
@ -40,6 +43,7 @@ enum cip_flags {
|
|||||||
CIP_SKIP_DBC_ZERO_CHECK = 0x20,
|
CIP_SKIP_DBC_ZERO_CHECK = 0x20,
|
||||||
CIP_SKIP_INIT_DBC_CHECK = 0x40,
|
CIP_SKIP_INIT_DBC_CHECK = 0x40,
|
||||||
CIP_EMPTY_HAS_WRONG_DBC = 0x80,
|
CIP_EMPTY_HAS_WRONG_DBC = 0x80,
|
||||||
|
CIP_JUMBO_PAYLOAD = 0x100,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -232,9 +232,15 @@ int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw,
|
|||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* OXFW starts to transmit packets with non-zero dbc. */
|
/*
|
||||||
|
* OXFW starts to transmit packets with non-zero dbc.
|
||||||
|
* OXFW postpone transferring packets till handling any asynchronous
|
||||||
|
* packets. As a result, next isochronous packet includes more data
|
||||||
|
* blocks than IEC 61883-6 defines.
|
||||||
|
*/
|
||||||
if (stream == &oxfw->tx_stream)
|
if (stream == &oxfw->tx_stream)
|
||||||
oxfw->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK;
|
oxfw->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK |
|
||||||
|
CIP_JUMBO_PAYLOAD;
|
||||||
end:
|
end:
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user