]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
6.6-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 25 May 2024 14:56:31 +0000 (16:56 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 25 May 2024 14:56:31 +0000 (16:56 +0200)
added patches:
series
tty-n_gsm-fix-missing-receive-state-reset-after-mode-switch.patch
tty-n_gsm-fix-possible-out-of-bounds-in-gsm0_receive.patch

queue-6.6/series [new file with mode: 0644]
queue-6.6/tty-n_gsm-fix-missing-receive-state-reset-after-mode-switch.patch [new file with mode: 0644]
queue-6.6/tty-n_gsm-fix-possible-out-of-bounds-in-gsm0_receive.patch [new file with mode: 0644]

diff --git a/queue-6.6/series b/queue-6.6/series
new file mode 100644 (file)
index 0000000..eca4dd3
--- /dev/null
@@ -0,0 +1,2 @@
+tty-n_gsm-fix-possible-out-of-bounds-in-gsm0_receive.patch
+tty-n_gsm-fix-missing-receive-state-reset-after-mode-switch.patch
diff --git a/queue-6.6/tty-n_gsm-fix-missing-receive-state-reset-after-mode-switch.patch b/queue-6.6/tty-n_gsm-fix-missing-receive-state-reset-after-mode-switch.patch
new file mode 100644 (file)
index 0000000..615ebd3
--- /dev/null
@@ -0,0 +1,294 @@
+From 70d7f1427afcf7fa2d21cb5a04c6f3555d5b9357 Mon Sep 17 00:00:00 2001
+From: Daniel Starke <daniel.starke@siemens.com>
+Date: Wed, 24 Apr 2024 07:48:42 +0200
+Subject: tty: n_gsm: fix missing receive state reset after mode switch
+
+From: Daniel Starke <daniel.starke@siemens.com>
+
+commit 70d7f1427afcf7fa2d21cb5a04c6f3555d5b9357 upstream.
+
+The current implementation uses either gsm0_receive() or gsm1_receive()
+depending on whether the user configured the mux in basic or advanced
+option mode. Both functions share some state values over the same logical
+elements of the frame. However, both frame types differ in their nature.
+gsm0_receive() uses non-transparency framing, whereas gsm1_receive() uses
+transparency mechanism. Switching between both modes leaves the receive
+function in an undefined state when done during frame reception.
+
+Fix this by splitting both states. Add gsm0_receive_state_check_and_fix()
+and gsm1_receive_state_check_and_fix() to ensure that gsm->state is reset
+after a change of gsm->receive.
+
+Note that gsm->state is only accessed in:
+- gsm0_receive()
+- gsm1_receive()
+- gsm_error()
+
+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/20240424054842.7741-2-daniel.starke@siemens.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/n_gsm.c |  133 +++++++++++++++++++++++++++++++++++-----------------
+ 1 file changed, 92 insertions(+), 41 deletions(-)
+
+--- a/drivers/tty/n_gsm.c
++++ b/drivers/tty/n_gsm.c
+@@ -244,16 +244,18 @@ enum gsm_encoding {
+ enum gsm_mux_state {
+       GSM_SEARCH,
+-      GSM_START,
+-      GSM_ADDRESS,
+-      GSM_CONTROL,
+-      GSM_LEN,
+-      GSM_DATA,
+-      GSM_FCS,
+-      GSM_OVERRUN,
+-      GSM_LEN0,
+-      GSM_LEN1,
+-      GSM_SSOF,
++      GSM0_ADDRESS,
++      GSM0_CONTROL,
++      GSM0_LEN0,
++      GSM0_LEN1,
++      GSM0_DATA,
++      GSM0_FCS,
++      GSM0_SSOF,
++      GSM1_START,
++      GSM1_ADDRESS,
++      GSM1_CONTROL,
++      GSM1_DATA,
++      GSM1_OVERRUN,
+ };
+ /*
+@@ -2846,6 +2848,30 @@ invalid:
+       return;
+ }
++/**
++ * gsm0_receive_state_check_and_fix   -       check and correct receive state
++ * @gsm: gsm data for this ldisc instance
++ *
++ * Ensures that the current receive state is valid for basic option mode.
++ */
++
++static void gsm0_receive_state_check_and_fix(struct gsm_mux *gsm)
++{
++      switch (gsm->state) {
++      case GSM_SEARCH:
++      case GSM0_ADDRESS:
++      case GSM0_CONTROL:
++      case GSM0_LEN0:
++      case GSM0_LEN1:
++      case GSM0_DATA:
++      case GSM0_FCS:
++      case GSM0_SSOF:
++              break;
++      default:
++              gsm->state = GSM_SEARCH;
++              break;
++      }
++}
+ /**
+  *    gsm0_receive    -       perform processing for non-transparency
+@@ -2859,26 +2885,27 @@ static void gsm0_receive(struct gsm_mux
+ {
+       unsigned int len;
++      gsm0_receive_state_check_and_fix(gsm);
+       switch (gsm->state) {
+       case GSM_SEARCH:        /* SOF marker */
+               if (c == GSM0_SOF) {
+-                      gsm->state = GSM_ADDRESS;
++                      gsm->state = GSM0_ADDRESS;
+                       gsm->address = 0;
+                       gsm->len = 0;
+                       gsm->fcs = INIT_FCS;
+               }
+               break;
+-      case GSM_ADDRESS:       /* Address EA */
++      case GSM0_ADDRESS:      /* Address EA */
+               gsm->fcs = gsm_fcs_add(gsm->fcs, c);
+               if (gsm_read_ea(&gsm->address, c))
+-                      gsm->state = GSM_CONTROL;
++                      gsm->state = GSM0_CONTROL;
+               break;
+-      case GSM_CONTROL:       /* Control Byte */
++      case GSM0_CONTROL:      /* Control Byte */
+               gsm->fcs = gsm_fcs_add(gsm->fcs, c);
+               gsm->control = c;
+-              gsm->state = GSM_LEN0;
++              gsm->state = GSM0_LEN0;
+               break;
+-      case GSM_LEN0:          /* Length EA */
++      case GSM0_LEN0:         /* Length EA */
+               gsm->fcs = gsm_fcs_add(gsm->fcs, c);
+               if (gsm_read_ea(&gsm->len, c)) {
+                       if (gsm->len > gsm->mru) {
+@@ -2888,14 +2915,14 @@ static void gsm0_receive(struct gsm_mux
+                       }
+                       gsm->count = 0;
+                       if (!gsm->len)
+-                              gsm->state = GSM_FCS;
++                              gsm->state = GSM0_FCS;
+                       else
+-                              gsm->state = GSM_DATA;
++                              gsm->state = GSM0_DATA;
+                       break;
+               }
+-              gsm->state = GSM_LEN1;
++              gsm->state = GSM0_LEN1;
+               break;
+-      case GSM_LEN1:
++      case GSM0_LEN1:
+               gsm->fcs = gsm_fcs_add(gsm->fcs, c);
+               len = c;
+               gsm->len |= len << 7;
+@@ -2906,11 +2933,11 @@ static void gsm0_receive(struct gsm_mux
+               }
+               gsm->count = 0;
+               if (!gsm->len)
+-                      gsm->state = GSM_FCS;
++                      gsm->state = GSM0_FCS;
+               else
+-                      gsm->state = GSM_DATA;
++                      gsm->state = GSM0_DATA;
+               break;
+-      case GSM_DATA:          /* Data */
++      case GSM0_DATA:         /* Data */
+               gsm->buf[gsm->count++] = c;
+               if (gsm->count >= MAX_MRU) {
+                       gsm->bad_size++;
+@@ -2921,14 +2948,14 @@ static void gsm0_receive(struct gsm_mux
+                               gsm->fcs = gsm_fcs_add_block(gsm->fcs, gsm->buf,
+                                                            gsm->count);
+                       }
+-                      gsm->state = GSM_FCS;
++                      gsm->state = GSM0_FCS;
+               }
+               break;
+-      case GSM_FCS:           /* FCS follows the packet */
++      case GSM0_FCS:          /* FCS follows the packet */
+               gsm->fcs = gsm_fcs_add(gsm->fcs, c);
+-              gsm->state = GSM_SSOF;
++              gsm->state = GSM0_SSOF;
+               break;
+-      case GSM_SSOF:
++      case GSM0_SSOF:
+               gsm->state = GSM_SEARCH;
+               if (c == GSM0_SOF)
+                       gsm_queue(gsm);
+@@ -2942,6 +2969,29 @@ static void gsm0_receive(struct gsm_mux
+ }
+ /**
++ * gsm1_receive_state_check_and_fix   -       check and correct receive state
++ * @gsm: gsm data for this ldisc instance
++ *
++ * Ensures that the current receive state is valid for advanced option mode.
++ */
++
++static void gsm1_receive_state_check_and_fix(struct gsm_mux *gsm)
++{
++      switch (gsm->state) {
++      case GSM_SEARCH:
++      case GSM1_START:
++      case GSM1_ADDRESS:
++      case GSM1_CONTROL:
++      case GSM1_DATA:
++      case GSM1_OVERRUN:
++              break;
++      default:
++              gsm->state = GSM_SEARCH;
++              break;
++      }
++}
++
++/**
+  *    gsm1_receive    -       perform processing for non-transparency
+  *    @gsm: gsm data for this ldisc instance
+  *    @c: character
+@@ -2951,6 +3001,7 @@ static void gsm0_receive(struct gsm_mux
+ static void gsm1_receive(struct gsm_mux *gsm, unsigned char c)
+ {
++      gsm1_receive_state_check_and_fix(gsm);
+       /* handle XON/XOFF */
+       if ((c & ISO_IEC_646_MASK) == XON) {
+               gsm->constipated = true;
+@@ -2963,11 +3014,11 @@ static void gsm1_receive(struct gsm_mux
+       }
+       if (c == GSM1_SOF) {
+               /* EOF is only valid in frame if we have got to the data state */
+-              if (gsm->state == GSM_DATA) {
++              if (gsm->state == GSM1_DATA) {
+                       if (gsm->count < 1) {
+                               /* Missing FSC */
+                               gsm->malformed++;
+-                              gsm->state = GSM_START;
++                              gsm->state = GSM1_START;
+                               return;
+                       }
+                       /* Remove the FCS from data */
+@@ -2983,14 +3034,14 @@ static void gsm1_receive(struct gsm_mux
+                       gsm->fcs = gsm_fcs_add(gsm->fcs, gsm->buf[gsm->count]);
+                       gsm->len = gsm->count;
+                       gsm_queue(gsm);
+-                      gsm->state  = GSM_START;
++                      gsm->state  = GSM1_START;
+                       return;
+               }
+               /* Any partial frame was a runt so go back to start */
+-              if (gsm->state != GSM_START) {
++              if (gsm->state != GSM1_START) {
+                       if (gsm->state != GSM_SEARCH)
+                               gsm->malformed++;
+-                      gsm->state = GSM_START;
++                      gsm->state = GSM1_START;
+               }
+               /* A SOF in GSM_START means we are still reading idling or
+                  framing bytes */
+@@ -3011,30 +3062,30 @@ static void gsm1_receive(struct gsm_mux
+               gsm->escape = false;
+       }
+       switch (gsm->state) {
+-      case GSM_START:         /* First byte after SOF */
++      case GSM1_START:                /* First byte after SOF */
+               gsm->address = 0;
+-              gsm->state = GSM_ADDRESS;
++              gsm->state = GSM1_ADDRESS;
+               gsm->fcs = INIT_FCS;
+               fallthrough;
+-      case GSM_ADDRESS:       /* Address continuation */
++      case GSM1_ADDRESS:      /* Address continuation */
+               gsm->fcs = gsm_fcs_add(gsm->fcs, c);
+               if (gsm_read_ea(&gsm->address, c))
+-                      gsm->state = GSM_CONTROL;
++                      gsm->state = GSM1_CONTROL;
+               break;
+-      case GSM_CONTROL:       /* Control Byte */
++      case GSM1_CONTROL:      /* Control Byte */
+               gsm->fcs = gsm_fcs_add(gsm->fcs, c);
+               gsm->control = c;
+               gsm->count = 0;
+-              gsm->state = GSM_DATA;
++              gsm->state = GSM1_DATA;
+               break;
+-      case GSM_DATA:          /* Data */
++      case GSM1_DATA:         /* Data */
+               if (gsm->count > gsm->mru || gsm->count > MAX_MRU) {    /* Allow one for the FCS */
+-                      gsm->state = GSM_OVERRUN;
++                      gsm->state = GSM1_OVERRUN;
+                       gsm->bad_size++;
+               } else
+                       gsm->buf[gsm->count++] = c;
+               break;
+-      case GSM_OVERRUN:       /* Over-long - eg a dropped SOF */
++      case GSM1_OVERRUN:      /* Over-long - eg a dropped SOF */
+               break;
+       default:
+               pr_debug("%s: unhandled state: %d\n", __func__, gsm->state);
diff --git a/queue-6.6/tty-n_gsm-fix-possible-out-of-bounds-in-gsm0_receive.patch b/queue-6.6/tty-n_gsm-fix-possible-out-of-bounds-in-gsm0_receive.patch
new file mode 100644 (file)
index 0000000..0b929a4
--- /dev/null
@@ -0,0 +1,63 @@
+From 47388e807f85948eefc403a8a5fdc5b406a65d5a Mon Sep 17 00:00:00 2001
+From: Daniel Starke <daniel.starke@siemens.com>
+Date: Wed, 24 Apr 2024 07:48:41 +0200
+Subject: tty: n_gsm: fix possible out-of-bounds in gsm0_receive()
+
+From: Daniel Starke <daniel.starke@siemens.com>
+
+commit 47388e807f85948eefc403a8a5fdc5b406a65d5a upstream.
+
+Assuming the following:
+- side A configures the n_gsm in basic option mode
+- side B sends the header of a basic option mode frame with data length 1
+- side A switches to advanced option mode
+- side B sends 2 data bytes which exceeds gsm->len
+  Reason: gsm->len is not used in advanced option mode.
+- side A switches to basic option mode
+- side B keeps sending until gsm0_receive() writes past gsm->buf
+  Reason: Neither gsm->state nor gsm->len have been reset after
+  reconfiguration.
+
+Fix this by changing gsm->count to gsm->len comparison from equal to less
+than. Also add upper limit checks against the constant MAX_MRU in
+gsm0_receive() and gsm1_receive() to harden against memory corruption of
+gsm->len and gsm->mru.
+
+All other checks remain as we still need to limit the data according to the
+user configuration and actual payload size.
+
+Reported-by: j51569436@gmail.com
+Closes: https://bugzilla.kernel.org/show_bug.cgi?id=218708
+Tested-by: j51569436@gmail.com
+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/20240424054842.7741-1-daniel.starke@siemens.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/tty/n_gsm.c |    7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+--- a/drivers/tty/n_gsm.c
++++ b/drivers/tty/n_gsm.c
+@@ -2912,7 +2912,10 @@ static void gsm0_receive(struct gsm_mux
+               break;
+       case GSM_DATA:          /* Data */
+               gsm->buf[gsm->count++] = c;
+-              if (gsm->count == gsm->len) {
++              if (gsm->count >= MAX_MRU) {
++                      gsm->bad_size++;
++                      gsm->state = GSM_SEARCH;
++              } else 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,
+@@ -3025,7 +3028,7 @@ static void gsm1_receive(struct gsm_mux
+               gsm->state = GSM_DATA;
+               break;
+       case GSM_DATA:          /* Data */
+-              if (gsm->count > gsm->mru) {    /* Allow one for the FCS */
++              if (gsm->count > gsm->mru || gsm->count > MAX_MRU) {    /* Allow one for the FCS */
+                       gsm->state = GSM_OVERRUN;
+                       gsm->bad_size++;
+               } else