]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
ALSA: firewire-lib: add buffer-over-run protection at receiving more data blocks...
authorTakashi Sakamoto <o-takashi@sakamocchi.jp>
Fri, 22 May 2015 14:00:50 +0000 (23:00 +0900)
committerTakashi Iwai <tiwai@suse.de>
Sat, 23 May 2015 07:13:18 +0000 (09:13 +0200)
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>
sound/firewire/amdtp.c
sound/firewire/amdtp.h
sound/firewire/oxfw/oxfw-stream.c

index e061355f535f071f0fd096bfee62fb2702c0c94d..a3970043e472d18a22d385c490574383d5f328ec 100644 (file)
@@ -251,7 +251,12 @@ EXPORT_SYMBOL(amdtp_stream_set_parameters);
  */
 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);
 
@@ -807,12 +812,16 @@ static void in_stream_callback(struct fw_iso_context *context, u32 cycle,
                               void *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;
 
        /* The number of packets in buffer */
        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++) {
                if (s->packet_index < 0)
                        break;
@@ -828,6 +837,14 @@ static void in_stream_callback(struct fw_iso_context *context, u32 cycle,
                /* The number of quadlets in this packet */
                payload_quadlets =
                        (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);
        }
 
index 8a03a91e728b0f9bc4feb23a4bdd9f22fdf5952d..26b909329e54d96d7fcb075f9538599aaf30cb09 100644 (file)
@@ -29,6 +29,9 @@
  *     packet is not continuous from an initial value.
  * @CIP_EMPTY_HAS_WRONG_DBC: Only for in-stream. The value of dbc in empty
  *     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 {
        CIP_NONBLOCKING         = 0x00,
@@ -40,6 +43,7 @@ enum cip_flags {
        CIP_SKIP_DBC_ZERO_CHECK = 0x20,
        CIP_SKIP_INIT_DBC_CHECK = 0x40,
        CIP_EMPTY_HAS_WRONG_DBC = 0x80,
+       CIP_JUMBO_PAYLOAD       = 0x100,
 };
 
 /**
index e6757cd8572422813b1627f3c5718ec4c53f2aac..873d40fc4509cb38da928e334add9f2794f0babb 100644 (file)
@@ -232,9 +232,15 @@ int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw,
                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)
-               oxfw->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK;
+               oxfw->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK |
+                                        CIP_JUMBO_PAYLOAD;
 end:
        return err;
 }