An unsolicited frame can move a bound PF_LLC socket from
LLC_CONN_STATE_ADM to LLC_CONN_OUT_OF_SVC while the socket stays open
from userspace's point of view. Both upper layer entry points handle
that badly:

 - llc_establish_connection() has no state check at all, so with the
   state machine now refusing the event, connect(2) would return the
   state machine's "1" failure indication as a positive syscall return
   value.

 - llc_build_and_send_pkt() special cases LLC_CONN_STATE_ADM as
   -ECONNABORTED but falls through to -EBUSY for LLC_CONN_OUT_OF_SVC.
   -EBUSY describes a connection that is momentarily unable to send, not
   one that no longer exists.

Report -ECONNABORTED from both.

There is deliberately no Fixes: tag here. The connect(2) return value
only becomes observable once the previous patch makes the state machine
refuse the event, and the llc_build_and_send_pkt() change is a
long-standing errno inaccuracy with no memory safety impact. Backporting
this on its own would fix nothing.

Build tested ARCH=x86_64 net/llc/ with GCC 14.2.0, CONFIG_LLC2=y and =m.

Assisted-by: Claude:claude-opus-5[1m]
Signed-off-by: Kees Cook <[email protected]>
---
 net/llc/llc_if.c | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/net/llc/llc_if.c b/net/llc/llc_if.c
index 1514362e613d..f1a3f3372c4f 100644
--- a/net/llc/llc_if.c
+++ b/net/llc/llc_if.c
@@ -41,7 +41,8 @@ int llc_build_and_send_pkt(struct sock *sk, struct sk_buff 
*skb)
        int rc = -ECONNABORTED;
        struct llc_sock *llc = llc_sk(sk);
 
-       if (unlikely(llc->state == LLC_CONN_STATE_ADM))
+       if (unlikely(llc->state == LLC_CONN_STATE_ADM ||
+                    llc->state == LLC_CONN_OUT_OF_SVC))
                goto out_free;
        rc = -EBUSY;
        if (unlikely(llc_data_accept_state(llc->state) || /* data_conn_refuse */
@@ -82,6 +83,15 @@ int llc_establish_connection(struct sock *sk, const u8 
*lmac, u8 *dmac, u8 dsap)
        struct llc_sock *llc = llc_sk(sk);
        struct sock *existing;
 
+       /*
+        * A socket parked in LLC_CONN_OUT_OF_SVC has no state machine to run,
+        * so there is nothing to establish. Report it as a closed connection
+        * rather than handing llc_conn_state_process() an event it can only
+        * throw away.
+        */
+       if (unlikely(llc->state == LLC_CONN_OUT_OF_SVC))
+               return -ECONNABORTED;
+
        laddr.lsap = llc->sap->laddr.lsap;
        daddr.lsap = dsap;
        memcpy(daddr.mac, dmac, sizeof(daddr.mac));
-- 
2.34.1


Reply via email to