/* Copyright (c) 2011-2015, 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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) { IPC_RTR_ERR("%s: Invalid pointers passed\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); }