Skip to contentSkip to conversation

Fix potential kfree on vmalloc-ed area of request_sock_queue

PATCH In review Pavel Emelyanov 3 participants 3 messages Nov 14, 2007 – Nov 15, 2007

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

PATCH 1/2 Fix potential kfree on vmalloc-ed area of request_sock_queue

The request_sock_queue's listen_opt is either vmalloc-ed or kmalloc-ed depending on the number of table entries. Thus it is expected to be handled properly on free, which is done in the reqsk_queue_destroy().

However the error path in inet_csk_listen_start() calls the lite version of reqsk_queue_destroy, called __reqsk_queue_destroy, which calls the kfree unconditionally.

Fix this and move the __reqsk_queue_destroy into a .c file as it looks too big to be inline.

---

include/net/request_sock.h +1 -5
index 7aed02c..0a954ee 100644
@@ -136,11 +136,7 @@ static inline struct listen_sock *reqsk_queue_yank_listen_sk(struct request_sock
 	return lopt;
 }
 
-static inline void __reqsk_queue_destroy(struct request_sock_queue *queue)
-{
-	kfree(reqsk_queue_yank_listen_sk(queue));
-}
-
+extern void __reqsk_queue_destroy(struct request_sock_queue *queue);
 extern void reqsk_queue_destroy(struct request_sock_queue *queue);
 
 static inline struct request_sock *
net/core/request_sock.c +14 -0
index 5f0818d..12a15f5 100644
@@ -71,6 +71,20 @@ int reqsk_queue_alloc(struct request_sock_queue *queue,
 
 EXPORT_SYMBOL(reqsk_queue_alloc);
 
+void __reqsk_queue_destroy(struct request_sock_queue *queue)
+{
+	struct listen_sock *lopt = reqsk_queue_yank_listen_sk(queue);
+	size_t lopt_size = sizeof(struct listen_sock) +
+		lopt->nr_table_entries * sizeof(struct request_sock *);
+
+	if (lopt_size > PAGE_SIZE)
+		vfree(lopt);
+	else
+		kfree(lopt);
+}
+
+EXPORT_SYMBOL(__reqsk_queue_destroy);
+
 void reqsk_queue_destroy(struct request_sock_queue *queue)
 {
 	/* make all the listen_opt local to us */
#1 lore raw
Quoted 17 lines · On Wed, 14 Nov 2007 21:08:29 +0300 Pavel Emelyanov <[email protected]> wrote:

The request_sock_queue's listen_opt is either vmalloc-ed or kmalloc-ed depending on the number of table entries. Thus it is expected to be handled properly on free, which is done in the reqsk_queue_destroy().

However the error path in inet_csk_listen_start() calls the lite version of reqsk_queue_destroy, called __reqsk_queue_destroy, which calls the kfree unconditionally.

Fix this and move the __reqsk_queue_destroy into a .c file as it looks too big to be inline.

---

net/core/request_sock.c int reqsk_queue_alloc(struct request_sock_queue *queue, · line 76

Quoted 3 lines
net/core/request_sock.c +3 -0
+void __reqsk_queue_destroy(struct request_sock_queue *queue)
+{
+	struct listen_sock *lopt = reqsk_queue_yank_listen_sk(queue);

WARNING : lopt can be NULL here (or else the locking in reqsk_queue_yank_listen_sk() would be useless ?)

kfree(NULL) was ok, not NULL->nr_table_entries :)

Quoted 8 lines
net/core/request_sock.c +8 -0
+	size_t lopt_size = sizeof(struct listen_sock) +
+		lopt->nr_table_entries * sizeof(struct request_sock *);
+
+	if (lopt_size > PAGE_SIZE)
+		vfree(lopt);
+	else
+		kfree(lopt);
+}
maintainer
#2 lore raw

From: Eric Dumazet <[email protected]>
Date: Wed, 14 Nov 2007 20:42:38 +0100

Quoted 20 lines
Quoted 16 lines · On Wed, 14 Nov 2007 21:08:29 +0300 Pavel Emelyanov <[email protected]> wrote:

The request_sock_queue's listen_opt is either vmalloc-ed or kmalloc-ed depending on the number of table entries. Thus it is expected to be handled properly on free, which is done in the reqsk_queue_destroy().

However the error path in inet_csk_listen_start() calls the lite version of reqsk_queue_destroy, called __reqsk_queue_destroy, which calls the kfree unconditionally.

Fix this and move the __reqsk_queue_destroy into a .c file as it looks too big to be inline.

---

Quoted 7 lines

net/core/request_sock.c int reqsk_queue_alloc(struct request_sock_queue *queue, · line 76

Quoted 3 lines
net/core/request_sock.c +3 -0
+void __reqsk_queue_destroy(struct request_sock_queue *queue)
+{
+	struct listen_sock *lopt = reqsk_queue_yank_listen_sk(queue);

WARNING : lopt can be NULL here (or else the locking in reqsk_queue_yank_listen_sk() would be useless ?)

kfree(NULL) was ok, not NULL->nr_table_entries :)

I think for the error recovery case he is trying to fix all of this locking is unnecessary and we know lopt is not NULL.

Pavel can you rework your fix a bit to deal with this?

Thanks a lot.