mirror of
https://github.com/LineageOS/android_kernel_samsung_msm8226.git
synced 2026-09-28 00:00:22 +02:00
Backport: ANDROID: Add untag hacks to inet_release function
To prevent protential risk of memory leak caused by closing socket with out untag it from qtaguid module, the qtaguid module now do not hold any socket file reference count. Instead, it will increase the sk_refcnt of the sk struct to prevent a reuse of the socket pointer. And when a socket is released. It will delete the tag if the socket is previously tagged so no more resources is held by xt_qtaguid moudle. A flag is added to the untag process to prevent possible kernel crash caused by fail to delete corresponding socket_tag_entry list. Issue: FP2P-307 Test: run cts -m CtsNativeNetTestCases Change-Id: I163bb06bdbf6aa63503f1f14ec1dd177a72b9df3 (cherry picked from commit b5428181f94bd0d1ff5d321bd58413bdb61f8df4)
This commit is contained in:
committed by
Francescodario Cuzzocrea
parent
155f6cd98d
commit
d6fedc0d07
@@ -10,4 +10,5 @@
|
||||
#define XT_QTAGUID_SOCKET XT_OWNER_SOCKET
|
||||
#define xt_qtaguid_match_info xt_owner_match_info
|
||||
|
||||
int qtaguid_untag(struct socket *sock, bool kernel);
|
||||
#endif /* _XT_QTAGUID_MATCH_H */
|
||||
|
||||
+4
-1
@@ -89,6 +89,7 @@
|
||||
#include <linux/netfilter_ipv4.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/netfilter/xt_qtaguid.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
@@ -450,7 +451,9 @@ int inet_release(struct socket *sock)
|
||||
|
||||
if (sk) {
|
||||
long timeout;
|
||||
|
||||
#ifdef CONFIG_NETFILTER_XT_MATCH_QTAGUID
|
||||
qtaguid_untag(sock, true);
|
||||
#endif
|
||||
sock_rps_reset_flow(sk);
|
||||
|
||||
/* Applications forget to leave groups before exiting */
|
||||
|
||||
+60
-56
@@ -416,7 +416,7 @@ static void sock_tag_tree_erase(struct rb_root *st_to_free_tree)
|
||||
st_entry->tag,
|
||||
get_uid_from_tag(st_entry->tag));
|
||||
rb_erase(&st_entry->sock_node, st_to_free_tree);
|
||||
sockfd_put(st_entry->socket);
|
||||
sock_put(st_entry->sk);
|
||||
kfree(st_entry);
|
||||
}
|
||||
}
|
||||
@@ -1942,7 +1942,6 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned,
|
||||
struct sock_tag *sock_tag_entry;
|
||||
int item_index = 0;
|
||||
int indent_level = 0;
|
||||
long f_count;
|
||||
|
||||
if (unlikely(module_passive)) {
|
||||
*eof = 1;
|
||||
@@ -1961,6 +1960,7 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned,
|
||||
for (node = rb_first(&sock_tag_tree);
|
||||
node;
|
||||
node = rb_next(node)) {
|
||||
int sk_ref_count;
|
||||
if (item_index++ < items_to_skip)
|
||||
continue;
|
||||
sock_tag_entry = rb_entry(node, struct sock_tag, sock_node);
|
||||
@@ -1972,14 +1972,14 @@ static int qtaguid_ctrl_proc_read(char *page, char **num_items_returned,
|
||||
uid,
|
||||
sock_tag_entry->pid
|
||||
);
|
||||
f_count = atomic_long_read(
|
||||
&sock_tag_entry->socket->file->f_count);
|
||||
sk_ref_count = atomic_read(
|
||||
&sock_tag_entry->sk->sk_refcnt);
|
||||
len = snprintf(outp, char_count,
|
||||
"sock=%pK tag=0x%llx (uid=%u) pid=%u "
|
||||
"f_count=%lu\n",
|
||||
"f_count=%d\n",
|
||||
sock_tag_entry->sk,
|
||||
sock_tag_entry->tag, uid,
|
||||
sock_tag_entry->pid, f_count);
|
||||
sock_tag_entry->pid, sk_ref_count);
|
||||
if (len >= char_count) {
|
||||
spin_unlock_bh(&sock_tag_list_lock);
|
||||
*outp = '\0';
|
||||
@@ -2292,8 +2292,8 @@ static int ctrl_cmd_tag(const char *input)
|
||||
current_fsuid());
|
||||
goto err;
|
||||
}
|
||||
CT_DEBUG("qtaguid: ctrl_tag(%s): socket->...->f_count=%ld ->sk=%p\n",
|
||||
input, atomic_long_read(&el_socket->file->f_count),
|
||||
CT_DEBUG("qtaguid: ctrl_tag(%s): socket->...->sk_refcnt=%d ->sk=%p\n",
|
||||
input, atomic_read(&el_socket->sk->sk_refcnt),
|
||||
el_socket->sk);
|
||||
if (argc < 3) {
|
||||
acct_tag = make_atag_from_value(0);
|
||||
@@ -2336,18 +2336,12 @@ static int ctrl_cmd_tag(const char *input)
|
||||
struct tag_ref *prev_tag_ref_entry;
|
||||
|
||||
CT_DEBUG("qtaguid: ctrl_tag(%s): retag for sk=%p "
|
||||
"st@%p ...->f_count=%ld\n",
|
||||
input, el_socket->sk, sock_tag_entry,
|
||||
atomic_long_read(&el_socket->file->f_count));
|
||||
/*
|
||||
* This is a re-tagging, so release the sock_fd that was
|
||||
* locked at the time of the 1st tagging.
|
||||
* There is still the ref from this call's sockfd_lookup() so
|
||||
* it can be done within the spinlock.
|
||||
*/
|
||||
sockfd_put(sock_tag_entry->socket);
|
||||
"st@%p ...->sk_refcnt=%d\n",
|
||||
input, el_socket->sk, sock_tag_entry,
|
||||
atomic_read(&el_socket->sk->sk_refcnt));
|
||||
|
||||
prev_tag_ref_entry = lookup_tag_ref(sock_tag_entry->tag,
|
||||
&uid_tag_data_entry);
|
||||
&uid_tag_data_entry);
|
||||
BUG_ON(IS_ERR_OR_NULL(prev_tag_ref_entry));
|
||||
BUG_ON(prev_tag_ref_entry->num_sock_tags <= 0);
|
||||
prev_tag_ref_entry->num_sock_tags--;
|
||||
@@ -2370,8 +2364,12 @@ static int ctrl_cmd_tag(const char *input)
|
||||
res = -ENOMEM;
|
||||
goto err_put;
|
||||
}
|
||||
/*
|
||||
* Hold the sk refcount here to make sure the sk pointer cannot
|
||||
* be freed and reused
|
||||
*/
|
||||
sock_hold(el_socket->sk);
|
||||
sock_tag_entry->sk = el_socket->sk;
|
||||
sock_tag_entry->socket = el_socket;
|
||||
sock_tag_entry->pid = current->tgid;
|
||||
sock_tag_entry->tag = combine_atag_with_uid(acct_tag,
|
||||
uid);
|
||||
@@ -2398,15 +2396,16 @@ static int ctrl_cmd_tag(const char *input)
|
||||
}
|
||||
spin_unlock_bh(&uid_tag_data_tree_lock);
|
||||
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 st@%p ...->f_count=%ld\n",
|
||||
input, sock_tag_entry,
|
||||
atomic_long_read(&el_socket->file->f_count));
|
||||
/* We keep the ref to the sk until it is untagged */
|
||||
CT_DEBUG("qtaguid: ctrl_tag(%s): done st@%p ...->sk_refcnt=%d\n",
|
||||
input, sock_tag_entry,
|
||||
atomic_read(&el_socket->sk->sk_refcnt));
|
||||
sockfd_put(el_socket);
|
||||
return 0;
|
||||
|
||||
err_put:
|
||||
CT_DEBUG("qtaguid: ctrl_tag(%s): done. ...->f_count=%ld\n",
|
||||
input, atomic_long_read(&el_socket->file->f_count) - 1);
|
||||
CT_DEBUG("qtaguid: ctrl_tag(%s): done. ...->sk_refcnt=%d\n",
|
||||
input, atomic_read(&el_socket->sk->sk_refcnt) - 1);
|
||||
/* Release the sock_fd that was grabbed by sockfd_lookup(). */
|
||||
sockfd_put(el_socket);
|
||||
return res;
|
||||
@@ -2422,17 +2421,13 @@ static int ctrl_cmd_untag(const char *input)
|
||||
int sock_fd = 0;
|
||||
struct socket *el_socket;
|
||||
int res, argc;
|
||||
struct sock_tag *sock_tag_entry;
|
||||
struct tag_ref *tag_ref_entry;
|
||||
struct uid_tag_data *utd_entry;
|
||||
struct proc_qtu_data *pqd_entry;
|
||||
|
||||
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);
|
||||
if (argc < 2) {
|
||||
res = -EINVAL;
|
||||
goto err;
|
||||
return res;
|
||||
}
|
||||
el_socket = sockfd_lookup(sock_fd, &res); /* This locks the file */
|
||||
if (!el_socket) {
|
||||
@@ -2440,17 +2435,33 @@ static int ctrl_cmd_untag(const char *input)
|
||||
" sock_fd=%d err=%d pid=%u tgid=%u uid=%u\n",
|
||||
input, sock_fd, res, current->pid, current->tgid,
|
||||
current_fsuid());
|
||||
goto err;
|
||||
return res;
|
||||
}
|
||||
CT_DEBUG("qtaguid: ctrl_untag(%s): socket->...->f_count=%ld ->sk=%p\n",
|
||||
input, atomic_long_read(&el_socket->file->f_count),
|
||||
el_socket->sk);
|
||||
|
||||
res = qtaguid_untag(el_socket, false);
|
||||
sockfd_put(el_socket);
|
||||
return res;
|
||||
}
|
||||
|
||||
int qtaguid_untag(struct socket *el_socket, bool kernel)
|
||||
{
|
||||
int res;
|
||||
pid_t pid;
|
||||
struct sock_tag *sock_tag_entry;
|
||||
struct tag_ref *tag_ref_entry;
|
||||
struct uid_tag_data *utd_entry;
|
||||
struct proc_qtu_data *pqd_entry;
|
||||
|
||||
|
||||
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_put;
|
||||
return res;
|
||||
}
|
||||
/*
|
||||
* The socket already belongs to the current process
|
||||
@@ -2462,20 +2473,26 @@ static int ctrl_cmd_untag(const char *input)
|
||||
BUG_ON(!tag_ref_entry);
|
||||
BUG_ON(tag_ref_entry->num_sock_tags <= 0);
|
||||
spin_lock_bh(&uid_tag_data_tree_lock);
|
||||
if (kernel)
|
||||
pid = sock_tag_entry->pid;
|
||||
else
|
||||
pid = current->tgid;
|
||||
pqd_entry = proc_qtu_data_tree_search(
|
||||
&proc_qtu_data_tree, current->tgid);
|
||||
&proc_qtu_data_tree, pid);
|
||||
/*
|
||||
* TODO: remove if, and start failing.
|
||||
* At first, we want to catch user-space code that is not
|
||||
* opening the /dev/xt_qtaguid.
|
||||
*/
|
||||
if (IS_ERR_OR_NULL(pqd_entry))
|
||||
if (IS_ERR_OR_NULL(pqd_entry) || !sock_tag_entry->list.next) {
|
||||
pr_warn_once("qtaguid: %s(): "
|
||||
"User space forgot to open /dev/xt_qtaguid? "
|
||||
"pid=%u tgid=%u uid=%u\n", __func__,
|
||||
current->pid, current->tgid, current_fsuid());
|
||||
else
|
||||
"User space forgot to open /dev/xt_qtaguid? "
|
||||
"pid=%u tgid=%u sk_pid=%u, uid=%u\n", __func__,
|
||||
current->pid, current->tgid, sock_tag_entry->pid,
|
||||
from_kuid(&init_user_ns, current_fsuid()));
|
||||
} else {
|
||||
list_del(&sock_tag_entry->list);
|
||||
}
|
||||
spin_unlock_bh(&uid_tag_data_tree_lock);
|
||||
/*
|
||||
* We don't free tag_ref from the utd_entry here,
|
||||
@@ -2485,29 +2502,16 @@ static int ctrl_cmd_untag(const char *input)
|
||||
spin_unlock_bh(&sock_tag_list_lock);
|
||||
/*
|
||||
* Release the sock_fd that was grabbed at tag time,
|
||||
* and once more for the sockfd_lookup() here.
|
||||
*/
|
||||
sockfd_put(sock_tag_entry->socket);
|
||||
CT_DEBUG("qtaguid: ctrl_untag(%s): done. st@%p ...->f_count=%ld\n",
|
||||
input, sock_tag_entry,
|
||||
atomic_long_read(&el_socket->file->f_count) - 1);
|
||||
sockfd_put(el_socket);
|
||||
sock_put(sock_tag_entry->sk);
|
||||
CT_DEBUG("qtaguid: done. st@%p ...->sk_refcnt=%d\n",
|
||||
sock_tag_entry,
|
||||
atomic_read(&el_socket->sk->sk_refcnt));
|
||||
|
||||
kfree(sock_tag_entry);
|
||||
atomic64_inc(&qtu_events.sockets_untagged);
|
||||
|
||||
return 0;
|
||||
|
||||
err_put:
|
||||
CT_DEBUG("qtaguid: ctrl_untag(%s): done. socket->...->f_count=%ld\n",
|
||||
input, atomic_long_read(&el_socket->file->f_count) - 1);
|
||||
/* Release the sock_fd that was grabbed by sockfd_lookup(). */
|
||||
sockfd_put(el_socket);
|
||||
return res;
|
||||
|
||||
err:
|
||||
CT_DEBUG("qtaguid: ctrl_untag(%s): done.\n", input);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int qtaguid_ctrl_parse(const char *input, int count)
|
||||
|
||||
@@ -256,8 +256,6 @@ struct iface_stat_work {
|
||||
struct sock_tag {
|
||||
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;
|
||||
/* Used to associate with a given pid */
|
||||
struct list_head list; /* in proc_qtu_data.sock_tag_list */
|
||||
pid_t pid;
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock_types.h>
|
||||
|
||||
#include <net/sock.h>
|
||||
|
||||
#include "xt_qtaguid_internal.h"
|
||||
#include "xt_qtaguid_print.h"
|
||||
@@ -237,10 +238,10 @@ char *pp_sock_tag(struct sock_tag *st)
|
||||
tag_str = pp_tag_t(&st->tag);
|
||||
res = kasprintf(GFP_ATOMIC, "sock_tag@%p{"
|
||||
"sock_node=rb_node{...}, "
|
||||
"sk=%p socket=%p (f_count=%lu), list=list_head{...}, "
|
||||
"sk=%p (f_count=%d), list=list_head{...}, "
|
||||
"pid=%u, tag=%s}",
|
||||
st, st->sk, st->socket, atomic_long_read(
|
||||
&st->socket->file->f_count),
|
||||
st, st->sk, atomic_read(
|
||||
&st->sk->sk_refcnt),
|
||||
st->pid, tag_str);
|
||||
_bug_on_err_or_null(res);
|
||||
kfree(tag_str);
|
||||
|
||||
Reference in New Issue
Block a user