Thread (3 messages) 3 messages, 2 authors, 9d ago
COOLING9d

[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
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help