Skip to content

Commit

Permalink
[INET]: Fix potential kfree on vmalloc-ed area of request_sock_queue
Browse files Browse the repository at this point in the history
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.

As David also noticed, this is an error recovery path only,
so no locking is required and the lopt is known to be not NULL.

reqsk_queue_yank_listen_sk is also now only used in
net/core/request_sock.c so we should move it there too.

Signed-off-by: Pavel Emelyanov <xemul@openvz.org>
Acked-by: Eric Dumazet <dada1@cosmosbay.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Pavel Emelyanov authored and David S. Miller committed Nov 15, 2007
1 parent bd7b3f3 commit dab6ba3
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 17 deletions.
18 changes: 1 addition & 17 deletions include/net/request_sock.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,23 +124,7 @@ struct request_sock_queue {
extern int reqsk_queue_alloc(struct request_sock_queue *queue,
unsigned int nr_table_entries);

static inline struct listen_sock *reqsk_queue_yank_listen_sk(struct request_sock_queue *queue)
{
struct listen_sock *lopt;

write_lock_bh(&queue->syn_wait_lock);
lopt = queue->listen_opt;
queue->listen_opt = NULL;
write_unlock_bh(&queue->syn_wait_lock);

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 *
Expand Down
35 changes: 35 additions & 0 deletions net/core/request_sock.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,41 @@ 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;
size_t lopt_size;

/*
* this is an error recovery path only
* no locking needed and the lopt is not NULL
*/

lopt = queue->listen_opt;
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);

static inline struct listen_sock *reqsk_queue_yank_listen_sk(
struct request_sock_queue *queue)
{
struct listen_sock *lopt;

write_lock_bh(&queue->syn_wait_lock);
lopt = queue->listen_opt;
queue->listen_opt = NULL;
write_unlock_bh(&queue->syn_wait_lock);

return lopt;
}

void reqsk_queue_destroy(struct request_sock_queue *queue)
{
/* make all the listen_opt local to us */
Expand Down

0 comments on commit dab6ba3

Please sign in to comment.