From 1c80cbb69f05a3237fa4778ae4b3462467f647e7 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Wed, 17 Aug 2011 16:43:00 -0700 Subject: netfilter: xt_qtaguid: Fix socket refcounts when tagging * Don't hold the sockets after tagging. sockfd_lookup() does a get() on the associated file. There was no matching put() so a closed socket could never be freed. * Don't rely on struct member order for tag_node The structs that had a struct tag_node member would work with the *_tree_* routines only because tag_node was 1st. * Improve debug messages Provide info on who the caller is. Use unsigned int for uid. * Only process NETDEV_UP events. * Pacifier: disable netfilter matching. Leave .../stats header. Change-Id: Iccb8ae3cca9608210c417597287a2391010dff2c Signed-off-by: JP Abgrall --- net/netfilter/xt_qtaguid.c | 309 +++++++++++++++++++++++++++------------------ 1 file changed, 186 insertions(+), 123 deletions(-) (limited to 'net') diff --git a/net/netfilter/xt_qtaguid.c b/net/netfilter/xt_qtaguid.c index b04b4710574e..b9dcfde99762 100644 --- a/net/netfilter/xt_qtaguid.c +++ b/net/netfilter/xt_qtaguid.c @@ -183,7 +183,6 @@ struct tag_stat { * matching parent uid_tag. */ struct data_counters *parent_counters; - struct proc_dir_entry *proc_ptr; }; struct iface_stat { @@ -215,8 +214,11 @@ struct iface_stat_work { * This is the tag against which tag_stat.counters will be billed. */ struct sock_tag { - struct rb_node node; - struct sock *sk; + struct rb_node sock_node; + struct sock *sk; /* Only used as a number, never dereferenced */ + /* The socket is needed for sockfd_put() */ + struct socket *socket; + tag_t tag; }; @@ -345,24 +347,31 @@ static void tag_node_tree_insert(struct tag_node *data, struct rb_root *root) static void tag_stat_tree_insert(struct tag_stat *data, struct rb_root *root) { - tag_node_tree_insert((struct tag_node *)data, root); + tag_node_tree_insert(&data->tn, root); } static struct tag_stat *tag_stat_tree_search(struct rb_root *root, tag_t tag) { - return (struct tag_stat *)tag_node_tree_search(root, tag); + struct tag_node *node = tag_node_tree_search(root, tag); + if (!node) + return NULL; + return rb_entry(&node->node, struct tag_stat, tn.node); } static void tag_counter_set_tree_insert(struct tag_counter_set *data, struct rb_root *root) { - tag_node_tree_insert((struct tag_node *)data, root); + tag_node_tree_insert(&data->tn, root); } static struct tag_counter_set *tag_counter_set_tree_search(struct rb_root *root, tag_t tag) { - return (struct tag_counter_set *)tag_node_tree_search(root, tag); + struct tag_node *node = tag_node_tree_search(root, tag); + if (!node) + return NULL; + return rb_entry(&node->node, struct tag_counter_set, tn.node); + } static struct sock_tag *sock_tag_tree_search(struct rb_root *root, @@ -371,7 +380,8 @@ static struct sock_tag *sock_tag_tree_search(struct rb_root *root, struct rb_node *node = root->rb_node; while (node) { - struct sock_tag *data = rb_entry(node, struct sock_tag, node); + struct sock_tag *data = rb_entry(node, struct sock_tag, + sock_node); ptrdiff_t result = sk - data->sk; if (result < 0) node = node->rb_left; @@ -389,7 +399,8 @@ static void sock_tag_tree_insert(struct sock_tag *data, struct rb_root *root) /* Figure out where to put new node */ while (*new) { - struct sock_tag *this = rb_entry(*new, struct sock_tag, node); + struct sock_tag *this = rb_entry(*new, struct sock_tag, + sock_node); ptrdiff_t result = data->sk - this->sk; parent = *new; if (result < 0) @@ -401,8 +412,8 @@ static void sock_tag_tree_insert(struct sock_tag *data, struct rb_root *root) } /* Add new node and rebalance tree. */ - rb_link_node(&data->node, parent, new); - rb_insert_color(&data->node, root); + rb_link_node(&data->sock_node, parent, new); + rb_insert_color(&data->sock_node, root); } static int read_proc_u64(char *page, char **start, off_t off, @@ -412,6 +423,7 @@ static int read_proc_u64(char *page, char **start, off_t off, uint64_t value; char *p = page; uint64_t *iface_entry = data; + if (!data) return 0; @@ -430,6 +442,7 @@ static int read_proc_bool(char *page, char **start, off_t off, bool value; char *p = page; bool *bool_entry = data; + if (!data) return 0; @@ -447,7 +460,7 @@ static int get_active_counter_set(tag_t tag) struct tag_counter_set *tcs; MT_DEBUG("qtaguid: get_active_counter_set(tag=0x%llx)" - " (uid=%d)\n", + " (uid=%u)\n", tag, get_uid_from_tag(tag)); /* For now we only handle UID tags for active sets */ tag = get_utag_from_tag(tag); @@ -469,11 +482,10 @@ static struct iface_stat *get_iface_entry(const char *ifname) /* Find the entry for tracking the specified tag within the interface */ if (ifname == NULL) { - pr_info("iface_stat: NULL device name\n"); + pr_info("qtaguid: iface_stat: get() NULL device name\n"); return NULL; } - /* Iterate over interfaces */ list_for_each_entry(iface_entry, &iface_stat_list, list) { if (!strcmp(ifname, iface_entry->ifname)) @@ -525,12 +537,14 @@ static struct iface_stat *iface_alloc(const char *ifname) new_iface = kzalloc(sizeof(*new_iface), GFP_ATOMIC); if (new_iface == NULL) { - pr_err("qtaguid: iface_stat: create(): failed to alloc iface_stat\n"); + pr_err("qtaguid: iface_stat: create(%s): " + "iface_stat alloc failed\n", ifname); return NULL; } new_iface->ifname = kstrdup(ifname, GFP_ATOMIC); if (new_iface->ifname == NULL) { - pr_err("qtaguid: iface_stat: create(): failed to alloc ifname\n"); + pr_err("qtaguid: iface_stat: create(%s): " + "ifname alloc failed\n", ifname); kfree(new_iface); return NULL; } @@ -544,8 +558,8 @@ static struct iface_stat *iface_alloc(const char *ifname) */ isw = kmalloc(sizeof(*isw), GFP_ATOMIC); if (!isw) { - pr_err("qtaguid: iface_stat: create(): " - "failed to alloc work for %s\n", new_iface->ifname); + pr_err("qtaguid: iface_stat: create(%s): " + "work alloc failed\n", new_iface->ifname); kfree(new_iface->ifname); kfree(new_iface); return NULL; @@ -571,10 +585,11 @@ void iface_stat_create(const struct net_device *net_dev, __be32 ipaddr = 0; struct iface_stat *new_iface; - IF_DEBUG("qtaguid: iface_stat: create(): ifa=%p netdev=%p->name=%s\n", - ifa, net_dev, net_dev ? net_dev->name : ""); + IF_DEBUG("qtaguid: iface_stat: create(%s): ifa=%p netdev=%p\n", + net_dev ? net_dev->name : "?", + ifa, net_dev); if (!net_dev) { - pr_err("qtaguid: iface_stat: create(): no net dev!\n"); + pr_err("qtaguid: iface_stat: create(): no net dev\n"); return; } @@ -582,16 +597,16 @@ void iface_stat_create(const struct net_device *net_dev, if (!ifa) { in_dev = in_dev_get(net_dev); if (!in_dev) { - pr_err("qtaguid: iface_stat: create(): " - "no inet dev for %s!\n", ifname); + pr_err("qtaguid: iface_stat: create(%s): no inet dev\n", + ifname); return; } - IF_DEBUG("qtaguid: iface_stat: create(): in_dev=%p ifname=%p\n", - in_dev, ifname); + IF_DEBUG("qtaguid: iface_stat: create(%s): in_dev=%p\n", + ifname, in_dev); for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { - IF_DEBUG("qtaguid: iface_stat: create(): " - "ifa=%p ifname=%s ifa_label=%s\n", - ifa, ifname, + IF_DEBUG("qtaguid: iface_stat: create(%s): " + "ifa=%p ifa_label=%s\n", + ifname, ifa, ifa->ifa_label ? ifa->ifa_label : "(null)"); if (ifa->ifa_label && !strcmp(ifname, ifa->ifa_label)) break; @@ -599,8 +614,7 @@ void iface_stat_create(const struct net_device *net_dev, } if (!ifa) { - IF_DEBUG("qtaguid: iface_stat: create(): " - "dev %s has no matching IP\n", + IF_DEBUG("qtaguid: iface_stat: create(%s): no matching IP\n", ifname); goto done_put; } @@ -609,29 +623,29 @@ void iface_stat_create(const struct net_device *net_dev, spin_lock_bh(&iface_stat_list_lock); entry = get_iface_entry(ifname); if (entry != NULL) { - IF_DEBUG("qtaguid: iface_stat: create(): dev %s entry=%p\n", + IF_DEBUG("qtaguid: iface_stat: create(%s): entry=%p\n", ifname, entry); if (ipv4_is_loopback(ipaddr)) { entry->active = false; - IF_DEBUG("qtaguid: iface_stat: create(): " - "disable tracking of loopback dev %s\n", + IF_DEBUG("qtaguid: iface_stat: create(%s): " + "disable tracking of loopback dev\n", ifname); } else { entry->active = true; - IF_DEBUG("qtaguid: iface_stat: create(): " - "enable tracking of dev %s with ip=%pI4\n", + IF_DEBUG("qtaguid: iface_stat: create(%s): " + "enable tracking. ip=%pI4\n", ifname, &ipaddr); } goto done_unlock_put; } else if (ipv4_is_loopback(ipaddr)) { - IF_DEBUG("qtaguid: iface_stat: create(): ignore loopback dev %s" - " ip=%pI4\n", ifname, &ipaddr); + IF_DEBUG("qtaguid: iface_stat: create(%s): " + "ignore loopback dev. ip=%pI4\n", ifname, &ipaddr); goto done_unlock_put; } new_iface = iface_alloc(ifname); - IF_DEBUG("qtaguid: iface_stat: create(): done " - "entry=%p dev=%s ip=%pI4\n", new_iface, ifname, &ipaddr); + IF_DEBUG("qtaguid: iface_stat: create(%s): done " + "entry=%p ip=%pI4\n", ifname, new_iface, &ipaddr); done_unlock_put: spin_unlock_bh(&iface_stat_list_lock); @@ -659,17 +673,16 @@ void iface_stat_create_ipv6(const struct net_device *net_dev, in_dev = in_dev_get(net_dev); if (!in_dev) { - pr_err("qtaguid: iface_stat: create6(): no inet dev for %s!\n", + pr_err("qtaguid: iface_stat: create6(%s): no inet dev\n", ifname); return; } - IF_DEBUG("qtaguid: iface_stat: create6(): in_dev=%p ifname=%p\n", - in_dev, ifname); + IF_DEBUG("qtaguid: iface_stat: create6(%s): in_dev=%p\n", + ifname, in_dev); if (!ifa) { - IF_DEBUG("qtaguid: iface_stat: create6(): " - "dev %s has no matching IP\n", + IF_DEBUG("qtaguid: iface_stat: create6(%s): no matching IP\n", ifname); goto done_put; } @@ -678,30 +691,30 @@ void iface_stat_create_ipv6(const struct net_device *net_dev, spin_lock_bh(&iface_stat_list_lock); entry = get_iface_entry(ifname); if (entry != NULL) { - IF_DEBUG("qtaguid: iface_stat: create6(): dev %s entry=%p\n", + IF_DEBUG("qtaguid: iface_stat: create6(%s): entry=%p\n", ifname, entry); if (addr_type & IPV6_ADDR_LOOPBACK) { entry->active = false; - IF_DEBUG("qtaguid: iface_stat: create6(): " - "disable tracking of loopback dev %s\n", + IF_DEBUG("qtaguid: iface_stat: create6(%s): " + "disable tracking of loopback dev\n", ifname); } else { entry->active = true; - IF_DEBUG("qtaguid: iface_stat: create6(): " - "enable tracking of dev %s with ip=%pI6c\n", + IF_DEBUG("qtaguid: iface_stat: create6(%s): " + "enable tracking. ip=%pI6c\n", ifname, &ifa->addr); } goto done_unlock_put; } else if (addr_type & IPV6_ADDR_LOOPBACK) { - IF_DEBUG("qtaguid: iface_stat: create6(): " - "ignore loopback dev %s ip=%pI6c\n", + IF_DEBUG("qtaguid: iface_stat: create6(%s): " + "ignore loopback dev. ip=%pI6c\n", ifname, &ifa->addr); goto done_unlock_put; } new_iface = iface_alloc(ifname); - IF_DEBUG("qtaguid: iface_stat: create6(): done " - "entry=%p dev=%s ip=%pI6c\n", new_iface, ifname, &ifa->addr); + IF_DEBUG("qtaguid: iface_stat: create6(%s): done " + "entry=%p ip=%pI6c\n", ifname, new_iface, &ifa->addr); done_unlock_put: spin_unlock_bh(&iface_stat_list_lock); @@ -760,22 +773,22 @@ static void iface_stat_update(struct net_device *dev) spin_lock_bh(&iface_stat_list_lock); entry = get_iface_entry(dev->name); if (entry == NULL) { - IF_DEBUG("qtaguid: iface_stat: dev %s monitor not found\n", + IF_DEBUG("qtaguid: iface_stat_update: dev=%s not tracked\n", dev->name); spin_unlock_bh(&iface_stat_list_lock); return; } + IF_DEBUG("qtaguid: iface_stat_update: dev=%s entry=%p\n", + dev->name, entry); if (entry->active) { entry->tx_bytes += stats->tx_bytes; entry->tx_packets += stats->tx_packets; entry->rx_bytes += stats->rx_bytes; entry->rx_packets += stats->rx_packets; entry->active = false; - IF_DEBUG("qtaguid: iface_stat: Updating stats for " - "dev %s which went down\n", dev->name); } else { - IF_DEBUG("qtaguid: iface_stat: Did not update stats for " - "dev %s which went down\n", dev->name); + IF_DEBUG("qtaguid: iface_stat_update: dev=%s inactive\n", + dev->name); } spin_unlock_bh(&iface_stat_list_lock); } @@ -785,7 +798,7 @@ static void tag_stat_update(struct tag_stat *tag_entry, { int active_set; active_set = get_active_counter_set(tag_entry->tn.tag); - MT_DEBUG("qtaguid: tag_stat_update(tag=0x%llx (uid=%d) set=%d " + MT_DEBUG("qtaguid: tag_stat_update(tag=0x%llx (uid=%u) set=%d " "dir=%d proto=%d bytes=%d)\n", tag_entry->tn.tag, get_uid_from_tag(tag_entry->tn.tag), active_set, direction, proto, bytes); @@ -806,11 +819,11 @@ static struct tag_stat *create_if_tag_stat(struct iface_stat *iface_entry, { struct tag_stat *new_tag_stat_entry = NULL; IF_DEBUG("qtaguid: iface_stat: create_if_tag_stat(): ife=%p tag=0x%llx" - " (uid=%d)\n", + " (uid=%u)\n", iface_entry, tag, get_uid_from_tag(tag)); new_tag_stat_entry = kzalloc(sizeof(*new_tag_stat_entry), GFP_ATOMIC); if (!new_tag_stat_entry) { - pr_err("qtaguid: iface_stat: failed to alloc new tag entry\n"); + pr_err("qtaguid: iface_stat: tag stat alloc failed\n"); goto done; } new_tag_stat_entry->tn.tag = tag; @@ -831,19 +844,20 @@ static void if_tag_stat_update(const char *ifname, uid_t uid, struct iface_stat *iface_entry; struct tag_stat *new_tag_stat; MT_DEBUG("qtaguid: if_tag_stat_update(ifname=%s " - "uid=%d sk=%p dir=%d proto=%d bytes=%d)\n", + "uid=%u sk=%p dir=%d proto=%d bytes=%d)\n", ifname, uid, sk, direction, proto, bytes); iface_entry = get_iface_entry(ifname); if (!iface_entry) { - pr_err("qtaguid: iface_stat: interface %s not found\n", ifname); + pr_err("qtaguid: iface_stat: stat_update() %s not found\n", + ifname); return; } /* It is ok to process data when an iface_entry is inactive */ - MT_DEBUG("qtaguid: iface_stat: stat_update() got entry=%p\n", - iface_entry); + MT_DEBUG("qtaguid: iface_stat: stat_update() dev=%s entry=%p\n", + ifname, iface_entry); /* * Look for a tagged sock. @@ -860,7 +874,7 @@ static void if_tag_stat_update(const char *ifname, uid_t uid, tag = combine_atag_with_uid(acct_tag, uid); } MT_DEBUG("qtaguid: iface_stat: stat_update(): " - " looking for tag=0x%llx (uid=%d) in ife=%p\n", + " looking for tag=0x%llx (uid=%u) in ife=%p\n", tag, get_uid_from_tag(tag), iface_entry); /* Loop over tag list under this interface for {acct_tag,uid_tag} */ spin_lock_bh(&iface_entry->tag_stat_list_lock); @@ -913,12 +927,6 @@ static int iface_netdev_event_handler(struct notifier_block *nb, switch (event) { case NETDEV_UP: - case NETDEV_REBOOT: - case NETDEV_CHANGE: - case NETDEV_REGISTER: /* Most likely no IP */ - case NETDEV_CHANGEADDR: /* MAC addr change */ - case NETDEV_CHANGENAME: - case NETDEV_FEAT_CHANGE: /* Might be usefull when cell type changes */ iface_stat_create(dev, NULL); break; case NETDEV_UNREGISTER: @@ -992,25 +1000,26 @@ static int __init iface_stat_init(struct proc_dir_entry *parent_procdir) iface_stat_procdir = proc_mkdir(iface_stat_procdirname, parent_procdir); if (!iface_stat_procdir) { - pr_err("qtaguid: iface_stat: failed to create proc entry\n"); + pr_err("qtaguid: iface_stat: init failed to create proc entry\n"); err = -1; goto err; } err = register_netdevice_notifier(&iface_netdev_notifier_blk); if (err) { - pr_err("qtaguid: iface_stat: failed to register dev event handler\n"); + pr_err("qtaguid: iface_stat: init " + "failed to register dev event handler\n"); goto err_zap_entry; } err = register_inetaddr_notifier(&iface_inetaddr_notifier_blk); if (err) { - pr_err("qtaguid: iface_stat: " + pr_err("qtaguid: iface_stat: init " "failed to register ipv4 dev event handler\n"); goto err_unreg_nd; } err = register_inet6addr_notifier(&iface_inet6addr_notifier_blk); if (err) { - pr_err("qtaguid: iface_stat: " + pr_err("qtaguid: iface_stat: init " "failed to register ipv6 dev event handler\n"); goto err_unreg_ip4_addr; } @@ -1116,6 +1125,9 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) uid_t sock_uid; bool res; + if (unlikely(module_passive)) + return (info->match ^ info->invert) == 0; + MT_DEBUG("qtaguid[%d]: entered skb=%p par->in=%p/out=%p fam=%d\n", par->hooknum, skb, par->in, par->out, par->family); @@ -1145,7 +1157,7 @@ static bool qtaguid_mt(const struct sk_buff *skb, struct xt_action_param *par) par->hooknum, sk, sk->sk_socket, sk->sk_socket ? sk->sk_socket->file : (void *)-1LL); filp = sk->sk_socket ? sk->sk_socket->file : NULL; - MT_DEBUG("qtaguid[%d]: filp...uid=%d\n", + MT_DEBUG("qtaguid[%d]: filp...uid=%u\n", par->hooknum, filp ? filp->f_cred->fsuid : -1); } @@ -1254,12 +1266,13 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned, node = rb_next(node)) { if (item_index++ < items_to_skip) continue; - sock_tag_entry = rb_entry(node, struct sock_tag, node); + sock_tag_entry = rb_entry(node, struct sock_tag, sock_node); uid = get_uid_from_tag(sock_tag_entry->tag); - CT_DEBUG("qtaguid: proc_read(): sk=%p tag=0x%llx (uid=%d)\n", - sock_tag_entry->sk, - sock_tag_entry->tag, - uid); + CT_DEBUG("qtaguid: proc_read(): sk=%p tag=0x%llx (uid=%u)\n", + sock_tag_entry->sk, + sock_tag_entry->tag, + uid + ); len = snprintf(outp, char_count, "sock=%p tag=0x%llx (uid=%u)\n", sock_tag_entry->sk, sock_tag_entry->tag, uid); @@ -1315,10 +1328,9 @@ static int ctrl_cmd_delete(const char *input) struct tag_stat *ts_entry; struct tag_counter_set *tcs_entry; - CT_DEBUG("qtaguid: ctrl_delete(%s): entered\n", input); argc = sscanf(input, "%c %llu %u", &cmd, &acct_tag, &uid); CT_DEBUG("qtaguid: ctrl_delete(%s): argc=%d cmd=%c " - "acct_tag=0x%llx uid=%u\n", input, argc, cmd, + "user_tag=0x%llx uid=%u\n", input, argc, cmd, acct_tag, uid); if (argc < 2) { res = -EINVAL; @@ -1332,8 +1344,9 @@ static int ctrl_cmd_delete(const char *input) if (argc < 3) { uid = current_fsuid(); } else if (!can_impersonate_uid(uid)) { - pr_info("qtaguid: ctrl_delete(%s): insuficient priv\n", - input); + pr_info("qtaguid: ctrl_delete(%s): " + "insufficient priv from pid=%u uid=%u\n", + input, current->pid, current_fsuid()); res = -EPERM; goto err; } @@ -1342,7 +1355,7 @@ static int ctrl_cmd_delete(const char *input) spin_lock_bh(&sock_tag_list_lock); node = rb_first(&sock_tag_tree); while (node) { - st_entry = rb_entry(node, struct sock_tag, node); + st_entry = rb_entry(node, struct sock_tag, sock_node); entry_uid = get_uid_from_tag(st_entry->tag); node = rb_next(node); if (entry_uid != uid) @@ -1350,11 +1363,12 @@ static int ctrl_cmd_delete(const char *input) if (!acct_tag || st_entry->tag == tag) { CT_DEBUG("qtaguid: ctrl_delete(): " - "erase st: sk=%p tag=0x%llx (uid=%d)\n", + "erase st: sk=%p tag=0x%llx (uid=%u)\n", st_entry->sk, st_entry->tag, entry_uid); - rb_erase(&st_entry->node, &sock_tag_tree); + rb_erase(&st_entry->sock_node, &sock_tag_tree); + sockfd_put(st_entry->socket); kfree(st_entry); } } @@ -1367,7 +1381,7 @@ static int ctrl_cmd_delete(const char *input) tcs_entry = tag_counter_set_tree_search(&tag_counter_set_tree, tag); if (tcs_entry) { CT_DEBUG("qtaguid: ctrl_delete(): " - "erase tcs: tag=0x%llx (uid=%d) set=%d\n", + "erase tcs: tag=0x%llx (uid=%u) set=%d\n", tcs_entry->tn.tag, get_uid_from_tag(tcs_entry->tn.tag), tcs_entry->active_set); @@ -1408,7 +1422,6 @@ static int ctrl_cmd_delete(const char *input) res = 0; err: - CT_DEBUG("qtaguid: ctrl_delete(%s) res=%d\n", input, res); return res; } @@ -1421,7 +1434,6 @@ static int ctrl_cmd_counter_set(const char *input) struct tag_counter_set *tcs; int counter_set; - CT_DEBUG("qtaguid: ctrl_counterset(%s): entered\n", input); argc = sscanf(input, "%c %d %u", &cmd, &counter_set, &uid); CT_DEBUG("qtaguid: ctrl_counterset(%s): argc=%d cmd=%c " "set=%d uid=%u\n", input, argc, cmd, @@ -1437,8 +1449,9 @@ static int ctrl_cmd_counter_set(const char *input) goto err; } if (!can_manipulate_uids()) { - pr_info("qtaguid: ctrl_counterset(%s): insufficient priv\n", - input); + pr_info("qtaguid: ctrl_counterset(%s): " + "insufficient priv from pid=%u uid=%u\n", + input, current->pid, current_fsuid()); res = -EPERM; goto err; } @@ -1459,7 +1472,7 @@ static int ctrl_cmd_counter_set(const char *input) tcs->tn.tag = tag; tag_counter_set_tree_insert(tcs, &tag_counter_set_tree); CT_DEBUG("qtaguid: ctrl_counterset(%s): added tcs tag=0x%llx " - "(uid=%d) set=%d\n", + "(uid=%u) set=%d\n", input, tag, get_uid_from_tag(tag), counter_set); } tcs->active_set = counter_set; @@ -1468,7 +1481,6 @@ static int ctrl_cmd_counter_set(const char *input) res = 0; err: - CT_DEBUG("qtaguid: ctrl_counterset(%s) res=%d\n", input, res); return res; } @@ -1479,6 +1491,7 @@ static int ctrl_cmd_tag(const char *input) uid_t uid = 0; tag_t acct_tag = 0; struct socket *el_socket; + int refcnt = -1; int res, argc; struct sock_tag *sock_tag_entry; @@ -1491,55 +1504,80 @@ static int ctrl_cmd_tag(const char *input) res = -EINVAL; goto err; } - el_socket = sockfd_lookup(sock_fd, &res); + el_socket = sockfd_lookup(sock_fd, &res); /* This locks the file */ if (!el_socket) { pr_info("qtaguid: ctrl_tag(%s): failed to lookup" " sock_fd=%d err=%d\n", input, sock_fd, res); goto err; } + refcnt = atomic_read(&el_socket->file->f_count); + CT_DEBUG("qtaguid: ctrl_tag(%s): socket->...->f_count=%d\n", + input, refcnt); if (argc < 3) { acct_tag = 0; } else if (!valid_atag(acct_tag)) { pr_info("qtaguid: ctrl_tag(%s): invalid tag\n", input); res = -EINVAL; - goto err; - } + goto err_put; + } + CT_DEBUG("qtaguid: ctrl_tag(%s): " + "uid=%u euid=%u fsuid=%u " + "in_group=%d in_egroup=%d\n", + input, current_uid(), current_euid(), current_fsuid(), + in_group_p(proc_stats_readall_gid), + in_egroup_p(proc_stats_readall_gid)); if (argc < 4) { uid = current_fsuid(); } else if (!can_impersonate_uid(uid)) { - pr_info("qtaguid: ctrl_tag(%s): insuficient priv\n", - input); + pr_info("qtaguid: ctrl_tag(%s): " + "insufficient priv from pid=%u uid=%u\n", + input, current->pid, current_fsuid()); res = -EPERM; - goto err; + goto err_put; } spin_lock_bh(&sock_tag_list_lock); sock_tag_entry = get_sock_stat_nl(el_socket->sk); if (sock_tag_entry) { + /* + * This is a re-tagging, so release the sock_fd that was + * locked at the time of the 1st tagging. + */ + sockfd_put(sock_tag_entry->socket); + refcnt--; sock_tag_entry->tag = combine_atag_with_uid(acct_tag, uid); } else { sock_tag_entry = kzalloc(sizeof(*sock_tag_entry), GFP_ATOMIC); if (!sock_tag_entry) { + pr_err("qtaguid: ctrl_tag(%s): " + "socket tag alloc failed\n", + input); + spin_unlock_bh(&sock_tag_list_lock); res = -ENOMEM; - goto err; + goto err_put; } sock_tag_entry->sk = el_socket->sk; + sock_tag_entry->socket = el_socket; sock_tag_entry->tag = combine_atag_with_uid(acct_tag, uid); sock_tag_tree_insert(sock_tag_entry, &sock_tag_tree); } spin_unlock_bh(&sock_tag_list_lock); + /* We keep the ref to the socket (file) until it is untagged */ + CT_DEBUG("qtaguid: ctrl_tag(%s): done. socket->...->f_count=%d\n", + input, + el_socket ? atomic_read(&el_socket->file->f_count) : -1); + return 0; - CT_DEBUG("qtaguid: tag: sock_tag_entry->sk=%p " - "...->tag=0x%llx (uid=%u)\n", - sock_tag_entry->sk, sock_tag_entry->tag, - get_uid_from_tag(sock_tag_entry->tag)); - res = 0; - +err_put: + /* Release the sock_fd that was grabbed by sockfd_lookup(). */ + sockfd_put(el_socket); + refcnt--; err: - CT_DEBUG("qtaguid: ctrl_tag(%s) res=%d\n", input, res); + CT_DEBUG("qtaguid: ctrl_tag(%s): done. socket->...->f_count=%d\n", + input, refcnt); return res; } @@ -1548,10 +1586,10 @@ static int ctrl_cmd_untag(const char *input) char cmd; int sock_fd = 0; struct socket *el_socket; + int refcnt = -1; int res, argc; struct sock_tag *sock_tag_entry; - CT_DEBUG("qtaguid: ctrl_untag(%s): entered\n", input); argc = sscanf(input, "%c %d", &cmd, &sock_fd); CT_DEBUG("qtaguid: ctrl_untag(%s): argc=%d cmd=%c sock_fd=%d\n", input, argc, cmd, sock_fd); @@ -1559,28 +1597,49 @@ static int ctrl_cmd_untag(const char *input) res = -EINVAL; goto err; } - el_socket = sockfd_lookup(sock_fd, &res); + el_socket = sockfd_lookup(sock_fd, &res); /* This locks the file */ if (!el_socket) { pr_info("qtaguid: ctrl_untag(%s): failed to lookup" " sock_fd=%d err=%d\n", input, sock_fd, res); goto err; } + refcnt = atomic_read(&el_socket->file->f_count); + CT_DEBUG("qtaguid: ctrl_untag(%s): socket->...->f_count=%d\n", + input, refcnt); spin_lock_bh(&sock_tag_list_lock); sock_tag_entry = get_sock_stat_nl(el_socket->sk); if (!sock_tag_entry) { spin_unlock_bh(&sock_tag_list_lock); res = -EINVAL; - goto err; + goto err_put; } - /* The socket already belongs to the current process - * so it can do whatever it wants to it. */ - rb_erase(&sock_tag_entry->node, &sock_tag_tree); + /* + * The socket already belongs to the current process + * so it can do whatever it wants to it. + */ + rb_erase(&sock_tag_entry->sock_node, &sock_tag_tree); + + /* + * Release the sock_fd that was grabbed at tag time, + * and once more for the sockfd_lookup() here. + */ + sockfd_put(sock_tag_entry->socket); spin_unlock_bh(&sock_tag_list_lock); + sockfd_put(el_socket); + refcnt -= 2; kfree(sock_tag_entry); + CT_DEBUG("qtaguid: ctrl_untag(%s): done. socket->...->f_count=%d\n", + input, refcnt); - res = 0; + return 0; + +err_put: + /* Release the sock_fd that was grabbed by sockfd_lookup(). */ + sockfd_put(el_socket); + refcnt--; err: - CT_DEBUG("qtaguid: ctrl_untag(%s): res=%d\n", input, res); + CT_DEBUG("qtaguid: ctrl_untag(%s): done. socket->...->f_count=%d\n", + input, refcnt); return res; } @@ -1589,7 +1648,6 @@ static int qtaguid_ctrl_parse(const char *input, int count) char cmd; int res; - CT_DEBUG("qtaguid: ctrl(%s): entered\n", input); cmd = input[0]; /* Collect params for commands */ switch (cmd) { @@ -1667,10 +1725,12 @@ static int pp_stats_line(struct proc_print_info *ppi, int cnt_set) tag_t tag = ppi->ts_entry->tn.tag; uid_t stat_uid = get_uid_from_tag(tag); if (!can_read_other_uid_stats(stat_uid)) { - CT_DEBUG("qtaguid: insufficient priv for stat line:" - "%s 0x%llx %u\n", + CT_DEBUG("qtaguid: stats line: " + "%s 0x%llx %u: " + "insufficient priv from pid=%u uid=%u\n", ppi->iface_entry->ifname, - get_atag_from_tag(tag), stat_uid); + get_atag_from_tag(tag), stat_uid, + current->pid, current_fsuid()); return 0; } cnts = &ppi->ts_entry->counters; @@ -1748,8 +1808,11 @@ static int qtaguid_stats_proc_read(char *page, char **num_items_returned, ppi.num_items_returned = num_items_returned; if (unlikely(module_passive)) { + len = pp_stats_line(&ppi, 0); + /* The header should always be shorter than the buffer. */ + WARN_ON(len >= ppi.char_count); *eof = 1; - return 0; + return len; } CT_DEBUG("qtaguid:proc stats page=%p *num_items_returned=%p off=%ld " -- cgit v1.2.3