diff options
author | Patrick McHardy <kaber@trash.net> | 2005-08-14 22:27:13 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2005-08-29 19:00:45 -0400 |
commit | 77247bbb3094246be9d057e7be442cc708f123a8 (patch) | |
tree | f6129e1aa25be790fdf38d5f39e1d38b2fa19587 /net | |
parent | db080529798b497eb5a37b92a25e966be5a7dd5d (diff) |
[NETLINK]: Fix module refcounting problems
Use-after-free: the struct proto_ops containing the module pointer
is freed when a socket with pid=0 is released, which besides for kernel
sockets is true for all unbound sockets.
Module refcount leak: when the kernel socket is closed before all user
sockets have been closed the proto_ops struct for this family is
replaced by the generic one and the module refcount can't be dropped.
The second problem can't be solved cleanly using module refcounting in the
generic socket code, so this patch adds explicit refcounting to
netlink_create/netlink_release.
Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/netlink/af_netlink.c | 100 |
1 files changed, 35 insertions, 65 deletions
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 7b7b45a19597..c41a88100fea 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c | |||
@@ -73,8 +73,12 @@ struct netlink_sock { | |||
73 | struct netlink_callback *cb; | 73 | struct netlink_callback *cb; |
74 | spinlock_t cb_lock; | 74 | spinlock_t cb_lock; |
75 | void (*data_ready)(struct sock *sk, int bytes); | 75 | void (*data_ready)(struct sock *sk, int bytes); |
76 | struct module *module; | ||
77 | u32 flags; | ||
76 | }; | 78 | }; |
77 | 79 | ||
80 | #define NETLINK_KERNEL_SOCKET 0x1 | ||
81 | |||
78 | static inline struct netlink_sock *nlk_sk(struct sock *sk) | 82 | static inline struct netlink_sock *nlk_sk(struct sock *sk) |
79 | { | 83 | { |
80 | return (struct netlink_sock *)sk; | 84 | return (struct netlink_sock *)sk; |
@@ -97,7 +101,7 @@ struct netlink_table { | |||
97 | struct nl_pid_hash hash; | 101 | struct nl_pid_hash hash; |
98 | struct hlist_head mc_list; | 102 | struct hlist_head mc_list; |
99 | unsigned int nl_nonroot; | 103 | unsigned int nl_nonroot; |
100 | struct proto_ops *p_ops; | 104 | struct module *module; |
101 | }; | 105 | }; |
102 | 106 | ||
103 | static struct netlink_table *nl_table; | 107 | static struct netlink_table *nl_table; |
@@ -338,6 +342,7 @@ static int netlink_create(struct socket *sock, int protocol) | |||
338 | { | 342 | { |
339 | struct sock *sk; | 343 | struct sock *sk; |
340 | struct netlink_sock *nlk; | 344 | struct netlink_sock *nlk; |
345 | struct module *module; | ||
341 | 346 | ||
342 | sock->state = SS_UNCONNECTED; | 347 | sock->state = SS_UNCONNECTED; |
343 | 348 | ||
@@ -347,30 +352,36 @@ static int netlink_create(struct socket *sock, int protocol) | |||
347 | if (protocol<0 || protocol >= MAX_LINKS) | 352 | if (protocol<0 || protocol >= MAX_LINKS) |
348 | return -EPROTONOSUPPORT; | 353 | return -EPROTONOSUPPORT; |
349 | 354 | ||
350 | netlink_table_grab(); | 355 | netlink_lock_table(); |
351 | if (!nl_table[protocol].hash.entries) { | 356 | if (!nl_table[protocol].hash.entries) { |
352 | #ifdef CONFIG_KMOD | 357 | #ifdef CONFIG_KMOD |
353 | /* We do 'best effort'. If we find a matching module, | 358 | /* We do 'best effort'. If we find a matching module, |
354 | * it is loaded. If not, we don't return an error to | 359 | * it is loaded. If not, we don't return an error to |
355 | * allow pure userspace<->userspace communication. -HW | 360 | * allow pure userspace<->userspace communication. -HW |
356 | */ | 361 | */ |
357 | netlink_table_ungrab(); | 362 | netlink_unlock_table(); |
358 | request_module("net-pf-%d-proto-%d", PF_NETLINK, protocol); | 363 | request_module("net-pf-%d-proto-%d", PF_NETLINK, protocol); |
359 | netlink_table_grab(); | 364 | netlink_lock_table(); |
360 | #endif | 365 | #endif |
361 | } | 366 | } |
362 | netlink_table_ungrab(); | 367 | module = nl_table[protocol].module; |
368 | if (!try_module_get(module)) | ||
369 | module = NULL; | ||
370 | netlink_unlock_table(); | ||
363 | 371 | ||
364 | sock->ops = nl_table[protocol].p_ops; | 372 | sock->ops = &netlink_ops; |
365 | 373 | ||
366 | sk = sk_alloc(PF_NETLINK, GFP_KERNEL, &netlink_proto, 1); | 374 | sk = sk_alloc(PF_NETLINK, GFP_KERNEL, &netlink_proto, 1); |
367 | if (!sk) | 375 | if (!sk) { |
376 | module_put(module); | ||
368 | return -ENOMEM; | 377 | return -ENOMEM; |
378 | } | ||
369 | 379 | ||
370 | sock_init_data(sock, sk); | 380 | sock_init_data(sock, sk); |
371 | 381 | ||
372 | nlk = nlk_sk(sk); | 382 | nlk = nlk_sk(sk); |
373 | 383 | ||
384 | nlk->module = module; | ||
374 | spin_lock_init(&nlk->cb_lock); | 385 | spin_lock_init(&nlk->cb_lock); |
375 | init_waitqueue_head(&nlk->wait); | 386 | init_waitqueue_head(&nlk->wait); |
376 | sk->sk_destruct = netlink_sock_destruct; | 387 | sk->sk_destruct = netlink_sock_destruct; |
@@ -415,22 +426,15 @@ static int netlink_release(struct socket *sock) | |||
415 | notifier_call_chain(&netlink_chain, NETLINK_URELEASE, &n); | 426 | notifier_call_chain(&netlink_chain, NETLINK_URELEASE, &n); |
416 | } | 427 | } |
417 | 428 | ||
418 | /* When this is a kernel socket, we need to remove the owner pointer, | 429 | if (nlk->module) |
419 | * since we don't know whether the module will be dying at any given | 430 | module_put(nlk->module); |
420 | * point - HW | ||
421 | */ | ||
422 | if (!nlk->pid) { | ||
423 | struct proto_ops *p_tmp; | ||
424 | 431 | ||
432 | if (nlk->flags & NETLINK_KERNEL_SOCKET) { | ||
425 | netlink_table_grab(); | 433 | netlink_table_grab(); |
426 | p_tmp = nl_table[sk->sk_protocol].p_ops; | 434 | nl_table[sk->sk_protocol].module = NULL; |
427 | if (p_tmp != &netlink_ops) { | ||
428 | nl_table[sk->sk_protocol].p_ops = &netlink_ops; | ||
429 | kfree(p_tmp); | ||
430 | } | ||
431 | netlink_table_ungrab(); | 435 | netlink_table_ungrab(); |
432 | } | 436 | } |
433 | 437 | ||
434 | sock_put(sk); | 438 | sock_put(sk); |
435 | return 0; | 439 | return 0; |
436 | } | 440 | } |
@@ -1060,9 +1064,9 @@ static void netlink_data_ready(struct sock *sk, int len) | |||
1060 | struct sock * | 1064 | struct sock * |
1061 | netlink_kernel_create(int unit, void (*input)(struct sock *sk, int len), struct module *module) | 1065 | netlink_kernel_create(int unit, void (*input)(struct sock *sk, int len), struct module *module) |
1062 | { | 1066 | { |
1063 | struct proto_ops *p_ops; | ||
1064 | struct socket *sock; | 1067 | struct socket *sock; |
1065 | struct sock *sk; | 1068 | struct sock *sk; |
1069 | struct netlink_sock *nlk; | ||
1066 | 1070 | ||
1067 | if (!nl_table) | 1071 | if (!nl_table) |
1068 | return NULL; | 1072 | return NULL; |
@@ -1070,64 +1074,32 @@ netlink_kernel_create(int unit, void (*input)(struct sock *sk, int len), struct | |||
1070 | if (unit<0 || unit>=MAX_LINKS) | 1074 | if (unit<0 || unit>=MAX_LINKS) |
1071 | return NULL; | 1075 | return NULL; |
1072 | 1076 | ||
1073 | /* Do a quick check, to make us not go down to netlink_insert() | ||
1074 | * if protocol already has kernel socket. | ||
1075 | */ | ||
1076 | sk = netlink_lookup(unit, 0); | ||
1077 | if (unlikely(sk)) { | ||
1078 | sock_put(sk); | ||
1079 | return NULL; | ||
1080 | } | ||
1081 | |||
1082 | if (sock_create_lite(PF_NETLINK, SOCK_DGRAM, unit, &sock)) | 1077 | if (sock_create_lite(PF_NETLINK, SOCK_DGRAM, unit, &sock)) |
1083 | return NULL; | 1078 | return NULL; |
1084 | 1079 | ||
1085 | sk = NULL; | 1080 | if (netlink_create(sock, unit) < 0) |
1086 | if (module) { | 1081 | goto out_sock_release; |
1087 | /* Every registering protocol implemented in a module needs | ||
1088 | * it's own p_ops, since the socket code cannot deal with | ||
1089 | * module refcounting otherwise. -HW | ||
1090 | */ | ||
1091 | p_ops = kmalloc(sizeof(*p_ops), GFP_KERNEL); | ||
1092 | if (!p_ops) | ||
1093 | goto out_sock_release; | ||
1094 | |||
1095 | memcpy(p_ops, &netlink_ops, sizeof(*p_ops)); | ||
1096 | p_ops->owner = module; | ||
1097 | } else | ||
1098 | p_ops = &netlink_ops; | ||
1099 | |||
1100 | netlink_table_grab(); | ||
1101 | nl_table[unit].p_ops = p_ops; | ||
1102 | netlink_table_ungrab(); | ||
1103 | |||
1104 | if (netlink_create(sock, unit) < 0) { | ||
1105 | sk = NULL; | ||
1106 | goto out_kfree_p_ops; | ||
1107 | } | ||
1108 | 1082 | ||
1109 | sk = sock->sk; | 1083 | sk = sock->sk; |
1110 | sk->sk_data_ready = netlink_data_ready; | 1084 | sk->sk_data_ready = netlink_data_ready; |
1111 | if (input) | 1085 | if (input) |
1112 | nlk_sk(sk)->data_ready = input; | 1086 | nlk_sk(sk)->data_ready = input; |
1113 | 1087 | ||
1114 | if (netlink_insert(sk, 0)) { | 1088 | if (netlink_insert(sk, 0)) |
1115 | sk = NULL; | 1089 | goto out_sock_release; |
1116 | goto out_kfree_p_ops; | ||
1117 | } | ||
1118 | 1090 | ||
1119 | return sk; | 1091 | nlk = nlk_sk(sk); |
1092 | nlk->flags |= NETLINK_KERNEL_SOCKET; | ||
1120 | 1093 | ||
1121 | out_kfree_p_ops: | ||
1122 | netlink_table_grab(); | 1094 | netlink_table_grab(); |
1123 | if (nl_table[unit].p_ops != &netlink_ops) { | 1095 | nl_table[unit].module = module; |
1124 | kfree(nl_table[unit].p_ops); | ||
1125 | nl_table[unit].p_ops = &netlink_ops; | ||
1126 | } | ||
1127 | netlink_table_ungrab(); | 1096 | netlink_table_ungrab(); |
1097 | |||
1098 | return sk; | ||
1099 | |||
1128 | out_sock_release: | 1100 | out_sock_release: |
1129 | sock_release(sock); | 1101 | sock_release(sock); |
1130 | return sk; | 1102 | return NULL; |
1131 | } | 1103 | } |
1132 | 1104 | ||
1133 | void netlink_set_nonroot(int protocol, unsigned int flags) | 1105 | void netlink_set_nonroot(int protocol, unsigned int flags) |
@@ -1490,8 +1462,6 @@ enomem: | |||
1490 | for (i = 0; i < MAX_LINKS; i++) { | 1462 | for (i = 0; i < MAX_LINKS; i++) { |
1491 | struct nl_pid_hash *hash = &nl_table[i].hash; | 1463 | struct nl_pid_hash *hash = &nl_table[i].hash; |
1492 | 1464 | ||
1493 | nl_table[i].p_ops = &netlink_ops; | ||
1494 | |||
1495 | hash->table = nl_pid_hash_alloc(1 * sizeof(*hash->table)); | 1465 | hash->table = nl_pid_hash_alloc(1 * sizeof(*hash->table)); |
1496 | if (!hash->table) { | 1466 | if (!hash->table) { |
1497 | while (i-- > 0) | 1467 | while (i-- > 0) |