CVE-2026-46098
Description
In the Linux kernel, the following vulnerability has been resolved:
net: caif: clear client service pointer on teardown
caif_connect() can tear down an existing client after remote shutdown by calling caif_disconnect_client() followed by caif_free_client(). caif_free_client() releases the service layer referenced by adap_layer->dn, but leaves that pointer stale.
When the socket is later destroyed, caif_sock_destructor() calls caif_free_client() again and dereferences the freed service pointer.
Clear the client/service links before releasing the service object so repeated teardown becomes harmless.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
In the Linux kernel, the CAIF protocol's client teardown leaves a stale service pointer, causing a use-after-free when the socket is later destroyed.
Vulnerability
In the Linux kernel's CAIF (CAI F) protocol implementation, a use-after-free vulnerability exists in the client teardown path. When caif_connect() handles a remote shutdown, it calls caif_disconnect_client() followed by caif_free_client(). The caif_free_client() function releases the service layer object referenced by adap_layer->dn but does not clear the pointer. Later, when the socket is destroyed, caif_sock_destructor() calls caif_free_client() again, dereferencing the freed service pointer. This affects all Linux kernel versions prior to the inclusion of commit 914c6456fcfc21a3d553945dff62fd1621d6155d. [1]
Exploitation
An attacker who can trigger a remote shutdown of a CAIF connection and subsequently cause the local socket to be closed can exploit this vulnerability. The remote shutdown causes the initial teardown, and the subsequent socket destruction triggers the use-after-free. No special privileges are required beyond the ability to establish and manipulate a CAIF connection.
Impact
Successful exploitation results in a use-after-free condition in kernel memory. This can lead to a system crash (denial of service) or, under the right memory conditions, arbitrary code execution with kernel privileges. The exact impact depends on the memory allocator state and the attacker's ability to control the freed memory.
Mitigation
The vulnerability is fixed in Linux kernel versions that include commit 914c6456fcfc21a3d553945dff62fd1621d6155d. This commit clears the client/service links before releasing the service object, preventing the double-free. Users should update their kernel to a version containing this fix. No workaround is known. [1]
AI Insight generated on May 27, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
2Patches
10f7cf8ece8ceenet: caif: clear client service pointer on teardown
1 file changed · +12 −3
net/caif/cfsrvl.c+12 −3 modifieddiff --git a/net/caif/cfsrvl.c b/net/caif/cfsrvl.c index 171fa32ada85c0..d687fd0b4ed3a8 100644 --- a/net/caif/cfsrvl.c +++ b/net/caif/cfsrvl.c @@ -191,10 +191,20 @@ bool cfsrvl_phyid_match(struct cflayer *layer, int phyid) void caif_free_client(struct cflayer *adap_layer) { + struct cflayer *serv_layer; struct cfsrvl *servl; - if (adap_layer == NULL || adap_layer->dn == NULL) + + if (!adap_layer) + return; + + serv_layer = adap_layer->dn; + if (!serv_layer) return; - servl = container_obj(adap_layer->dn); + + layer_set_dn(adap_layer, NULL); + layer_set_up(serv_layer, NULL); + + servl = container_obj(serv_layer); servl->release(&servl->layer); } EXPORT_SYMBOL(caif_free_client); -- cgit 1.3-korg
914c6456fcfcnet: caif: clear client service pointer on teardown
1 file changed · +12 −3
net/caif/cfsrvl.c+12 −3 modifieddiff --git a/net/caif/cfsrvl.c b/net/caif/cfsrvl.c index 9cef9496a70705..9a474d99bae8b7 100644 --- a/net/caif/cfsrvl.c +++ b/net/caif/cfsrvl.c @@ -197,10 +197,20 @@ bool cfsrvl_phyid_match(struct cflayer *layer, int phyid) void caif_free_client(struct cflayer *adap_layer) { + struct cflayer *serv_layer; struct cfsrvl *servl; - if (adap_layer == NULL || adap_layer->dn == NULL) + + if (!adap_layer) + return; + + serv_layer = adap_layer->dn; + if (!serv_layer) return; - servl = container_obj(adap_layer->dn); + + layer_set_dn(adap_layer, NULL); + layer_set_up(serv_layer, NULL); + + servl = container_obj(serv_layer); servl->release(&servl->layer); } EXPORT_SYMBOL(caif_free_client); -- cgit 1.3-korg
3ac6db584d9dnet: caif: clear client service pointer on teardown
1 file changed · +12 −3
net/caif/cfsrvl.c+12 −3 modifieddiff --git a/net/caif/cfsrvl.c b/net/caif/cfsrvl.c index 9cef9496a70705..9a474d99bae8b7 100644 --- a/net/caif/cfsrvl.c +++ b/net/caif/cfsrvl.c @@ -197,10 +197,20 @@ bool cfsrvl_phyid_match(struct cflayer *layer, int phyid) void caif_free_client(struct cflayer *adap_layer) { + struct cflayer *serv_layer; struct cfsrvl *servl; - if (adap_layer == NULL || adap_layer->dn == NULL) + + if (!adap_layer) + return; + + serv_layer = adap_layer->dn; + if (!serv_layer) return; - servl = container_obj(adap_layer->dn); + + layer_set_dn(adap_layer, NULL); + layer_set_up(serv_layer, NULL); + + servl = container_obj(serv_layer); servl->release(&servl->layer); } EXPORT_SYMBOL(caif_free_client); -- cgit 1.3-korg
63d21a3aa010net: caif: clear client service pointer on teardown
1 file changed · +12 −3
net/caif/cfsrvl.c+12 −3 modifieddiff --git a/net/caif/cfsrvl.c b/net/caif/cfsrvl.c index 171fa32ada85c0..d687fd0b4ed3a8 100644 --- a/net/caif/cfsrvl.c +++ b/net/caif/cfsrvl.c @@ -191,10 +191,20 @@ bool cfsrvl_phyid_match(struct cflayer *layer, int phyid) void caif_free_client(struct cflayer *adap_layer) { + struct cflayer *serv_layer; struct cfsrvl *servl; - if (adap_layer == NULL || adap_layer->dn == NULL) + + if (!adap_layer) + return; + + serv_layer = adap_layer->dn; + if (!serv_layer) return; - servl = container_obj(adap_layer->dn); + + layer_set_dn(adap_layer, NULL); + layer_set_up(serv_layer, NULL); + + servl = container_obj(serv_layer); servl->release(&servl->layer); } EXPORT_SYMBOL(caif_free_client); -- cgit 1.3-korg
a4b191ddc12cnet: caif: clear client service pointer on teardown
1 file changed · +12 −3
net/caif/cfsrvl.c+12 −3 modifieddiff --git a/net/caif/cfsrvl.c b/net/caif/cfsrvl.c index 171fa32ada85c0..d687fd0b4ed3a8 100644 --- a/net/caif/cfsrvl.c +++ b/net/caif/cfsrvl.c @@ -191,10 +191,20 @@ bool cfsrvl_phyid_match(struct cflayer *layer, int phyid) void caif_free_client(struct cflayer *adap_layer) { + struct cflayer *serv_layer; struct cfsrvl *servl; - if (adap_layer == NULL || adap_layer->dn == NULL) + + if (!adap_layer) + return; + + serv_layer = adap_layer->dn; + if (!serv_layer) return; - servl = container_obj(adap_layer->dn); + + layer_set_dn(adap_layer, NULL); + layer_set_up(serv_layer, NULL); + + servl = container_obj(serv_layer); servl->release(&servl->layer); } EXPORT_SYMBOL(caif_free_client); -- cgit 1.3-korg
3ac6db584d9dnet: caif: clear client service pointer on teardown
1 file changed · +12 −3
net/caif/cfsrvl.c+12 −3 modifieddiff --git a/net/caif/cfsrvl.c b/net/caif/cfsrvl.c index 9cef9496a70705..9a474d99bae8b7 100644 --- a/net/caif/cfsrvl.c +++ b/net/caif/cfsrvl.c @@ -197,10 +197,20 @@ bool cfsrvl_phyid_match(struct cflayer *layer, int phyid) void caif_free_client(struct cflayer *adap_layer) { + struct cflayer *serv_layer; struct cfsrvl *servl; - if (adap_layer == NULL || adap_layer->dn == NULL) + + if (!adap_layer) + return; + + serv_layer = adap_layer->dn; + if (!serv_layer) return; - servl = container_obj(adap_layer->dn); + + layer_set_dn(adap_layer, NULL); + layer_set_up(serv_layer, NULL); + + servl = container_obj(serv_layer); servl->release(&servl->layer); } EXPORT_SYMBOL(caif_free_client); -- cgit 1.3-korg
63d21a3aa010net: caif: clear client service pointer on teardown
1 file changed · +12 −3
net/caif/cfsrvl.c+12 −3 modifieddiff --git a/net/caif/cfsrvl.c b/net/caif/cfsrvl.c index 171fa32ada85c0..d687fd0b4ed3a8 100644 --- a/net/caif/cfsrvl.c +++ b/net/caif/cfsrvl.c @@ -191,10 +191,20 @@ bool cfsrvl_phyid_match(struct cflayer *layer, int phyid) void caif_free_client(struct cflayer *adap_layer) { + struct cflayer *serv_layer; struct cfsrvl *servl; - if (adap_layer == NULL || adap_layer->dn == NULL) + + if (!adap_layer) + return; + + serv_layer = adap_layer->dn; + if (!serv_layer) return; - servl = container_obj(adap_layer->dn); + + layer_set_dn(adap_layer, NULL); + layer_set_up(serv_layer, NULL); + + servl = container_obj(serv_layer); servl->release(&servl->layer); } EXPORT_SYMBOL(caif_free_client); -- cgit 1.3-korg
914c6456fcfcnet: caif: clear client service pointer on teardown
1 file changed · +12 −3
net/caif/cfsrvl.c+12 −3 modifieddiff --git a/net/caif/cfsrvl.c b/net/caif/cfsrvl.c index 9cef9496a70705..9a474d99bae8b7 100644 --- a/net/caif/cfsrvl.c +++ b/net/caif/cfsrvl.c @@ -197,10 +197,20 @@ bool cfsrvl_phyid_match(struct cflayer *layer, int phyid) void caif_free_client(struct cflayer *adap_layer) { + struct cflayer *serv_layer; struct cfsrvl *servl; - if (adap_layer == NULL || adap_layer->dn == NULL) + + if (!adap_layer) + return; + + serv_layer = adap_layer->dn; + if (!serv_layer) return; - servl = container_obj(adap_layer->dn); + + layer_set_dn(adap_layer, NULL); + layer_set_up(serv_layer, NULL); + + servl = container_obj(serv_layer); servl->release(&servl->layer); } EXPORT_SYMBOL(caif_free_client); -- cgit 1.3-korg
a4b191ddc12cnet: caif: clear client service pointer on teardown
1 file changed · +12 −3
net/caif/cfsrvl.c+12 −3 modifieddiff --git a/net/caif/cfsrvl.c b/net/caif/cfsrvl.c index 171fa32ada85c0..d687fd0b4ed3a8 100644 --- a/net/caif/cfsrvl.c +++ b/net/caif/cfsrvl.c @@ -191,10 +191,20 @@ bool cfsrvl_phyid_match(struct cflayer *layer, int phyid) void caif_free_client(struct cflayer *adap_layer) { + struct cflayer *serv_layer; struct cfsrvl *servl; - if (adap_layer == NULL || adap_layer->dn == NULL) + + if (!adap_layer) + return; + + serv_layer = adap_layer->dn; + if (!serv_layer) return; - servl = container_obj(adap_layer->dn); + + layer_set_dn(adap_layer, NULL); + layer_set_up(serv_layer, NULL); + + servl = container_obj(serv_layer); servl->release(&servl->layer); } EXPORT_SYMBOL(caif_free_client); -- cgit 1.3-korg
f7cf8ece8ceenet: caif: clear client service pointer on teardown
1 file changed · +12 −3
net/caif/cfsrvl.c+12 −3 modifieddiff --git a/net/caif/cfsrvl.c b/net/caif/cfsrvl.c index 171fa32ada85c0..d687fd0b4ed3a8 100644 --- a/net/caif/cfsrvl.c +++ b/net/caif/cfsrvl.c @@ -191,10 +191,20 @@ bool cfsrvl_phyid_match(struct cflayer *layer, int phyid) void caif_free_client(struct cflayer *adap_layer) { + struct cflayer *serv_layer; struct cfsrvl *servl; - if (adap_layer == NULL || adap_layer->dn == NULL) + + if (!adap_layer) + return; + + serv_layer = adap_layer->dn; + if (!serv_layer) return; - servl = container_obj(adap_layer->dn); + + layer_set_dn(adap_layer, NULL); + layer_set_up(serv_layer, NULL); + + servl = container_obj(serv_layer); servl->release(&servl->layer); } EXPORT_SYMBOL(caif_free_client); -- cgit 1.3-korg
Vulnerability mechanics
Root cause
"Use-after-free due to stale pointer: caif_free_client() frees the service layer object but does not clear adap_layer->dn, so a second call dereferences freed memory."
Attack vector
An attacker can trigger a use-after-free by causing a CAIF socket to be torn down twice. `caif_connect()` tears down an existing client after remote shutdown by calling `caif_disconnect_client()` followed by `caif_free_client()`. The first call to `caif_free_client()` frees the service layer object but leaves `adap_layer->dn` pointing to freed memory. When the socket is later destroyed, `caif_sock_destructor()` calls `caif_free_client()` again, which dereferences the stale `adap_layer->dn` pointer, leading to a use-after-free [patch_id=2659729].
Affected code
The vulnerability is in the `caif_free_client()` function in `net/caif/cfsrvl.c` [patch_id=2659729]. The function releases the service layer referenced by `adap_layer->dn` but does not clear that pointer, leaving it stale for any subsequent call.
What the fix does
The patch modifies `caif_free_client()` in `net/caif/cfsrvl.c` to clear the cross-layer pointers before releasing the service object [patch_id=2659729]. It saves `adap_layer->dn` into a local variable `serv_layer`, then calls `layer_set_dn(adap_layer, NULL)` and `layer_set_up(serv_layer, NULL)` to null out both the adaptation layer's down pointer and the service layer's up pointer. Only after clearing these links does it call `servl->release()`. This ensures that if `caif_free_client()` is called again on the same adaptation layer, the null check on `adap_layer->dn` will cause an early return, making repeated teardown harmless.
Preconditions
- inputA CAIF socket must have been connected and then remotely shut down, causing caif_connect() to tear down the client.
- inputThe socket must later be destroyed (e.g., via close()), triggering caif_sock_destructor() which calls caif_free_client() again.
Generated on May 27, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- git.kernel.org/stable/c/3ac6db584d9d420267bb8413115707eeec76d9cfnvd
- git.kernel.org/stable/c/63d21a3aa0108b9dde4e99b0d3d5d679ac68c0f9nvd
- git.kernel.org/stable/c/914c6456fcfc21a3d553945dff62fd1621d6155dnvd
- git.kernel.org/stable/c/a4b191ddc12c55ddb62feb096536f819f384d6f1nvd
- git.kernel.org/stable/c/f7cf8ece8cee3c1ee361991470cdb1eb65ab02e8nvd
News mentions
0No linked articles in our index yet.