diff options
Diffstat (limited to 'net/socket.c')
-rw-r--r-- | net/socket.c | 353 |
1 files changed, 212 insertions, 141 deletions
diff --git a/net/socket.c b/net/socket.c index 7e1bdef8b09..e2d5bae994d 100644 --- a/net/socket.c +++ b/net/socket.c @@ -68,6 +68,7 @@ #include <linux/netdevice.h> #include <linux/proc_fs.h> #include <linux/seq_file.h> +#include <linux/mutex.h> #include <linux/wanrouter.h> #include <linux/if_bridge.h> #include <linux/if_frad.h> @@ -106,6 +107,10 @@ static unsigned int sock_poll(struct file *file, struct poll_table_struct *wait); static long sock_ioctl(struct file *file, unsigned int cmd, unsigned long arg); +#ifdef CONFIG_COMPAT +static long compat_sock_ioctl(struct file *file, + unsigned int cmd, unsigned long arg); +#endif static int sock_fasync(int fd, struct file *filp, int on); static ssize_t sock_readv(struct file *file, const struct iovec *vector, unsigned long count, loff_t *ppos); @@ -127,6 +132,9 @@ static struct file_operations socket_file_ops = { .aio_write = sock_aio_write, .poll = sock_poll, .unlocked_ioctl = sock_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = compat_sock_ioctl, +#endif .mmap = sock_mmap, .open = sock_no_open, /* special open code to disallow open via /proc */ .release = sock_close, @@ -348,8 +356,8 @@ static struct dentry_operations sockfs_dentry_operations = { /* * Obtains the first available file descriptor and sets it up for use. * - * This function creates file structure and maps it to fd space - * of current process. On success it returns file descriptor + * These functions create file structures and maps them to fd space + * of the current process. On success it returns file descriptor * and file struct implicitly stored in sock->file. * Note that another thread may close file descriptor before we return * from this function. We use the fact that now we do not refer @@ -362,53 +370,90 @@ static struct dentry_operations sockfs_dentry_operations = { * but we take care of internal coherence yet. */ -int sock_map_fd(struct socket *sock) +static int sock_alloc_fd(struct file **filep) { int fd; - struct qstr this; - char name[32]; - - /* - * Find a file descriptor suitable for return to the user. - */ fd = get_unused_fd(); - if (fd >= 0) { + if (likely(fd >= 0)) { struct file *file = get_empty_filp(); - if (!file) { + *filep = file; + if (unlikely(!file)) { put_unused_fd(fd); - fd = -ENFILE; - goto out; + return -ENFILE; } + } else + *filep = NULL; + return fd; +} + +static int sock_attach_fd(struct socket *sock, struct file *file) +{ + struct qstr this; + char name[32]; - this.len = sprintf(name, "[%lu]", SOCK_INODE(sock)->i_ino); - this.name = name; - this.hash = SOCK_INODE(sock)->i_ino; + this.len = sprintf(name, "[%lu]", SOCK_INODE(sock)->i_ino); + this.name = name; + this.hash = SOCK_INODE(sock)->i_ino; - file->f_dentry = d_alloc(sock_mnt->mnt_sb->s_root, &this); - if (!file->f_dentry) { - put_filp(file); + file->f_dentry = d_alloc(sock_mnt->mnt_sb->s_root, &this); + if (unlikely(!file->f_dentry)) + return -ENOMEM; + + file->f_dentry->d_op = &sockfs_dentry_operations; + d_add(file->f_dentry, SOCK_INODE(sock)); + file->f_vfsmnt = mntget(sock_mnt); + file->f_mapping = file->f_dentry->d_inode->i_mapping; + + sock->file = file; + file->f_op = SOCK_INODE(sock)->i_fop = &socket_file_ops; + file->f_mode = FMODE_READ | FMODE_WRITE; + file->f_flags = O_RDWR; + file->f_pos = 0; + file->private_data = sock; + + return 0; +} + +int sock_map_fd(struct socket *sock) +{ + struct file *newfile; + int fd = sock_alloc_fd(&newfile); + + if (likely(fd >= 0)) { + int err = sock_attach_fd(sock, newfile); + + if (unlikely(err < 0)) { + put_filp(newfile); put_unused_fd(fd); - fd = -ENOMEM; - goto out; + return err; } - file->f_dentry->d_op = &sockfs_dentry_operations; - d_add(file->f_dentry, SOCK_INODE(sock)); - file->f_vfsmnt = mntget(sock_mnt); - file->f_mapping = file->f_dentry->d_inode->i_mapping; + fd_install(fd, newfile); + } + return fd; +} - sock->file = file; - file->f_op = SOCK_INODE(sock)->i_fop = &socket_file_ops; - file->f_mode = FMODE_READ | FMODE_WRITE; - file->f_flags = O_RDWR; - file->f_pos = 0; - file->private_data = sock; - fd_install(fd, file); +static struct socket *sock_from_file(struct file *file, int *err) +{ + struct inode *inode; + struct socket *sock; + + if (file->f_op == &socket_file_ops) + return file->private_data; /* set in sock_map_fd */ + + inode = file->f_dentry->d_inode; + if (!S_ISSOCK(inode->i_mode)) { + *err = -ENOTSOCK; + return NULL; } -out: - return fd; + sock = SOCKET_I(inode); + if (sock->file != file) { + printk(KERN_ERR "socki_lookup: socket file changed!\n"); + sock->file = file; + } + return sock; } /** @@ -427,31 +472,31 @@ out: struct socket *sockfd_lookup(int fd, int *err) { struct file *file; - struct inode *inode; struct socket *sock; - if (!(file = fget(fd))) - { + if (!(file = fget(fd))) { *err = -EBADF; return NULL; } - - if (file->f_op == &socket_file_ops) - return file->private_data; /* set in sock_map_fd */ - - inode = file->f_dentry->d_inode; - if (!S_ISSOCK(inode->i_mode)) { - *err = -ENOTSOCK; + sock = sock_from_file(file, err); + if (!sock) fput(file); - return NULL; - } + return sock; +} - sock = SOCKET_I(inode); - if (sock->file != file) { - printk(KERN_ERR "socki_lookup: socket file changed!\n"); - sock->file = file; +static struct socket *sockfd_lookup_light(int fd, int *err, int *fput_needed) +{ + struct file *file; + struct socket *sock; + + file = fget_light(fd, fput_needed); + if (file) { + sock = sock_from_file(file, err); + if (sock) + return sock; + fput_light(file, *fput_needed); } - return sock; + return NULL; } /** @@ -789,36 +834,36 @@ static ssize_t sock_aio_write(struct kiocb *iocb, const char __user *ubuf, * with module unload. */ -static DECLARE_MUTEX(br_ioctl_mutex); +static DEFINE_MUTEX(br_ioctl_mutex); static int (*br_ioctl_hook)(unsigned int cmd, void __user *arg) = NULL; void brioctl_set(int (*hook)(unsigned int, void __user *)) { - down(&br_ioctl_mutex); + mutex_lock(&br_ioctl_mutex); br_ioctl_hook = hook; - up(&br_ioctl_mutex); + mutex_unlock(&br_ioctl_mutex); } EXPORT_SYMBOL(brioctl_set); -static DECLARE_MUTEX(vlan_ioctl_mutex); +static DEFINE_MUTEX(vlan_ioctl_mutex); static int (*vlan_ioctl_hook)(void __user *arg); void vlan_ioctl_set(int (*hook)(void __user *)) { - down(&vlan_ioctl_mutex); + mutex_lock(&vlan_ioctl_mutex); vlan_ioctl_hook = hook; - up(&vlan_ioctl_mutex); + mutex_unlock(&vlan_ioctl_mutex); } EXPORT_SYMBOL(vlan_ioctl_set); -static DECLARE_MUTEX(dlci_ioctl_mutex); +static DEFINE_MUTEX(dlci_ioctl_mutex); static int (*dlci_ioctl_hook)(unsigned int, void __user *); void dlci_ioctl_set(int (*hook)(unsigned int, void __user *)) { - down(&dlci_ioctl_mutex); + mutex_lock(&dlci_ioctl_mutex); dlci_ioctl_hook = hook; - up(&dlci_ioctl_mutex); + mutex_unlock(&dlci_ioctl_mutex); } EXPORT_SYMBOL(dlci_ioctl_set); @@ -862,10 +907,10 @@ static long sock_ioctl(struct file *file, unsigned cmd, unsigned long arg) if (!br_ioctl_hook) request_module("bridge"); - down(&br_ioctl_mutex); + mutex_lock(&br_ioctl_mutex); if (br_ioctl_hook) err = br_ioctl_hook(cmd, argp); - up(&br_ioctl_mutex); + mutex_unlock(&br_ioctl_mutex); break; case SIOCGIFVLAN: case SIOCSIFVLAN: @@ -873,10 +918,10 @@ static long sock_ioctl(struct file *file, unsigned cmd, unsigned long arg) if (!vlan_ioctl_hook) request_module("8021q"); - down(&vlan_ioctl_mutex); + mutex_lock(&vlan_ioctl_mutex); if (vlan_ioctl_hook) err = vlan_ioctl_hook(argp); - up(&vlan_ioctl_mutex); + mutex_unlock(&vlan_ioctl_mutex); break; case SIOCGIFDIVERT: case SIOCSIFDIVERT: @@ -890,9 +935,9 @@ static long sock_ioctl(struct file *file, unsigned cmd, unsigned long arg) request_module("dlci"); if (dlci_ioctl_hook) { - down(&dlci_ioctl_mutex); + mutex_lock(&dlci_ioctl_mutex); err = dlci_ioctl_hook(cmd, argp); - up(&dlci_ioctl_mutex); + mutex_unlock(&dlci_ioctl_mutex); } break; default: @@ -1286,19 +1331,17 @@ asmlinkage long sys_bind(int fd, struct sockaddr __user *umyaddr, int addrlen) { struct socket *sock; char address[MAX_SOCK_ADDR]; - int err; + int err, fput_needed; - if((sock = sockfd_lookup(fd,&err))!=NULL) + if((sock = sockfd_lookup_light(fd, &err, &fput_needed))!=NULL) { if((err=move_addr_to_kernel(umyaddr,addrlen,address))>=0) { err = security_socket_bind(sock, (struct sockaddr *)address, addrlen); - if (err) { - sockfd_put(sock); - return err; - } - err = sock->ops->bind(sock, (struct sockaddr *)address, addrlen); + if (!err) + err = sock->ops->bind(sock, + (struct sockaddr *)address, addrlen); } - sockfd_put(sock); + fput_light(sock->file, fput_needed); } return err; } @@ -1315,20 +1358,17 @@ int sysctl_somaxconn = SOMAXCONN; asmlinkage long sys_listen(int fd, int backlog) { struct socket *sock; - int err; + int err, fput_needed; - if ((sock = sockfd_lookup(fd, &err)) != NULL) { + if ((sock = sockfd_lookup_light(fd, &err, &fput_needed)) != NULL) { if ((unsigned) backlog > sysctl_somaxconn) backlog = sysctl_somaxconn; err = security_socket_listen(sock, backlog); - if (err) { - sockfd_put(sock); - return err; - } + if (!err) + err = sock->ops->listen(sock, backlog); - err=sock->ops->listen(sock, backlog); - sockfd_put(sock); + fput_light(sock->file, fput_needed); } return err; } @@ -1349,10 +1389,11 @@ asmlinkage long sys_listen(int fd, int backlog) asmlinkage long sys_accept(int fd, struct sockaddr __user *upeer_sockaddr, int __user *upeer_addrlen) { struct socket *sock, *newsock; - int err, len; + struct file *newfile; + int err, len, newfd, fput_needed; char address[MAX_SOCK_ADDR]; - sock = sockfd_lookup(fd, &err); + sock = sockfd_lookup_light(fd, &err, &fput_needed); if (!sock) goto out; @@ -1369,35 +1410,48 @@ asmlinkage long sys_accept(int fd, struct sockaddr __user *upeer_sockaddr, int _ */ __module_get(newsock->ops->owner); + newfd = sock_alloc_fd(&newfile); + if (unlikely(newfd < 0)) { + err = newfd; + goto out_release; + } + + err = sock_attach_fd(newsock, newfile); + if (err < 0) + goto out_fd; + err = security_socket_accept(sock, newsock); if (err) - goto out_release; + goto out_fd; err = sock->ops->accept(sock, newsock, sock->file->f_flags); if (err < 0) - goto out_release; + goto out_fd; if (upeer_sockaddr) { if(newsock->ops->getname(newsock, (struct sockaddr *)address, &len, 2)<0) { err = -ECONNABORTED; - goto out_release; + goto out_fd; } err = move_addr_to_user(address, len, upeer_sockaddr, upeer_addrlen); if (err < 0) - goto out_release; + goto out_fd; } /* File flags are not inherited via accept() unlike another OSes. */ - if ((err = sock_map_fd(newsock)) < 0) - goto out_release; + fd_install(newfd, newfile); + err = newfd; security_socket_post_accept(sock, newsock); out_put: - sockfd_put(sock); + fput_light(sock->file, fput_needed); out: return err; +out_fd: + put_filp(newfile); + put_unused_fd(newfd); out_release: sock_release(newsock); goto out_put; @@ -1420,9 +1474,9 @@ asmlinkage long sys_connect(int fd, struct sockaddr __user *uservaddr, int addrl { struct socket *sock; char address[MAX_SOCK_ADDR]; - int err; + int err, fput_needed; - sock = sockfd_lookup(fd, &err); + sock = sockfd_lookup_light(fd, &err, &fput_needed); if (!sock) goto out; err = move_addr_to_kernel(uservaddr, addrlen, address); @@ -1436,7 +1490,7 @@ asmlinkage long sys_connect(int fd, struct sockaddr __user *uservaddr, int addrl err = sock->ops->connect(sock, (struct sockaddr *) address, addrlen, sock->file->f_flags); out_put: - sockfd_put(sock); + fput_light(sock->file, fput_needed); out: return err; } @@ -1450,9 +1504,9 @@ asmlinkage long sys_getsockname(int fd, struct sockaddr __user *usockaddr, int _ { struct socket *sock; char address[MAX_SOCK_ADDR]; - int len, err; + int len, err, fput_needed; - sock = sockfd_lookup(fd, &err); + sock = sockfd_lookup_light(fd, &err, &fput_needed); if (!sock) goto out; @@ -1466,7 +1520,7 @@ asmlinkage long sys_getsockname(int fd, struct sockaddr __user *usockaddr, int _ err = move_addr_to_user(address, len, usockaddr, usockaddr_len); out_put: - sockfd_put(sock); + fput_light(sock->file, fput_needed); out: return err; } @@ -1480,20 +1534,19 @@ asmlinkage long sys_getpeername(int fd, struct sockaddr __user *usockaddr, int _ { struct socket *sock; char address[MAX_SOCK_ADDR]; - int len, err; + int len, err, fput_needed; - if ((sock = sockfd_lookup(fd, &err))!=NULL) - { + if ((sock = sockfd_lookup_light(fd, &err, &fput_needed)) != NULL) { err = security_socket_getpeername(sock); if (err) { - sockfd_put(sock); + fput_light(sock->file, fput_needed); return err; } err = sock->ops->getname(sock, (struct sockaddr *)address, &len, 1); if (!err) err=move_addr_to_user(address,len, usockaddr, usockaddr_len); - sockfd_put(sock); + fput_light(sock->file, fput_needed); } return err; } @@ -1512,10 +1565,16 @@ asmlinkage long sys_sendto(int fd, void __user * buff, size_t len, unsigned flag int err; struct msghdr msg; struct iovec iov; - - sock = sockfd_lookup(fd, &err); + int fput_needed; + struct file *sock_file; + + sock_file = fget_light(fd, &fput_needed); + if (!sock_file) + return -EBADF; + + sock = sock_from_file(sock_file, &err); if (!sock) - goto out; + goto out_put; iov.iov_base=buff; iov.iov_len=len; msg.msg_name=NULL; @@ -1524,8 +1583,7 @@ asmlinkage long sys_sendto(int fd, void __user * buff, size_t len, unsigned flag msg.msg_control=NULL; msg.msg_controllen=0; msg.msg_namelen=0; - if(addr) - { + if (addr) { err = move_addr_to_kernel(addr, addr_len, address); if (err < 0) goto out_put; @@ -1538,8 +1596,7 @@ asmlinkage long sys_sendto(int fd, void __user * buff, size_t len, unsigned flag err = sock_sendmsg(sock, &msg, len); out_put: - sockfd_put(sock); -out: + fput_light(sock_file, fput_needed); return err; } @@ -1566,8 +1623,14 @@ asmlinkage long sys_recvfrom(int fd, void __user * ubuf, size_t size, unsigned f struct msghdr msg; char address[MAX_SOCK_ADDR]; int err,err2; + struct file *sock_file; + int fput_needed; + + sock_file = fget_light(fd, &fput_needed); + if (!sock_file) + return -EBADF; - sock = sockfd_lookup(fd, &err); + sock = sock_from_file(sock_file, &err); if (!sock) goto out; @@ -1589,8 +1652,8 @@ asmlinkage long sys_recvfrom(int fd, void __user * ubuf, size_t size, unsigned f if(err2<0) err=err2; } - sockfd_put(sock); out: + fput_light(sock_file, fput_needed); return err; } @@ -1610,25 +1673,24 @@ asmlinkage long sys_recv(int fd, void __user * ubuf, size_t size, unsigned flags asmlinkage long sys_setsockopt(int fd, int level, int optname, char __user *optval, int optlen) { - int err; + int err, fput_needed; struct socket *sock; if (optlen < 0) return -EINVAL; - if ((sock = sockfd_lookup(fd, &err))!=NULL) + if ((sock = sockfd_lookup_light(fd, &err, &fput_needed)) != NULL) { err = security_socket_setsockopt(sock,level,optname); - if (err) { - sockfd_put(sock); - return err; - } + if (err) + goto out_put; if (level == SOL_SOCKET) err=sock_setsockopt(sock,level,optname,optval,optlen); else err=sock->ops->setsockopt(sock, level, optname, optval, optlen); - sockfd_put(sock); +out_put: + fput_light(sock->file, fput_needed); } return err; } @@ -1640,23 +1702,20 @@ asmlinkage long sys_setsockopt(int fd, int level, int optname, char __user *optv asmlinkage long sys_getsockopt(int fd, int level, int optname, char __user *optval, int __user *optlen) { - int err; + int err, fput_needed; struct socket *sock; - if ((sock = sockfd_lookup(fd, &err))!=NULL) - { - err = security_socket_getsockopt(sock, level, - optname); - if (err) { - sockfd_put(sock); - return err; - } + if ((sock = sockfd_lookup_light(fd, &err, &fput_needed)) != NULL) { + err = security_socket_getsockopt(sock, level, optname); + if (err) + goto out_put; if (level == SOL_SOCKET) err=sock_getsockopt(sock,level,optname,optval,optlen); else err=sock->ops->getsockopt(sock, level, optname, optval, optlen); - sockfd_put(sock); +out_put: + fput_light(sock->file, fput_needed); } return err; } @@ -1668,19 +1727,15 @@ asmlinkage long sys_getsockopt(int fd, int level, int optname, char __user *optv asmlinkage long sys_shutdown(int fd, int how) { - int err; + int err, fput_needed; struct socket *sock; - if ((sock = sockfd_lookup(fd, &err))!=NULL) + if ((sock = sockfd_lookup_light(fd, &err, &fput_needed))!=NULL) { err = security_socket_shutdown(sock, how); - if (err) { - sockfd_put(sock); - return err; - } - - err=sock->ops->shutdown(sock, how); - sockfd_put(sock); + if (!err) + err = sock->ops->shutdown(sock, how); + fput_light(sock->file, fput_needed); } return err; } @@ -1709,6 +1764,7 @@ asmlinkage long sys_sendmsg(int fd, struct msghdr __user *msg, unsigned flags) unsigned char *ctl_buf = ctl; struct msghdr msg_sys; int err, ctl_len, iov_size, total_len; + int fput_needed; err = -EFAULT; if (MSG_CMSG_COMPAT & flags) { @@ -1717,7 +1773,7 @@ asmlinkage long sys_sendmsg(int fd, struct msghdr __user *msg, unsigned flags) } else if (copy_from_user(&msg_sys, msg, sizeof(struct msghdr))) return -EFAULT; - sock = sockfd_lookup(fd, &err); + sock = sockfd_lookup_light(fd, &err, &fput_needed); if (!sock) goto out; @@ -1785,7 +1841,7 @@ out_freeiov: if (iov != iovstack) sock_kfree_s(sock->sk, iov, iov_size); out_put: - sockfd_put(sock); + fput_light(sock->file, fput_needed); out: return err; } @@ -1803,6 +1859,7 @@ asmlinkage long sys_recvmsg(int fd, struct msghdr __user *msg, unsigned int flag struct msghdr msg_sys; unsigned long cmsg_ptr; int err, iov_size, total_len, len; + int fput_needed; /* kernel mode address */ char addr[MAX_SOCK_ADDR]; @@ -1818,7 +1875,7 @@ asmlinkage long sys_recvmsg(int fd, struct msghdr __user *msg, unsigned int flag if (copy_from_user(&msg_sys,msg,sizeof(struct msghdr))) return -EFAULT; - sock = sockfd_lookup(fd, &err); + sock = sockfd_lookup_light(fd, &err, &fput_needed); if (!sock) goto out; @@ -1885,7 +1942,7 @@ out_freeiov: if (iov != iovstack) sock_kfree_s(sock->sk, iov, iov_size); out_put: - sockfd_put(sock); + fput_light(sock->file, fput_needed); out: return err; } @@ -2086,6 +2143,20 @@ void socket_seq_show(struct seq_file *seq) } #endif /* CONFIG_PROC_FS */ +#ifdef CONFIG_COMPAT +static long compat_sock_ioctl(struct file *file, unsigned cmd, + unsigned long arg) +{ + struct socket *sock = file->private_data; + int ret = -ENOIOCTLCMD; + + if (sock->ops->compat_ioctl) + ret = sock->ops->compat_ioctl(sock, cmd, arg); + + return ret; +} +#endif + /* ABI emulation layers need these two */ EXPORT_SYMBOL(move_addr_to_kernel); EXPORT_SYMBOL(move_addr_to_user); |