android_kernel_oneplus_msm8998/net/ipc_router/ipc_router_socket.c
Arun Kumar Neelakantam 596aa5c28a net: ipc_router: Initialize the sockaddr in recvmsg() handler
sockaddr structure is filled with required information only which
results in few memory locations of structure with uninitialized data.

Memset complete structure before using it to remove uninitialized data.

CRs-Fixed: 2274853
Change-Id: I181710bde100fb1553b925d9fdf227af35ff38b5
Signed-off-by: Arun Kumar Neelakantam <aneela@codeaurora.org>
2018-07-25 22:57:06 -07:00

693 lines
17 KiB
C

/* Copyright (c) 2011-2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/net.h>
#include <linux/socket.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/poll.h>
#include <linux/fcntl.h>
#include <linux/gfp.h>
#include <linux/msm_ipc.h>
#include <linux/sched.h>
#include <linux/thread_info.h>
#include <linux/slab.h>
#include <linux/kmemleak.h>
#include <linux/ipc_logging.h>
#include <linux/string.h>
#include <linux/atomic.h>
#include <linux/ipc_router.h>
#include <net/sock.h>
#include "ipc_router_private.h"
#include "ipc_router_security.h"
#define msm_ipc_sk(sk) ((struct msm_ipc_sock *)(sk))
#define msm_ipc_sk_port(sk) ((struct msm_ipc_port *)(msm_ipc_sk(sk)->port))
#ifndef SIZE_MAX
#define SIZE_MAX ((size_t)-1)
#endif
static int sockets_enabled;
static struct proto msm_ipc_proto;
static const struct proto_ops msm_ipc_proto_ops;
static RAW_NOTIFIER_HEAD(ipcrtr_af_init_chain);
static DEFINE_MUTEX(ipcrtr_af_init_lock);
static struct sk_buff_head *msm_ipc_router_build_msg(struct msghdr *m,
size_t total_len)
{
struct sk_buff_head *msg_head;
struct sk_buff *msg;
int first = 1;
int last = 1;
size_t data_size = 0;
size_t alloc_size, align_size;
void *data;
size_t total_copied_size = 0, copied_size;
if (iov_iter_count(&m->msg_iter) == total_len)
data_size = total_len;
if (!data_size)
return NULL;
align_size = ALIGN_SIZE(data_size);
msg_head = kmalloc(sizeof(struct sk_buff_head), GFP_KERNEL);
if (!msg_head) {
IPC_RTR_ERR("%s: cannot allocate skb_head\n", __func__);
return NULL;
}
skb_queue_head_init(msg_head);
while (total_copied_size < total_len) {
alloc_size = data_size;
if (first)
alloc_size += IPC_ROUTER_HDR_SIZE;
if (last)
alloc_size += align_size;
msg = alloc_skb(alloc_size, GFP_KERNEL);
if (!msg) {
if (alloc_size <= (PAGE_SIZE/2)) {
IPC_RTR_ERR("%s: cannot allocated skb\n",
__func__);
goto msg_build_failure;
}
data_size = data_size / 2;
last = 0;
continue;
}
if (first) {
skb_reserve(msg, IPC_ROUTER_HDR_SIZE);
first = 0;
}
data = skb_put(msg, data_size);
copied_size = copy_from_iter(msg->data, data_size, &m->msg_iter);
if (copied_size != data_size) {
IPC_RTR_ERR("%s: copy_from_iter failed %zu %zu %zu\n", __func__, alloc_size, data_size, copied_size);
kfree_skb(msg);
goto msg_build_failure;
}
skb_queue_tail(msg_head, msg);
total_copied_size += data_size;
data_size = total_len - total_copied_size;
last = 1;
}
return msg_head;
msg_build_failure:
while (!skb_queue_empty(msg_head)) {
msg = skb_dequeue(msg_head);
kfree_skb(msg);
}
kfree(msg_head);
return NULL;
}
static int msm_ipc_router_extract_msg(struct msghdr *m,
struct rr_packet *pkt)
{
struct sockaddr_msm_ipc *addr;
struct rr_header_v1 *hdr;
struct sk_buff *temp;
union rr_control_msg *ctl_msg;
int offset = 0, data_len = 0, copy_len, copied_len;
if (!m || !pkt) {
IPC_RTR_ERR("%s: Invalid pointers passed\n", __func__);
return -EINVAL;
}
addr = (struct sockaddr_msm_ipc *)m->msg_name;
hdr = &(pkt->hdr);
if (addr && (hdr->type == IPC_ROUTER_CTRL_CMD_RESUME_TX)) {
temp = skb_peek(pkt->pkt_fragment_q);
if (!temp || !temp->data) {
IPC_RTR_ERR("%s: Invalid skb\n", __func__);
return -EINVAL;
}
ctl_msg = (union rr_control_msg *)(temp->data);
memset(addr, 0x0, sizeof(*addr));
addr->family = AF_MSM_IPC;
addr->address.addrtype = MSM_IPC_ADDR_ID;
addr->address.addr.port_addr.node_id = ctl_msg->cli.node_id;
addr->address.addr.port_addr.port_id = ctl_msg->cli.port_id;
m->msg_namelen = sizeof(struct sockaddr_msm_ipc);
return offset;
}
if (addr && (hdr->type == IPC_ROUTER_CTRL_CMD_DATA)) {
memset(addr, 0x0, sizeof(*addr));
addr->family = AF_MSM_IPC;
addr->address.addrtype = MSM_IPC_ADDR_ID;
addr->address.addr.port_addr.node_id = hdr->src_node_id;
addr->address.addr.port_addr.port_id = hdr->src_port_id;
m->msg_namelen = sizeof(struct sockaddr_msm_ipc);
}
data_len = hdr->size;
skb_queue_walk(pkt->pkt_fragment_q, temp) {
copy_len = data_len < temp->len ? data_len : temp->len;
copied_len = copy_to_iter(temp->data, copy_len, &m->msg_iter);
if (copy_len != copied_len) {
IPC_RTR_ERR("%s: Copy to user failed\n", __func__);
return -EFAULT;
}
offset += copy_len;
data_len -= copy_len;
}
return offset;
}
static int msm_ipc_router_create(struct net *net,
struct socket *sock,
int protocol,
int kern)
{
struct sock *sk;
struct msm_ipc_port *port_ptr;
if (unlikely(protocol != 0)) {
IPC_RTR_ERR("%s: Protocol not supported\n", __func__);
return -EPROTONOSUPPORT;
}
switch (sock->type) {
case SOCK_DGRAM:
break;
default:
IPC_RTR_ERR("%s: Protocol type not supported\n", __func__);
return -EPROTOTYPE;
}
sk = sk_alloc(net, AF_MSM_IPC, GFP_KERNEL, &msm_ipc_proto, kern);
if (!sk) {
IPC_RTR_ERR("%s: sk_alloc failed\n", __func__);
return -ENOMEM;
}
sock->ops = &msm_ipc_proto_ops;
sock_init_data(sock, sk);
sk->sk_data_ready = NULL;
sk->sk_write_space = ipc_router_dummy_write_space;
sk->sk_rcvtimeo = DEFAULT_RCV_TIMEO;
sk->sk_sndtimeo = DEFAULT_SND_TIMEO;
port_ptr = msm_ipc_router_create_raw_port(sk, NULL, NULL);
if (!port_ptr) {
IPC_RTR_ERR("%s: port_ptr alloc failed\n", __func__);
sock_put(sk);
sock->sk = NULL;
return -ENOMEM;
}
port_ptr->check_send_permissions = msm_ipc_check_send_permissions;
msm_ipc_sk(sk)->port = port_ptr;
msm_ipc_sk(sk)->default_node_vote_info = NULL;
return 0;
}
int msm_ipc_router_bind(struct socket *sock, struct sockaddr *uaddr,
int uaddr_len)
{
struct sockaddr_msm_ipc *addr = (struct sockaddr_msm_ipc *)uaddr;
struct sock *sk = sock->sk;
struct msm_ipc_port *port_ptr;
int ret;
if (!sk)
return -EINVAL;
if (!check_permissions()) {
IPC_RTR_ERR("%s: %s Do not have permissions\n",
__func__, current->comm);
return -EPERM;
}
if (!uaddr_len) {
IPC_RTR_ERR("%s: Invalid address length\n", __func__);
return -EINVAL;
}
if (addr->family != AF_MSM_IPC) {
IPC_RTR_ERR("%s: Address family is incorrect\n", __func__);
return -EAFNOSUPPORT;
}
if (addr->address.addrtype != MSM_IPC_ADDR_NAME) {
IPC_RTR_ERR("%s: Address type is incorrect\n", __func__);
return -EINVAL;
}
port_ptr = msm_ipc_sk_port(sk);
if (!port_ptr)
return -ENODEV;
if (!msm_ipc_sk(sk)->default_node_vote_info)
msm_ipc_sk(sk)->default_node_vote_info =
msm_ipc_load_default_node();
lock_sock(sk);
ret = msm_ipc_router_register_server(port_ptr, &addr->address);
release_sock(sk);
return ret;
}
static int ipc_router_connect(struct socket *sock, struct sockaddr *uaddr,
int uaddr_len, int flags)
{
struct sockaddr_msm_ipc *addr = (struct sockaddr_msm_ipc *)uaddr;
struct sock *sk = sock->sk;
struct msm_ipc_port *port_ptr;
int ret;
if (!sk)
return -EINVAL;
if (uaddr_len <= 0) {
IPC_RTR_ERR("%s: Invalid address length\n", __func__);
return -EINVAL;
}
if (!addr) {
IPC_RTR_ERR("%s: Invalid address\n", __func__);
return -EINVAL;
}
if (addr->family != AF_MSM_IPC) {
IPC_RTR_ERR("%s: Address family is incorrect\n", __func__);
return -EAFNOSUPPORT;
}
port_ptr = msm_ipc_sk_port(sk);
if (!port_ptr)
return -ENODEV;
lock_sock(sk);
ret = ipc_router_set_conn(port_ptr, &addr->address);
release_sock(sk);
return ret;
}
static int msm_ipc_router_sendmsg(struct socket *sock,
struct msghdr *m, size_t total_len)
{
struct sock *sk = sock->sk;
struct msm_ipc_port *port_ptr = msm_ipc_sk_port(sk);
struct sockaddr_msm_ipc *dest = (struct sockaddr_msm_ipc *)m->msg_name;
struct sk_buff_head *msg;
int ret;
struct msm_ipc_addr dest_addr = {0};
long timeout;
if (dest) {
if (m->msg_namelen < sizeof(*dest) ||
dest->family != AF_MSM_IPC)
return -EINVAL;
memcpy(&dest_addr, &dest->address, sizeof(dest_addr));
} else {
if (port_ptr->conn_status == NOT_CONNECTED) {
return -EDESTADDRREQ;
} else if (port_ptr->conn_status < CONNECTION_RESET) {
return -ENETRESET;
} else {
memcpy(&dest_addr.addr.port_addr, &port_ptr->dest_addr,
sizeof(struct msm_ipc_port_addr));
dest_addr.addrtype = MSM_IPC_ADDR_ID;
}
}
if (total_len > MAX_IPC_PKT_SIZE)
return -EINVAL;
lock_sock(sk);
timeout = sock_sndtimeo(sk, m->msg_flags & MSG_DONTWAIT);
msg = msm_ipc_router_build_msg(m, total_len);
if (!msg) {
IPC_RTR_ERR("%s: Msg build failure\n", __func__);
ret = -ENOMEM;
goto out_sendmsg;
}
kmemleak_not_leak(msg);
if (port_ptr->type == CLIENT_PORT)
wait_for_irsc_completion();
ret = msm_ipc_router_send_to(port_ptr, msg, &dest_addr, timeout);
if (ret != total_len) {
if (ret < 0) {
if (ret != -EAGAIN)
IPC_RTR_ERR("%s: Send_to failure %d\n",
__func__, ret);
msm_ipc_router_free_skb(msg);
} else if (ret >= 0) {
ret = -EFAULT;
}
}
out_sendmsg:
release_sock(sk);
return ret;
}
static int msm_ipc_router_recvmsg(struct socket *sock,
struct msghdr *m, size_t buf_len, int flags)
{
struct sock *sk = sock->sk;
struct msm_ipc_port *port_ptr = msm_ipc_sk_port(sk);
struct rr_packet *pkt;
long timeout;
int ret;
lock_sock(sk);
if (!buf_len) {
if (flags & MSG_PEEK)
ret = msm_ipc_router_get_curr_pkt_size(port_ptr);
else
ret = -EINVAL;
release_sock(sk);
return ret;
}
timeout = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);
ret = msm_ipc_router_rx_data_wait(port_ptr, timeout);
if (ret) {
release_sock(sk);
if (ret == -ENOMSG)
m->msg_namelen = 0;
return ret;
}
ret = msm_ipc_router_read(port_ptr, &pkt, buf_len);
if (ret <= 0 || !pkt) {
release_sock(sk);
return ret;
}
ret = msm_ipc_router_extract_msg(m, pkt);
release_pkt(pkt);
release_sock(sk);
return ret;
}
static int msm_ipc_router_ioctl(struct socket *sock,
unsigned int cmd, unsigned long arg)
{
struct sock *sk = sock->sk;
struct msm_ipc_port *port_ptr;
struct server_lookup_args server_arg;
struct msm_ipc_server_info *srv_info = NULL;
unsigned int n;
size_t srv_info_sz = 0;
int ret;
if (!sk)
return -EINVAL;
lock_sock(sk);
port_ptr = msm_ipc_sk_port(sock->sk);
if (!port_ptr) {
release_sock(sk);
return -EINVAL;
}
switch (cmd) {
case IPC_ROUTER_IOCTL_GET_VERSION:
n = IPC_ROUTER_V1;
ret = put_user(n, (unsigned int *)arg);
break;
case IPC_ROUTER_IOCTL_GET_MTU:
n = (MAX_IPC_PKT_SIZE - IPC_ROUTER_HDR_SIZE);
ret = put_user(n, (unsigned int *)arg);
break;
case IPC_ROUTER_IOCTL_GET_CURR_PKT_SIZE:
ret = msm_ipc_router_get_curr_pkt_size(port_ptr);
break;
case IPC_ROUTER_IOCTL_LOOKUP_SERVER:
if (!msm_ipc_sk(sk)->default_node_vote_info)
msm_ipc_sk(sk)->default_node_vote_info =
msm_ipc_load_default_node();
ret = copy_from_user(&server_arg, (void *)arg,
sizeof(server_arg));
if (ret) {
ret = -EFAULT;
break;
}
if (server_arg.num_entries_in_array < 0) {
ret = -EINVAL;
break;
}
if (server_arg.num_entries_in_array) {
if (server_arg.num_entries_in_array >
(SIZE_MAX / sizeof(*srv_info))) {
IPC_RTR_ERR("%s: Integer Overflow %zu * %d\n",
__func__, sizeof(*srv_info),
server_arg.num_entries_in_array);
ret = -EINVAL;
break;
}
srv_info_sz = server_arg.num_entries_in_array *
sizeof(*srv_info);
srv_info = kmalloc(srv_info_sz, GFP_KERNEL);
if (!srv_info) {
ret = -ENOMEM;
break;
}
}
ret = msm_ipc_router_lookup_server_name(&server_arg.port_name,
srv_info, server_arg.num_entries_in_array,
server_arg.lookup_mask);
if (ret < 0) {
IPC_RTR_ERR("%s: Server not found\n", __func__);
ret = -ENODEV;
kfree(srv_info);
break;
}
server_arg.num_entries_found = ret;
ret = copy_to_user((void *)arg, &server_arg,
sizeof(server_arg));
n = min(server_arg.num_entries_found,
server_arg.num_entries_in_array);
if (ret == 0 && n) {
ret = copy_to_user((void *)(arg + sizeof(server_arg)),
srv_info, n * sizeof(*srv_info));
}
if (ret)
ret = -EFAULT;
kfree(srv_info);
break;
case IPC_ROUTER_IOCTL_BIND_CONTROL_PORT:
ret = msm_ipc_router_bind_control_port(port_ptr);
break;
case IPC_ROUTER_IOCTL_CONFIG_SEC_RULES:
ret = msm_ipc_config_sec_rules((void *)arg);
if (ret != -EPERM)
port_ptr->type = IRSC_PORT;
break;
default:
ret = -EINVAL;
}
release_sock(sk);
return ret;
}
static unsigned int msm_ipc_router_poll(struct file *file,
struct socket *sock, poll_table *wait)
{
struct sock *sk = sock->sk;
struct msm_ipc_port *port_ptr;
uint32_t mask = 0;
if (!sk)
return -EINVAL;
port_ptr = msm_ipc_sk_port(sk);
if (!port_ptr)
return -EINVAL;
poll_wait(file, &port_ptr->port_rx_wait_q, wait);
if (!list_empty(&port_ptr->port_rx_q))
mask |= (POLLRDNORM | POLLIN);
if (port_ptr->conn_status == CONNECTION_RESET)
mask |= (POLLHUP | POLLERR);
return mask;
}
static int msm_ipc_router_close(struct socket *sock)
{
struct sock *sk = sock->sk;
struct msm_ipc_port *port_ptr;
int ret;
if (!sk)
return -EINVAL;
lock_sock(sk);
port_ptr = msm_ipc_sk_port(sk);
if (!port_ptr) {
release_sock(sk);
return -EINVAL;
}
ret = msm_ipc_router_close_port(port_ptr);
msm_ipc_unload_default_node(msm_ipc_sk(sk)->default_node_vote_info);
release_sock(sk);
sock_put(sk);
sock->sk = NULL;
return ret;
}
/**
* register_ipcrtr_af_init_notifier() - Register for ipc router socket
* address family initialization callback
* @nb: Notifier block which will be notified when address family is
* initialized.
*
* Return: 0 on success, standard error code otherwise.
*/
int register_ipcrtr_af_init_notifier(struct notifier_block *nb)
{
int ret;
if (!nb)
return -EINVAL;
mutex_lock(&ipcrtr_af_init_lock);
if (sockets_enabled)
nb->notifier_call(nb, IPCRTR_AF_INIT, NULL);
ret = raw_notifier_chain_register(&ipcrtr_af_init_chain, nb);
mutex_unlock(&ipcrtr_af_init_lock);
return ret;
}
EXPORT_SYMBOL(register_ipcrtr_af_init_notifier);
/**
* unregister_ipcrtr_af_init_notifier() - Unregister for ipc router socket
* address family initialization callback
* @nb: Notifier block which will be notified once address family is
* initialized.
*
* Return: 0 on success, standard error code otherwise.
*/
int unregister_ipcrtr_af_init_notifier(struct notifier_block *nb)
{
int ret;
if (!nb)
return -EINVAL;
ret = raw_notifier_chain_unregister(&ipcrtr_af_init_chain, nb);
return ret;
}
EXPORT_SYMBOL(unregister_ipcrtr_af_init_notifier);
static const struct net_proto_family msm_ipc_family_ops = {
.owner = THIS_MODULE,
.family = AF_MSM_IPC,
.create = msm_ipc_router_create
};
static const struct proto_ops msm_ipc_proto_ops = {
.family = AF_MSM_IPC,
.owner = THIS_MODULE,
.release = msm_ipc_router_close,
.bind = msm_ipc_router_bind,
.connect = ipc_router_connect,
.socketpair = sock_no_socketpair,
.accept = sock_no_accept,
.getname = sock_no_getname,
.poll = msm_ipc_router_poll,
.ioctl = msm_ipc_router_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = msm_ipc_router_ioctl,
#endif
.listen = sock_no_listen,
.shutdown = sock_no_shutdown,
.setsockopt = sock_no_setsockopt,
.getsockopt = sock_no_getsockopt,
#ifdef CONFIG_COMPAT
.compat_setsockopt = sock_no_setsockopt,
.compat_getsockopt = sock_no_getsockopt,
#endif
.sendmsg = msm_ipc_router_sendmsg,
.recvmsg = msm_ipc_router_recvmsg,
.mmap = sock_no_mmap,
.sendpage = sock_no_sendpage,
};
static struct proto msm_ipc_proto = {
.name = "MSM_IPC",
.owner = THIS_MODULE,
.obj_size = sizeof(struct msm_ipc_sock),
};
int msm_ipc_router_init_sockets(void)
{
int ret;
ret = proto_register(&msm_ipc_proto, 1);
if (ret) {
IPC_RTR_ERR("%s: Failed to register MSM_IPC protocol type\n",
__func__);
goto out_init_sockets;
}
ret = sock_register(&msm_ipc_family_ops);
if (ret) {
IPC_RTR_ERR("%s: Failed to register MSM_IPC socket type\n",
__func__);
proto_unregister(&msm_ipc_proto);
goto out_init_sockets;
}
mutex_lock(&ipcrtr_af_init_lock);
sockets_enabled = 1;
raw_notifier_call_chain(&ipcrtr_af_init_chain,
IPCRTR_AF_INIT, NULL);
mutex_unlock(&ipcrtr_af_init_lock);
out_init_sockets:
return ret;
}
void msm_ipc_router_exit_sockets(void)
{
if (!sockets_enabled)
return;
sock_unregister(msm_ipc_family_ops.family);
proto_unregister(&msm_ipc_proto);
mutex_lock(&ipcrtr_af_init_lock);
sockets_enabled = 0;
raw_notifier_call_chain(&ipcrtr_af_init_chain,
IPCRTR_AF_DEINIT, NULL);
mutex_unlock(&ipcrtr_af_init_lock);
}