tty: n_gsm: fix frame reception handling
authorDaniel Starke <daniel.starke@siemens.com>
Thu, 14 Apr 2022 09:42:11 +0000 (02:42 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 15 Apr 2022 06:36:04 +0000 (08:36 +0200)
The frame checksum (FCS) is currently handled in gsm_queue() after
reception of a frame. However, this breaks layering. A workaround with
'received_fcs' was implemented so far.
Furthermore, frames are handled as such even if no end flag was received.
Move FCS calculation from gsm_queue() to gsm0_receive() and gsm1_receive().
Also delay gsm_queue() call there until a full frame was received to fix
both points.

Fixes: e1eaea46bb40 ("tty: n_gsm line discipline")
Cc: stable@vger.kernel.org
Signed-off-by: Daniel Starke <daniel.starke@siemens.com>
Link: https://lore.kernel.org/r/20220414094225.4527-6-daniel.starke@siemens.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/tty/n_gsm.c

index 3ba2505..4ce18b4 100644 (file)
@@ -219,7 +219,6 @@ struct gsm_mux {
        int encoding;
        u8 control;
        u8 fcs;
-       u8 received_fcs;
        u8 *txframe;                    /* TX framing buffer */
 
        /* Method for the receiver side */
@@ -1794,18 +1793,7 @@ static void gsm_queue(struct gsm_mux *gsm)
        u8 cr;
        int address;
        int i, j, k, address_tmp;
-       /* We have to sneak a look at the packet body to do the FCS.
-          A somewhat layering violation in the spec */
 
-       if ((gsm->control & ~PF) == UI)
-               gsm->fcs = gsm_fcs_add_block(gsm->fcs, gsm->buf, gsm->len);
-       if (gsm->encoding == 0) {
-               /* WARNING: gsm->received_fcs is used for
-               gsm->encoding = 0 only.
-               In this case it contain the last piece of data
-               required to generate final CRC */
-               gsm->fcs = gsm_fcs_add(gsm->fcs, gsm->received_fcs);
-       }
        if (gsm->fcs != GOOD_FCS) {
                gsm->bad_fcs++;
                if (debug & 4)
@@ -1993,19 +1981,25 @@ static void gsm0_receive(struct gsm_mux *gsm, unsigned char c)
                break;
        case GSM_DATA:          /* Data */
                gsm->buf[gsm->count++] = c;
-               if (gsm->count == gsm->len)
+               if (gsm->count == gsm->len) {
+                       /* Calculate final FCS for UI frames over all data */
+                       if ((gsm->control & ~PF) != UIH) {
+                               gsm->fcs = gsm_fcs_add_block(gsm->fcs, gsm->buf,
+                                                            gsm->count);
+                       }
                        gsm->state = GSM_FCS;
+               }
                break;
        case GSM_FCS:           /* FCS follows the packet */
-               gsm->received_fcs = c;
-               gsm_queue(gsm);
+               gsm->fcs = gsm_fcs_add(gsm->fcs, c);
                gsm->state = GSM_SSOF;
                break;
        case GSM_SSOF:
-               if (c == GSM0_SOF) {
-                       gsm->state = GSM_SEARCH;
-                       break;
-               }
+               gsm->state = GSM_SEARCH;
+               if (c == GSM0_SOF)
+                       gsm_queue(gsm);
+               else
+                       gsm->bad_size++;
                break;
        default:
                pr_debug("%s: unhandled state: %d\n", __func__, gsm->state);
@@ -2024,11 +2018,24 @@ static void gsm0_receive(struct gsm_mux *gsm, unsigned char c)
 static void gsm1_receive(struct gsm_mux *gsm, unsigned char c)
 {
        if (c == GSM1_SOF) {
-               /* EOF is only valid in frame if we have got to the data state
-                  and received at least one byte (the FCS) */
-               if (gsm->state == GSM_DATA && gsm->count) {
-                       /* Extract the FCS */
+               /* EOF is only valid in frame if we have got to the data state */
+               if (gsm->state == GSM_DATA) {
+                       if (gsm->count < 1) {
+                               /* Missing FSC */
+                               gsm->malformed++;
+                               gsm->state = GSM_START;
+                               return;
+                       }
+                       /* Remove the FCS from data */
                        gsm->count--;
+                       if ((gsm->control & ~PF) != UIH) {
+                               /* Calculate final FCS for UI frames over all
+                                * data but FCS
+                                */
+                               gsm->fcs = gsm_fcs_add_block(gsm->fcs, gsm->buf,
+                                                            gsm->count);
+                       }
+                       /* Add the FCS itself to test against GOOD_FCS */
                        gsm->fcs = gsm_fcs_add(gsm->fcs, gsm->buf[gsm->count]);
                        gsm->len = gsm->count;
                        gsm_queue(gsm);