summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
authorJP Abgrall <jpa@google.com>2011-08-17 16:43:00 -0700
committerDan Willemsen <dwillemsen@nvidia.com>2011-11-30 21:39:00 -0800
commit1c80cbb69f05a3237fa4778ae4b3462467f647e7 (patch)
tree13a2afd1d0180686071daeaac5db2e3a6082251f /net
parente7a39cea4531be8320a7553b0359b3f16dead219 (diff)
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 <jpa@google.com>
Diffstat (limited to 'net')
-rw-r--r--net/netfilter/xt_qtaguid.c309
1 files changed, 186 insertions, 123 deletions
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 "