[PATCH net] net: pin protocol module before socket allocation

Chengfeng Ye <[email protected]>
Newsgroups org.kernel.vger.netdev,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
sk_prot_alloc() reads prot->slab and starts allocating the socket
before it gets a reference to prot->owner. A protocol module can begin
unloading after its protocol was selected but before the reference is
taken, allowing this interleaving:

  CPU 0                                CPU 1
  slab = prot->slab
                                       proto_unregister(prot)
                                         kmem_cache_destroy(prot->slab)
  kmem_cache_alloc(slab, ...)

kmem_cache_alloc() then dereferences a freed struct kmem_cache and can
crash or corrupt memory. The kernel reported:

  Oops: general protection fault, probably for non-canonical address
  KASAN: maybe wild-memory-access in range
  RIP: kmem_cache_alloc_noprof+0x63/0x370
  Call Trace:
   sk_prot_alloc+0x74/0x2c0
   sk_alloc+0x2b/0x6c0
   inet_create+0x2cd/0xd40
   __sock_create+0x1c3/0x430
   __sys_socket+0x116/0x1d0

Take the module reference before reading prot->slab so module removal
cannot destroy the cache during allocation. Drop that reference after
freeing the allocation on either failure path; on success sk_prot_free()
continues to release it as before.

Fixes: a79af59efd20 ("[NET]: Fix module reference counts for loadable protocol modules")
Signed-off-by: Chengfeng Ye <[email protected]>
---
 net/core/sock.c | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/net/core/sock.c b/net/core/sock.c
index 1ad41904db25..59f4b15fd594 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -2240,33 +2240,34 @@ static struct sock *sk_prot_alloc(struct proto *prot, gfp_t priority,
 	struct sock *sk;
 	struct kmem_cache *slab;
 
+	if (!try_module_get(prot->owner))
+		return NULL;
+
 	slab = prot->slab;
 	if (slab != NULL) {
 		sk = kmem_cache_alloc(slab, priority & ~__GFP_ZERO);
 		if (!sk)
-			return sk;
+			goto out_module_put;
 		if (want_init_on_alloc(priority))
 			sk_prot_clear_nulls(sk, prot->obj_size);
 	} else
 		sk = kmalloc(prot->obj_size, priority);
 
-	if (sk != NULL) {
-		if (security_sk_alloc(sk, family, priority))
-			goto out_free;
-
-		if (!try_module_get(prot->owner))
-			goto out_free_sec;
-	}
+	if (!sk)
+		goto out_module_put;
+
+	if (security_sk_alloc(sk, family, priority))
+		goto out_free;
 
 	return sk;
 
-out_free_sec:
-	security_sk_free(sk);
 out_free:
 	if (slab != NULL)
 		kmem_cache_free(slab, sk);
 	else
 		kfree(sk);
+out_module_put:
+	module_put(prot->owner);
 	return NULL;
 }
 
-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.