[PATCH net v2 1/2] llc: conn: use one state snapshot in llc_conn_service
From: Ren Wei <hidden>
Date: 2026-06-08 07:47:58
Subsystem:
llc (802.2), networking [general], the rest · Maintainers:
"David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds
From: Zhengchuan Liang <redacted>
llc_conn_service() only rejects states above NBR_CONN_STATES before
the state machine lookup. That misses LLC_CONN_OUT_OF_SVC, whose value
is 0.
The LLC state machine can set a socket to LLC_CONN_OUT_OF_SVC, and
llc_conn_disc() does not actually detach or free that socket yet. A
later packet can therefore reach llc_qualify_conn_ev() with state 0,
which indexes llc_conn_state_table[state - 1] and turns the lookup
into an out-of-bounds access.
Take one READ_ONCE() snapshot in llc_conn_service(), reject
LLC_CONN_OUT_OF_SVC there, and pass the validated state down to
llc_qualify_conn_ev(). This also keeps the lookup on one consistent
state value.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@vger.kernel.org
Reported-by: Yuan Tan <redacted>
Reported-by: Yifan Wu <redacted>
Reported-by: Juefei Pu <redacted>
Reported-by: Xin Liu <redacted>
Assisted-by: Codex:GPT-5.4
Signed-off-by: Zhengchuan Liang <redacted>
Signed-off-by: Ren Wei <redacted>
---
changes in v2:
- Use a single READ_ONCE() snapshot of llc->state in llc_conn_service().
- Pass the validated state into llc_qualify_conn_ev() to avoid mixed state reads.
- v1 link: https://lore.kernel.org/all/5f646c530f4a0820060499054c46b8dbecebd7be.1778638129.git.zlian064@ucr.edu/ (local)
net/llc/llc_conn.c | 18 +++++++++++-------
1 file changed, 11 insertions(+), 7 deletions(-)
diff --git a/net/llc/llc_conn.c b/net/llc/llc_conn.c
index 5c0ac243b248..9602183d09d8 100644
--- a/net/llc/llc_conn.c
+++ b/net/llc/llc_conn.c@@ -37,7 +37,8 @@ static int llc_exec_conn_trans_actions(struct sock *sk, const struct llc_conn_state_trans *trans, struct sk_buff *ev); static const struct llc_conn_state_trans *llc_qualify_conn_ev(struct sock *sk, - struct sk_buff *skb); + struct sk_buff *skb, + u8 state); /* Offset table on connection states transition diagram */ static int llc_offset_table[NBR_CONN_STATES][NBR_CONN_EV];
@@ -358,12 +359,15 @@ static int llc_conn_service(struct sock *sk, struct sk_buff *skb) { const struct llc_conn_state_trans *trans; struct llc_sock *llc = llc_sk(sk); + u8 state = READ_ONCE(llc->state); int rc = 1; - if (llc->state > NBR_CONN_STATES) + if (state == LLC_CONN_OUT_OF_SVC) + return 0; + if (state > NBR_CONN_STATES) goto out; rc = 0; - trans = llc_qualify_conn_ev(sk, skb); + trans = llc_qualify_conn_ev(sk, skb, state); if (trans) { rc = llc_exec_conn_trans_actions(sk, trans, skb); if (!rc && trans->next_state != NO_STATE_CHANGE) {
@@ -385,20 +389,20 @@ static int llc_conn_service(struct sock *sk, struct sk_buff *skb) * Returns pointer to found transition on success, %NULL otherwise. */ static const struct llc_conn_state_trans *llc_qualify_conn_ev(struct sock *sk, - struct sk_buff *skb) + struct sk_buff *skb, + u8 state) { const struct llc_conn_state_trans **next_trans; const llc_conn_ev_qfyr_t *next_qualifier; struct llc_conn_state_ev *ev = llc_conn_ev(skb); - struct llc_sock *llc = llc_sk(sk); struct llc_conn_state *curr_state = - &llc_conn_state_table[llc->state - 1]; + &llc_conn_state_table[state - 1]; /* search thru events for this state until * list exhausted or until no more */ for (next_trans = curr_state->transitions + - llc_find_offset(llc->state - 1, ev->type); + llc_find_offset(state - 1, ev->type); (*next_trans)->ev; next_trans++) { if (!((*next_trans)->ev)(sk, skb)) { /* got POSSIBLE event match; the event may require
--
2.47.3