From 8617e23177af32d1e491156ea54c680ac9227700 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 25 May 2024 16:56:31 +0200 Subject: [PATCH] 6.6-stable patches 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 | 2 + ...eceive-state-reset-after-mode-switch.patch | 294 ++++++++++++++++++ ...ssible-out-of-bounds-in-gsm0_receive.patch | 63 ++++ 3 files changed, 359 insertions(+) create mode 100644 queue-6.6/series create mode 100644 queue-6.6/tty-n_gsm-fix-missing-receive-state-reset-after-mode-switch.patch create mode 100644 queue-6.6/tty-n_gsm-fix-possible-out-of-bounds-in-gsm0_receive.patch diff --git a/queue-6.6/series b/queue-6.6/series new file mode 100644 index 00000000000..eca4dd30c24 --- /dev/null +++ b/queue-6.6/series @@ -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 index 00000000000..615ebd36d7d --- /dev/null +++ b/queue-6.6/tty-n_gsm-fix-missing-receive-state-reset-after-mode-switch.patch @@ -0,0 +1,294 @@ +From 70d7f1427afcf7fa2d21cb5a04c6f3555d5b9357 Mon Sep 17 00:00:00 2001 +From: Daniel Starke +Date: Wed, 24 Apr 2024 07:48:42 +0200 +Subject: tty: n_gsm: fix missing receive state reset after mode switch + +From: Daniel Starke + +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 +Link: https://lore.kernel.org/r/20240424054842.7741-2-daniel.starke@siemens.com +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 00000000000..0b929a4bd6a --- /dev/null +++ b/queue-6.6/tty-n_gsm-fix-possible-out-of-bounds-in-gsm0_receive.patch @@ -0,0 +1,63 @@ +From 47388e807f85948eefc403a8a5fdc5b406a65d5a Mon Sep 17 00:00:00 2001 +From: Daniel Starke +Date: Wed, 24 Apr 2024 07:48:41 +0200 +Subject: tty: n_gsm: fix possible out-of-bounds in gsm0_receive() + +From: Daniel Starke + +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 +Link: https://lore.kernel.org/r/20240424054842.7741-1-daniel.starke@siemens.com +Signed-off-by: Greg Kroah-Hartman +--- + 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 -- 2.47.3