X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=net%2Fcompat.c;h=d5d69fa15d07a65a26975797d654813e5f055568;hb=43bc926fffd92024b46cafaf7350d669ba9ca884;hp=59937f595a61ccb21edced81546c2da63f26f06b;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/net/compat.c b/net/compat.c index 59937f595..d5d69fa15 100644 --- a/net/compat.c +++ b/net/compat.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -29,7 +30,7 @@ #include static inline int iov_from_user_compat_to_kern(struct iovec *kiov, - struct compat_iovec *uiov32, + struct compat_iovec __user *uiov32, int niov) { int tot_len = 0; @@ -53,7 +54,7 @@ static inline int iov_from_user_compat_to_kern(struct iovec *kiov, return tot_len; } -int get_compat_msghdr(struct msghdr *kmsg, struct compat_msghdr *umsg) +int get_compat_msghdr(struct msghdr *kmsg, struct compat_msghdr __user *umsg) { compat_uptr_t tmp1, tmp2, tmp3; @@ -90,20 +91,11 @@ int verify_compat_iovec(struct msghdr *kern_msg, struct iovec *kern_iov, } else kern_msg->msg_name = NULL; - if(kern_msg->msg_iovlen > UIO_FASTIOV) { - kern_iov = kmalloc(kern_msg->msg_iovlen * sizeof(struct iovec), - GFP_KERNEL); - if(!kern_iov) - return -ENOMEM; - } - tot_len = iov_from_user_compat_to_kern(kern_iov, - (struct compat_iovec *)kern_msg->msg_iov, + (struct compat_iovec __user *)kern_msg->msg_iov, kern_msg->msg_iovlen); if(tot_len >= 0) kern_msg->msg_iov = kern_iov; - else if(kern_msg->msg_iovlen > UIO_FASTIOV) - kfree(kern_iov); return tot_len; } @@ -112,7 +104,7 @@ int verify_compat_iovec(struct msghdr *kern_msg, struct iovec *kern_iov, #define CMSG_COMPAT_ALIGN(len) ALIGN((len), sizeof(s32)) #define CMSG_COMPAT_DATA(cmsg) \ - ((void *)((char *)(cmsg) + CMSG_COMPAT_ALIGN(sizeof(struct compat_cmsghdr)))) + ((void __user *)((char __user *)(cmsg) + CMSG_COMPAT_ALIGN(sizeof(struct compat_cmsghdr)))) #define CMSG_COMPAT_SPACE(len) \ (CMSG_COMPAT_ALIGN(sizeof(struct compat_cmsghdr)) + CMSG_COMPAT_ALIGN(len)) #define CMSG_COMPAT_LEN(len) \ @@ -120,33 +112,37 @@ int verify_compat_iovec(struct msghdr *kern_msg, struct iovec *kern_iov, #define CMSG_COMPAT_FIRSTHDR(msg) \ (((msg)->msg_controllen) >= sizeof(struct compat_cmsghdr) ? \ - (struct compat_cmsghdr *)((msg)->msg_control) : \ - (struct compat_cmsghdr *)NULL) + (struct compat_cmsghdr __user *)((msg)->msg_control) : \ + (struct compat_cmsghdr __user *)NULL) -static inline struct compat_cmsghdr *cmsg_compat_nxthdr(struct msghdr *msg, - struct compat_cmsghdr *cmsg, int cmsg_len) -{ - struct compat_cmsghdr *ptr; +#define CMSG_COMPAT_OK(ucmlen, ucmsg, mhdr) \ + ((ucmlen) >= sizeof(struct compat_cmsghdr) && \ + (ucmlen) <= (unsigned long) \ + ((mhdr)->msg_controllen - \ + ((char *)(ucmsg) - (char *)(mhdr)->msg_control))) - ptr = (struct compat_cmsghdr *)(((unsigned char *)cmsg) + - CMSG_COMPAT_ALIGN(cmsg_len)); - if ((unsigned long)((char *)(ptr + 1) - (char *)msg->msg_control) > +static inline struct compat_cmsghdr __user *cmsg_compat_nxthdr(struct msghdr *msg, + struct compat_cmsghdr __user *cmsg, int cmsg_len) +{ + char __user *ptr = (char __user *)cmsg + CMSG_COMPAT_ALIGN(cmsg_len); + if ((unsigned long)(ptr + 1 - (char __user *)msg->msg_control) > msg->msg_controllen) return NULL; - return ptr; + return (struct compat_cmsghdr __user *)ptr; } /* There is a lot of hair here because the alignment rules (and * thus placement) of cmsg headers and length are different for * 32-bit apps. -DaveM */ -int cmsghdr_from_user_compat_to_kern(struct msghdr *kmsg, +int cmsghdr_from_user_compat_to_kern(struct msghdr *kmsg, struct sock *sk, unsigned char *stackbuf, int stackbuf_size) { - struct compat_cmsghdr *ucmsg; + struct compat_cmsghdr __user *ucmsg; struct cmsghdr *kcmsg, *kcmsg_base; compat_size_t ucmlen; __kernel_size_t kcmlen, tmp; + int err = -EFAULT; kcmlen = 0; kcmsg_base = kcmsg = (struct cmsghdr *)stackbuf; @@ -156,15 +152,12 @@ int cmsghdr_from_user_compat_to_kern(struct msghdr *kmsg, return -EFAULT; /* Catch bogons. */ - if(CMSG_COMPAT_ALIGN(ucmlen) < - CMSG_COMPAT_ALIGN(sizeof(struct compat_cmsghdr))) - return -EINVAL; - if((unsigned long)(((char *)ucmsg - (char *)kmsg->msg_control) - + ucmlen) > kmsg->msg_controllen) + if (!CMSG_COMPAT_OK(ucmlen, ucmsg, kmsg)) return -EINVAL; tmp = ((ucmlen - CMSG_COMPAT_ALIGN(sizeof(*ucmsg))) + CMSG_ALIGN(sizeof(struct cmsghdr))); + tmp = CMSG_ALIGN(tmp); kcmlen += tmp; ucmsg = cmsg_compat_nxthdr(kmsg, ucmsg, ucmlen); } @@ -176,30 +169,34 @@ int cmsghdr_from_user_compat_to_kern(struct msghdr *kmsg, * until we have successfully copied over all of the data * from the user. */ - if(kcmlen > stackbuf_size) - kcmsg_base = kcmsg = kmalloc(kcmlen, GFP_KERNEL); - if(kcmsg == NULL) + if (kcmlen > stackbuf_size) + kcmsg_base = kcmsg = sock_kmalloc(sk, kcmlen, GFP_KERNEL); + if (kcmsg == NULL) return -ENOBUFS; /* Now copy them over neatly. */ memset(kcmsg, 0, kcmlen); ucmsg = CMSG_COMPAT_FIRSTHDR(kmsg); while(ucmsg != NULL) { - __get_user(ucmlen, &ucmsg->cmsg_len); + if (__get_user(ucmlen, &ucmsg->cmsg_len)) + goto Efault; + if (!CMSG_COMPAT_OK(ucmlen, ucmsg, kmsg)) + goto Einval; tmp = ((ucmlen - CMSG_COMPAT_ALIGN(sizeof(*ucmsg))) + CMSG_ALIGN(sizeof(struct cmsghdr))); + if ((char *)kcmsg_base + kcmlen - (char *)kcmsg < CMSG_ALIGN(tmp)) + goto Einval; kcmsg->cmsg_len = tmp; - __get_user(kcmsg->cmsg_level, &ucmsg->cmsg_level); - __get_user(kcmsg->cmsg_type, &ucmsg->cmsg_type); - - /* Copy over the data. */ - if(copy_from_user(CMSG_DATA(kcmsg), - CMSG_COMPAT_DATA(ucmsg), - (ucmlen - CMSG_COMPAT_ALIGN(sizeof(*ucmsg))))) - goto out_free_efault; + tmp = CMSG_ALIGN(tmp); + if (__get_user(kcmsg->cmsg_level, &ucmsg->cmsg_level) || + __get_user(kcmsg->cmsg_type, &ucmsg->cmsg_type) || + copy_from_user(CMSG_DATA(kcmsg), + CMSG_COMPAT_DATA(ucmsg), + (ucmlen - CMSG_COMPAT_ALIGN(sizeof(*ucmsg))))) + goto Efault; /* Advance. */ - kcmsg = (struct cmsghdr *)((char *)kcmsg + CMSG_ALIGN(tmp)); + kcmsg = (struct cmsghdr *)((char *)kcmsg + tmp); ucmsg = cmsg_compat_nxthdr(kmsg, ucmsg, ucmlen); } @@ -208,16 +205,18 @@ int cmsghdr_from_user_compat_to_kern(struct msghdr *kmsg, kmsg->msg_controllen = kcmlen; return 0; -out_free_efault: - if(kcmsg_base != (struct cmsghdr *)stackbuf) - kfree(kcmsg_base); - return -EFAULT; +Einval: + err = -EINVAL; +Efault: + if (kcmsg_base != (struct cmsghdr *)stackbuf) + sock_kfree_s(sk, kcmsg_base, kcmlen); + return err; } int put_cmsg_compat(struct msghdr *kmsg, int level, int type, int len, void *data) { struct compat_timeval ctv; - struct compat_cmsghdr *cm = (struct compat_cmsghdr *) kmsg->msg_control; + struct compat_cmsghdr __user *cm = (struct compat_cmsghdr __user *) kmsg->msg_control; struct compat_cmsghdr cmhdr; int cmlen; @@ -255,18 +254,21 @@ int put_cmsg_compat(struct msghdr *kmsg, int level, int type, int len, void *dat void scm_detach_fds_compat(struct msghdr *kmsg, struct scm_cookie *scm) { - struct compat_cmsghdr *cm = (struct compat_cmsghdr *) kmsg->msg_control; + struct compat_cmsghdr __user *cm = (struct compat_cmsghdr __user *) kmsg->msg_control; int fdmax = (kmsg->msg_controllen - sizeof(struct compat_cmsghdr)) / sizeof(int); int fdnum = scm->fp->count; struct file **fp = scm->fp->fp; - int *cmfptr; + int __user *cmfptr; int err = 0, i; if (fdnum < fdmax) fdmax = fdnum; - for (i = 0, cmfptr = (int *) CMSG_COMPAT_DATA(cm); i < fdmax; i++, cmfptr++) { + for (i = 0, cmfptr = (int __user *) CMSG_COMPAT_DATA(cm); i < fdmax; i++, cmfptr++) { int new_fd; + err = security_file_receive(fp[i]); + if (err) + break; err = get_unused_fd(); if (err < 0) break; @@ -322,10 +324,10 @@ struct compat_ipt_replace { }; static int do_netfilter_replace(int fd, int level, int optname, - char *optval, int optlen) + char __user *optval, int optlen) { - struct compat_ipt_replace *urepl = (struct compat_ipt_replace *)optval; - struct ipt_replace *repl_nat; + struct compat_ipt_replace __user *urepl; + struct ipt_replace __user *repl_nat; char name[IPT_TABLE_MAXNAMELEN]; u32 origsize, tmp32, num_counters; unsigned int repl_nat_size; @@ -333,6 +335,7 @@ static int do_netfilter_replace(int fd, int level, int optname, int i; compat_uptr_t ucntrs; + urepl = (struct compat_ipt_replace __user *)optval; if (get_user(origsize, &urepl->size)) return -EFAULT; @@ -399,7 +402,7 @@ static int do_netfilter_replace(int fd, int level, int optname, ret = sys_setsockopt(fd, level, optname, - (char *)repl_nat, repl_nat_size); + (char __user *)repl_nat, repl_nat_size); out: return ret; @@ -413,11 +416,11 @@ struct compat_sock_fprog { compat_uptr_t filter; /* struct sock_filter * */ }; -static int do_set_attach_filter(int fd, int level, int optname, - char *optval, int optlen) +static int do_set_attach_filter(struct socket *sock, int level, int optname, + char __user *optval, int optlen) { - struct compat_sock_fprog *fprog32 = (struct compat_sock_fprog *)optval; - struct sock_fprog *kfprog = compat_alloc_user_space(sizeof(struct sock_fprog)); + struct compat_sock_fprog __user *fprog32 = (struct compat_sock_fprog __user *)optval; + struct sock_fprog __user *kfprog = compat_alloc_user_space(sizeof(struct sock_fprog)); compat_uptr_t ptr; u16 len; @@ -429,13 +432,14 @@ static int do_set_attach_filter(int fd, int level, int optname, __put_user(compat_ptr(ptr), &kfprog->filter)) return -EFAULT; - return sys_setsockopt(fd, level, optname, (char *)kfprog, + return sock_setsockopt(sock, level, optname, (char __user *)kfprog, sizeof(struct sock_fprog)); } -static int do_set_sock_timeout(int fd, int level, int optname, char *optval, int optlen) +static int do_set_sock_timeout(struct socket *sock, int level, + int optname, char __user *optval, int optlen) { - struct compat_timeval *up = (struct compat_timeval *) optval; + struct compat_timeval __user *up = (struct compat_timeval __user *) optval; struct timeval ktime; mm_segment_t old_fs; int err; @@ -448,35 +452,68 @@ static int do_set_sock_timeout(int fd, int level, int optname, char *optval, int return -EFAULT; old_fs = get_fs(); set_fs(KERNEL_DS); - err = sys_setsockopt(fd, level, optname, (char *) &ktime, sizeof(ktime)); + err = sock_setsockopt(sock, level, optname, (char *) &ktime, sizeof(ktime)); set_fs(old_fs); return err; } -asmlinkage long compat_sys_setsockopt(int fd, int level, int optname, - char *optval, int optlen) +static int compat_sock_setsockopt(struct socket *sock, int level, int optname, + char __user *optval, int optlen) { - if (optname == IPT_SO_SET_REPLACE) - return do_netfilter_replace(fd, level, optname, - optval, optlen); if (optname == SO_ATTACH_FILTER) - return do_set_attach_filter(fd, level, optname, + return do_set_attach_filter(sock, level, optname, optval, optlen); if (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO) - return do_set_sock_timeout(fd, level, optname, optval, optlen); + return do_set_sock_timeout(sock, level, optname, optval, optlen); + + return sock_setsockopt(sock, level, optname, optval, optlen); +} + +asmlinkage long compat_sys_setsockopt(int fd, int level, int optname, + char __user *optval, int optlen) +{ + int err; + struct socket *sock; + + if (level == SOL_IPV6 && optname == IPT_SO_SET_REPLACE) + return do_netfilter_replace(fd, level, optname, + optval, optlen); + + if (optlen < 0) + return -EINVAL; + + if ((sock = sockfd_lookup(fd, &err))!=NULL) + { + err = security_socket_setsockopt(sock,level,optname); + if (err) { + sockfd_put(sock); + return err; + } - return sys_setsockopt(fd, level, optname, optval, optlen); + if (level == SOL_SOCKET) + err = compat_sock_setsockopt(sock, level, + optname, optval, optlen); + else if (sock->ops->compat_setsockopt) + err = sock->ops->compat_setsockopt(sock, level, + optname, optval, optlen); + else + err = sock->ops->setsockopt(sock, level, + optname, optval, optlen); + sockfd_put(sock); + } + return err; } -static int do_get_sock_timeout(int fd, int level, int optname, char *optval, - int *optlen) +static int do_get_sock_timeout(struct socket *sock, int level, int optname, + char __user *optval, int __user *optlen) { - struct compat_timeval *up = (struct compat_timeval *) optval; + struct compat_timeval __user *up; struct timeval ktime; mm_segment_t old_fs; int len, err; + up = (struct compat_timeval __user *) optval; if (get_user(len, optlen)) return -EFAULT; if (len < sizeof(*up)) @@ -484,7 +521,7 @@ static int do_get_sock_timeout(int fd, int level, int optname, char *optval, len = sizeof(ktime); old_fs = get_fs(); set_fs(KERNEL_DS); - err = sys_getsockopt(fd, level, optname, (char *) &ktime, &len); + err = sock_getsockopt(sock, level, optname, (char *) &ktime, &len); set_fs(old_fs); if (!err) { @@ -497,14 +534,61 @@ static int do_get_sock_timeout(int fd, int level, int optname, char *optval, return err; } -asmlinkage long compat_sys_getsockopt(int fd, int level, int optname, - char *optval, int *optlen) +static int compat_sock_getsockopt(struct socket *sock, int level, int optname, + char __user *optval, int __user *optlen) { if (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO) - return do_get_sock_timeout(fd, level, optname, optval, optlen); - return sys_getsockopt(fd, level, optname, optval, optlen); + return do_get_sock_timeout(sock, level, optname, optval, optlen); + return sock_getsockopt(sock, level, optname, optval, optlen); +} + +int compat_sock_get_timestamp(struct sock *sk, struct timeval __user *userstamp) +{ + struct compat_timeval __user *ctv = + (struct compat_timeval __user*) userstamp; + int err = -ENOENT; + + if (!sock_flag(sk, SOCK_TIMESTAMP)) + sock_enable_timestamp(sk); + if (sk->sk_stamp.tv_sec == -1) + return err; + if (sk->sk_stamp.tv_sec == 0) + do_gettimeofday(&sk->sk_stamp); + if (put_user(sk->sk_stamp.tv_sec, &ctv->tv_sec) || + put_user(sk->sk_stamp.tv_usec, &ctv->tv_usec)) + err = -EFAULT; + return err; } +EXPORT_SYMBOL(compat_sock_get_timestamp); +asmlinkage long compat_sys_getsockopt(int fd, int level, int optname, + char __user *optval, int __user *optlen) +{ + int err; + 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 (level == SOL_SOCKET) + err = compat_sock_getsockopt(sock, level, + optname, optval, optlen); + else if (sock->ops->compat_getsockopt) + err = sock->ops->compat_getsockopt(sock, level, + optname, optval, optlen); + else + err = sock->ops->getsockopt(sock, level, + optname, optval, optlen); + sockfd_put(sock); + } + return err; +} /* Argument list sizes for compat_sys_socketcall */ #define AL(x) ((x) * sizeof(u32)) static unsigned char nas[18]={AL(0),AL(3),AL(3),AL(3),AL(2),AL(3), @@ -512,17 +596,17 @@ static unsigned char nas[18]={AL(0),AL(3),AL(3),AL(3),AL(2),AL(3), AL(6),AL(2),AL(5),AL(5),AL(3),AL(3)}; #undef AL -asmlinkage long compat_sys_sendmsg(int fd, struct compat_msghdr *msg, unsigned flags) +asmlinkage long compat_sys_sendmsg(int fd, struct compat_msghdr __user *msg, unsigned flags) { - return sys_sendmsg(fd, (struct msghdr *)msg, flags | MSG_CMSG_COMPAT); + return sys_sendmsg(fd, (struct msghdr __user *)msg, flags | MSG_CMSG_COMPAT); } -asmlinkage long compat_sys_recvmsg(int fd, struct compat_msghdr *msg, unsigned int flags) +asmlinkage long compat_sys_recvmsg(int fd, struct compat_msghdr __user *msg, unsigned int flags) { - return sys_recvmsg(fd, (struct msghdr *)msg, flags | MSG_CMSG_COMPAT); + return sys_recvmsg(fd, (struct msghdr __user *)msg, flags | MSG_CMSG_COMPAT); } -asmlinkage long compat_sys_socketcall(int call, u32 *args) +asmlinkage long compat_sys_socketcall(int call, u32 __user *args) { int ret; u32 a[6];