tty: n_gsm: fix missing receive state reset after mode switch
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: e1eaea46bb
("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>
This commit is contained in:
parent
774d83b008
commit
62c3763dcb
|
@ -244,16 +244,18 @@ enum gsm_encoding {
|
||||||
|
|
||||||
enum gsm_mux_state {
|
enum gsm_mux_state {
|
||||||
GSM_SEARCH,
|
GSM_SEARCH,
|
||||||
GSM_START,
|
GSM0_ADDRESS,
|
||||||
GSM_ADDRESS,
|
GSM0_CONTROL,
|
||||||
GSM_CONTROL,
|
GSM0_LEN0,
|
||||||
GSM_LEN,
|
GSM0_LEN1,
|
||||||
GSM_DATA,
|
GSM0_DATA,
|
||||||
GSM_FCS,
|
GSM0_FCS,
|
||||||
GSM_OVERRUN,
|
GSM0_SSOF,
|
||||||
GSM_LEN0,
|
GSM1_START,
|
||||||
GSM_LEN1,
|
GSM1_ADDRESS,
|
||||||
GSM_SSOF,
|
GSM1_CONTROL,
|
||||||
|
GSM1_DATA,
|
||||||
|
GSM1_OVERRUN,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2846,6 +2848,30 @@ invalid:
|
||||||
return;
|
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
|
* gsm0_receive - perform processing for non-transparency
|
||||||
|
@ -2859,26 +2885,27 @@ static void gsm0_receive(struct gsm_mux *gsm, unsigned char c)
|
||||||
{
|
{
|
||||||
unsigned int len;
|
unsigned int len;
|
||||||
|
|
||||||
|
gsm0_receive_state_check_and_fix(gsm);
|
||||||
switch (gsm->state) {
|
switch (gsm->state) {
|
||||||
case GSM_SEARCH: /* SOF marker */
|
case GSM_SEARCH: /* SOF marker */
|
||||||
if (c == GSM0_SOF) {
|
if (c == GSM0_SOF) {
|
||||||
gsm->state = GSM_ADDRESS;
|
gsm->state = GSM0_ADDRESS;
|
||||||
gsm->address = 0;
|
gsm->address = 0;
|
||||||
gsm->len = 0;
|
gsm->len = 0;
|
||||||
gsm->fcs = INIT_FCS;
|
gsm->fcs = INIT_FCS;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case GSM_ADDRESS: /* Address EA */
|
case GSM0_ADDRESS: /* Address EA */
|
||||||
gsm->fcs = gsm_fcs_add(gsm->fcs, c);
|
gsm->fcs = gsm_fcs_add(gsm->fcs, c);
|
||||||
if (gsm_read_ea(&gsm->address, c))
|
if (gsm_read_ea(&gsm->address, c))
|
||||||
gsm->state = GSM_CONTROL;
|
gsm->state = GSM0_CONTROL;
|
||||||
break;
|
break;
|
||||||
case GSM_CONTROL: /* Control Byte */
|
case GSM0_CONTROL: /* Control Byte */
|
||||||
gsm->fcs = gsm_fcs_add(gsm->fcs, c);
|
gsm->fcs = gsm_fcs_add(gsm->fcs, c);
|
||||||
gsm->control = c;
|
gsm->control = c;
|
||||||
gsm->state = GSM_LEN0;
|
gsm->state = GSM0_LEN0;
|
||||||
break;
|
break;
|
||||||
case GSM_LEN0: /* Length EA */
|
case GSM0_LEN0: /* Length EA */
|
||||||
gsm->fcs = gsm_fcs_add(gsm->fcs, c);
|
gsm->fcs = gsm_fcs_add(gsm->fcs, c);
|
||||||
if (gsm_read_ea(&gsm->len, c)) {
|
if (gsm_read_ea(&gsm->len, c)) {
|
||||||
if (gsm->len > gsm->mru) {
|
if (gsm->len > gsm->mru) {
|
||||||
|
@ -2888,14 +2915,14 @@ static void gsm0_receive(struct gsm_mux *gsm, unsigned char c)
|
||||||
}
|
}
|
||||||
gsm->count = 0;
|
gsm->count = 0;
|
||||||
if (!gsm->len)
|
if (!gsm->len)
|
||||||
gsm->state = GSM_FCS;
|
gsm->state = GSM0_FCS;
|
||||||
else
|
else
|
||||||
gsm->state = GSM_DATA;
|
gsm->state = GSM0_DATA;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
gsm->state = GSM_LEN1;
|
gsm->state = GSM0_LEN1;
|
||||||
break;
|
break;
|
||||||
case GSM_LEN1:
|
case GSM0_LEN1:
|
||||||
gsm->fcs = gsm_fcs_add(gsm->fcs, c);
|
gsm->fcs = gsm_fcs_add(gsm->fcs, c);
|
||||||
len = c;
|
len = c;
|
||||||
gsm->len |= len << 7;
|
gsm->len |= len << 7;
|
||||||
|
@ -2906,11 +2933,11 @@ static void gsm0_receive(struct gsm_mux *gsm, unsigned char c)
|
||||||
}
|
}
|
||||||
gsm->count = 0;
|
gsm->count = 0;
|
||||||
if (!gsm->len)
|
if (!gsm->len)
|
||||||
gsm->state = GSM_FCS;
|
gsm->state = GSM0_FCS;
|
||||||
else
|
else
|
||||||
gsm->state = GSM_DATA;
|
gsm->state = GSM0_DATA;
|
||||||
break;
|
break;
|
||||||
case GSM_DATA: /* Data */
|
case GSM0_DATA: /* Data */
|
||||||
gsm->buf[gsm->count++] = c;
|
gsm->buf[gsm->count++] = c;
|
||||||
if (gsm->count >= MAX_MRU) {
|
if (gsm->count >= MAX_MRU) {
|
||||||
gsm->bad_size++;
|
gsm->bad_size++;
|
||||||
|
@ -2921,14 +2948,14 @@ static void gsm0_receive(struct gsm_mux *gsm, unsigned char c)
|
||||||
gsm->fcs = gsm_fcs_add_block(gsm->fcs, gsm->buf,
|
gsm->fcs = gsm_fcs_add_block(gsm->fcs, gsm->buf,
|
||||||
gsm->count);
|
gsm->count);
|
||||||
}
|
}
|
||||||
gsm->state = GSM_FCS;
|
gsm->state = GSM0_FCS;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case GSM_FCS: /* FCS follows the packet */
|
case GSM0_FCS: /* FCS follows the packet */
|
||||||
gsm->fcs = gsm_fcs_add(gsm->fcs, c);
|
gsm->fcs = gsm_fcs_add(gsm->fcs, c);
|
||||||
gsm->state = GSM_SSOF;
|
gsm->state = GSM0_SSOF;
|
||||||
break;
|
break;
|
||||||
case GSM_SSOF:
|
case GSM0_SSOF:
|
||||||
gsm->state = GSM_SEARCH;
|
gsm->state = GSM_SEARCH;
|
||||||
if (c == GSM0_SOF)
|
if (c == GSM0_SOF)
|
||||||
gsm_queue(gsm);
|
gsm_queue(gsm);
|
||||||
|
@ -2941,6 +2968,29 @@ static void gsm0_receive(struct gsm_mux *gsm, unsigned char c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
* gsm1_receive - perform processing for non-transparency
|
||||||
* @gsm: gsm data for this ldisc instance
|
* @gsm: gsm data for this ldisc instance
|
||||||
|
@ -2951,6 +3001,7 @@ static void gsm0_receive(struct gsm_mux *gsm, unsigned char c)
|
||||||
|
|
||||||
static void gsm1_receive(struct gsm_mux *gsm, unsigned char c)
|
static void gsm1_receive(struct gsm_mux *gsm, unsigned char c)
|
||||||
{
|
{
|
||||||
|
gsm1_receive_state_check_and_fix(gsm);
|
||||||
/* handle XON/XOFF */
|
/* handle XON/XOFF */
|
||||||
if ((c & ISO_IEC_646_MASK) == XON) {
|
if ((c & ISO_IEC_646_MASK) == XON) {
|
||||||
gsm->constipated = true;
|
gsm->constipated = true;
|
||||||
|
@ -2963,11 +3014,11 @@ static void gsm1_receive(struct gsm_mux *gsm, unsigned char c)
|
||||||
}
|
}
|
||||||
if (c == GSM1_SOF) {
|
if (c == GSM1_SOF) {
|
||||||
/* EOF is only valid in frame if we have got to the data state */
|
/* 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) {
|
if (gsm->count < 1) {
|
||||||
/* Missing FSC */
|
/* Missing FSC */
|
||||||
gsm->malformed++;
|
gsm->malformed++;
|
||||||
gsm->state = GSM_START;
|
gsm->state = GSM1_START;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/* Remove the FCS from data */
|
/* Remove the FCS from data */
|
||||||
|
@ -2983,14 +3034,14 @@ static void gsm1_receive(struct gsm_mux *gsm, unsigned char c)
|
||||||
gsm->fcs = gsm_fcs_add(gsm->fcs, gsm->buf[gsm->count]);
|
gsm->fcs = gsm_fcs_add(gsm->fcs, gsm->buf[gsm->count]);
|
||||||
gsm->len = gsm->count;
|
gsm->len = gsm->count;
|
||||||
gsm_queue(gsm);
|
gsm_queue(gsm);
|
||||||
gsm->state = GSM_START;
|
gsm->state = GSM1_START;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/* Any partial frame was a runt so go back to start */
|
/* 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)
|
if (gsm->state != GSM_SEARCH)
|
||||||
gsm->malformed++;
|
gsm->malformed++;
|
||||||
gsm->state = GSM_START;
|
gsm->state = GSM1_START;
|
||||||
}
|
}
|
||||||
/* A SOF in GSM_START means we are still reading idling or
|
/* A SOF in GSM_START means we are still reading idling or
|
||||||
framing bytes */
|
framing bytes */
|
||||||
|
@ -3011,30 +3062,30 @@ static void gsm1_receive(struct gsm_mux *gsm, unsigned char c)
|
||||||
gsm->escape = false;
|
gsm->escape = false;
|
||||||
}
|
}
|
||||||
switch (gsm->state) {
|
switch (gsm->state) {
|
||||||
case GSM_START: /* First byte after SOF */
|
case GSM1_START: /* First byte after SOF */
|
||||||
gsm->address = 0;
|
gsm->address = 0;
|
||||||
gsm->state = GSM_ADDRESS;
|
gsm->state = GSM1_ADDRESS;
|
||||||
gsm->fcs = INIT_FCS;
|
gsm->fcs = INIT_FCS;
|
||||||
fallthrough;
|
fallthrough;
|
||||||
case GSM_ADDRESS: /* Address continuation */
|
case GSM1_ADDRESS: /* Address continuation */
|
||||||
gsm->fcs = gsm_fcs_add(gsm->fcs, c);
|
gsm->fcs = gsm_fcs_add(gsm->fcs, c);
|
||||||
if (gsm_read_ea(&gsm->address, c))
|
if (gsm_read_ea(&gsm->address, c))
|
||||||
gsm->state = GSM_CONTROL;
|
gsm->state = GSM1_CONTROL;
|
||||||
break;
|
break;
|
||||||
case GSM_CONTROL: /* Control Byte */
|
case GSM1_CONTROL: /* Control Byte */
|
||||||
gsm->fcs = gsm_fcs_add(gsm->fcs, c);
|
gsm->fcs = gsm_fcs_add(gsm->fcs, c);
|
||||||
gsm->control = c;
|
gsm->control = c;
|
||||||
gsm->count = 0;
|
gsm->count = 0;
|
||||||
gsm->state = GSM_DATA;
|
gsm->state = GSM1_DATA;
|
||||||
break;
|
break;
|
||||||
case GSM_DATA: /* Data */
|
case GSM1_DATA: /* Data */
|
||||||
if (gsm->count > gsm->mru || gsm->count > MAX_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->state = GSM1_OVERRUN;
|
||||||
gsm->bad_size++;
|
gsm->bad_size++;
|
||||||
} else
|
} else
|
||||||
gsm->buf[gsm->count++] = c;
|
gsm->buf[gsm->count++] = c;
|
||||||
break;
|
break;
|
||||||
case GSM_OVERRUN: /* Over-long - eg a dropped SOF */
|
case GSM1_OVERRUN: /* Over-long - eg a dropped SOF */
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
pr_debug("%s: unhandled state: %d\n", __func__, gsm->state);
|
pr_debug("%s: unhandled state: %d\n", __func__, gsm->state);
|
||||||
|
|
Loading…
Reference in New Issue