Commit 82709531 authored by Gerrit Renker's avatar Gerrit Renker Committed by David S. Miller

[DCCP]: Fix Oops in DCCPv6

I think I got the cause for the Oops observed in
http://www.mail-archive.com/dccp@vger.kernel.org/msg00578.html

The problem is always with applications listening on PF_INET6 sockets. Apart
from the mentioned oops, I observed another one one, triggered at irregular
intervals via timer interrupt:

    run_timer_softirq -> dccp_keepalive_timer
                      -> inet_csk_reqsk_queue_prune
                      -> reqsk_free
                      -> dccp_v6_reqsk_destructor

The latter function is the problem and is also the last function to be called
in said kernel panic.

In any case, there is a real problem with allocating the right request_sock
which is what this patch tackles.

It fixes the following problem:
 - application listens on PF_INET6
 - DCCPv4 packet comes in, is handed over to dccp_v4_do_rcv, from there
   to dccp_v4_conn_request

Now: socket is PF_INET6, packet is IPv4. The following code then furnishes the
connection with IPv6 - request_sock operations:

   req = reqsk_alloc(sk->sk_prot->rsk_prot);

The first problem is that all further incoming packets will get a Reset since
the connection can not be looked up.

The second problem is worse:
 --> reqsk_alloc is called instead of inet6_reqsk_alloc
 --> consequently inet6_rsk_offset is never set (dangling pointer)
 --> the request_sock_ops are nevertheless still dccp6_request_ops
 --> destructor is called via reqsk_free
 --> dccp_v6_reqsk_destructor tries to free random memory location (inet6_rsk_offset not set)
 --> panic

I have tested this for a while, DCCP sockets are now handled correctly in all
three scenarios (v4/v6 only/v4-mapped).

Commiter note: I've added the dccp_request_sock_ops forward declaration to keep
               the tree building and to reduce the size of the patch for 2.6.19,
               later I'll move the functions to the top of the affected source
               code to match what we have in the TCP counterpart, where this
               problem hasn't existed in the first place, dumb me not to have
               done the same thing on DCCP land 8)
Signed-off-by: default avatarGerrit Renker <gerrit@erg.abdn.ac.uk>
Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@mandriva.com>
parent 5cfc35cf
...@@ -449,6 +449,8 @@ static inline u64 dccp_v4_init_sequence(const struct sock *sk, ...@@ -449,6 +449,8 @@ static inline u64 dccp_v4_init_sequence(const struct sock *sk,
dccp_hdr(skb)->dccph_sport); dccp_hdr(skb)->dccph_sport);
} }
static struct request_sock_ops dccp_request_sock_ops;
int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb) int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
{ {
struct inet_request_sock *ireq; struct inet_request_sock *ireq;
...@@ -489,7 +491,7 @@ int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb) ...@@ -489,7 +491,7 @@ int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1) if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1)
goto drop; goto drop;
req = reqsk_alloc(sk->sk_prot->rsk_prot); req = reqsk_alloc(&dccp_request_sock_ops);
if (req == NULL) if (req == NULL)
goto drop; goto drop;
......
...@@ -672,7 +672,6 @@ static struct sock *dccp_v6_hnd_req(struct sock *sk,struct sk_buff *skb) ...@@ -672,7 +672,6 @@ static struct sock *dccp_v6_hnd_req(struct sock *sk,struct sk_buff *skb)
static int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb) static int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
{ {
struct inet_request_sock *ireq;
struct dccp_sock dp; struct dccp_sock dp;
struct request_sock *req; struct request_sock *req;
struct dccp_request_sock *dreq; struct dccp_request_sock *dreq;
...@@ -701,7 +700,7 @@ static int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb) ...@@ -701,7 +700,7 @@ static int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1) if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1)
goto drop; goto drop;
req = inet6_reqsk_alloc(sk->sk_prot->rsk_prot); req = inet6_reqsk_alloc(&dccp6_request_sock_ops);
if (req == NULL) if (req == NULL)
goto drop; goto drop;
...@@ -713,7 +712,6 @@ static int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb) ...@@ -713,7 +712,6 @@ static int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
goto drop_and_free; goto drop_and_free;
ireq6 = inet6_rsk(req); ireq6 = inet6_rsk(req);
ireq = inet_rsk(req);
ipv6_addr_copy(&ireq6->rmt_addr, &skb->nh.ipv6h->saddr); ipv6_addr_copy(&ireq6->rmt_addr, &skb->nh.ipv6h->saddr);
ipv6_addr_copy(&ireq6->loc_addr, &skb->nh.ipv6h->daddr); ipv6_addr_copy(&ireq6->loc_addr, &skb->nh.ipv6h->daddr);
req->rcv_wnd = dccp_feat_default_sequence_window; req->rcv_wnd = dccp_feat_default_sequence_window;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment