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

