diff --git a/include/linux/socket.h b/include/linux/socket.h --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -90,6 +90,7 @@ struct cmsghdr { #define CMSG_ALIGN(len) ( ((len)+sizeof(long)-1) & ~(sizeof(long)-1) ) #define CMSG_DATA(cmsg) ((void *)((char *)(cmsg) + CMSG_ALIGN(sizeof(struct cmsghdr)))) +#define CMSG_DATA_USER(cmsg) ((void __user *)((char __user *)(cmsg) + CMSG_ALIGN(sizeof(struct cmsghdr)))) #define CMSG_SPACE(len) (CMSG_ALIGN(sizeof(struct cmsghdr)) + CMSG_ALIGN(len)) #define CMSG_LEN(len) (CMSG_ALIGN(sizeof(struct cmsghdr)) + (len)) @@ -297,7 +298,15 @@ extern int verify_iovec(struct msghdr *m extern int memcpy_toiovec(struct iovec *v, unsigned char *kdata, int len); extern int move_addr_to_user(void *kaddr, int klen, void __user *uaddr, int __user *ulen); extern int move_addr_to_kernel(void __user *uaddr, int ulen, void *kaddr); -extern int put_cmsg(struct msghdr*, int level, int type, int len, void *data); +extern int __put_cmsg(struct msghdr *msg, int level, int type, + int len, void *data, struct cmsghdr __user *cm); + +static inline int put_cmsg(struct msghdr *msg, int level, int type, + int len, void *data) +{ + return __put_cmsg(msg, level, type, len, data, + (struct cmsghdr __user *)msg->msg_control); +} #endif #endif /* not kernel and not glibc */ diff --git a/include/net/compat.h b/include/net/compat.h --- a/include/net/compat.h +++ b/include/net/compat.h @@ -32,7 +32,16 @@ extern int verify_compat_iovec(struct ms extern asmlinkage long compat_sys_sendmsg(int,struct compat_msghdr __user *,unsigned); extern asmlinkage long compat_sys_recvmsg(int,struct compat_msghdr __user *,unsigned); extern asmlinkage long compat_sys_getsockopt(int, int, int, char __user *, int __user *); -extern int put_cmsg_compat(struct msghdr*, int, int, int, void *); + +extern int __put_cmsg_compat(struct msghdr *kmsg, int level, int type, int len, + void *data, struct cmsghdr __user *kcm); +static inline int put_cmsg_compat(struct msghdr *kmsg, int level, int type, + int len, void *data) +{ + return __put_cmsg_compat(kmsg, level, type, len, data, + (struct cmsghdr __user *)kmsg->msg_control); +} + extern int cmsghdr_from_user_compat_to_kern(struct msghdr *, unsigned char *, int); diff --git a/include/net/transp_v6.h b/include/net/transp_v6.h --- a/include/net/transp_v6.h +++ b/include/net/transp_v6.h @@ -30,9 +30,16 @@ extern int udpv6_connect(struct sock struct sockaddr *uaddr, int addr_len); -extern int datagram_recv_ctl(struct sock *sk, - struct msghdr *msg, - struct sk_buff *skb); +extern int __datagram_recv_ctl(struct sock *sk, struct msghdr *msg, + struct cmsghdr __user *cmsg, + struct sk_buff *skb); +extern inline int datagram_recv_ctl(struct sock *sk, struct msghdr *msg, + struct sk_buff *skb) +{ + return __datagram_recv_ctl(sk, msg, + (struct cmsghdr __user *)msg->msg_control, + skb); +} extern int datagram_send_ctl(struct msghdr *msg, struct flowi *fl, diff --git a/net/compat.c b/net/compat.c --- a/net/compat.c +++ b/net/compat.c @@ -205,10 +205,11 @@ out_free_efault: return -EFAULT; } -int put_cmsg_compat(struct msghdr *kmsg, int level, int type, int len, void *data) +int __put_cmsg_compat(struct msghdr *kmsg, int level, int type, int len, + void *data, struct cmsghdr __user *kcm) { struct compat_timeval ctv; - struct compat_cmsghdr __user *cm = (struct compat_cmsghdr __user *) kmsg->msg_control; + struct compat_cmsghdr __user *cm = (struct compat_cmsghdr __user *)kcm; struct compat_cmsghdr cmhdr; int cmlen; diff --git a/net/core/scm.c b/net/core/scm.c --- a/net/core/scm.c +++ b/net/core/scm.c @@ -164,15 +164,15 @@ error: return err; } -int put_cmsg(struct msghdr * msg, int level, int type, int len, void *data) +int __put_cmsg(struct msghdr * msg, int level, int type, int len, void *data, + struct cmsghdr __user *cm) { - struct cmsghdr __user *cm = (struct cmsghdr __user *)msg->msg_control; struct cmsghdr cmhdr; int cmlen = CMSG_LEN(len); int err; if (MSG_CMSG_COMPAT & msg->msg_flags) - return put_cmsg_compat(msg, level, type, len, data); + return __put_cmsg_compat(msg, level, type, len, data, cm); if (cm==NULL || msg->msg_controllen < sizeof(*cm)) { msg->msg_flags |= MSG_CTRUNC; @@ -189,7 +189,7 @@ int put_cmsg(struct msghdr * msg, int le err = -EFAULT; if (copy_to_user(cm, &cmhdr, sizeof cmhdr)) goto out; - if (copy_to_user(CMSG_DATA(cm), data, cmlen - sizeof(struct cmsghdr))) + if (copy_to_user(CMSG_DATA_USER(cm), data, cmlen - sizeof(struct cmsghdr))) goto out; cmlen = CMSG_SPACE(len); msg->msg_control += cmlen; @@ -221,7 +221,7 @@ void scm_detach_fds(struct msghdr *msg, if (fdnum < fdmax) fdmax = fdnum; - for (i=0, cmfptr=(int __user *)CMSG_DATA(cm); isk_type != SOCK_STREAM) return -ENOPROTOOPT; - msg.msg_control = optval; msg.msg_controllen = len; msg.msg_flags = 0; @@ -1042,11 +1041,15 @@ int ip_getsockopt(struct sock *sk, int l info.ipi_addr.s_addr = inet->rcv_saddr; info.ipi_spec_dst.s_addr = inet->rcv_saddr; info.ipi_ifindex = inet->mc_index; - put_cmsg(&msg, SOL_IP, IP_PKTINFO, sizeof(info), &info); + __put_cmsg(&msg, SOL_IP, IP_PKTINFO, + sizeof(info), &info, + (struct cmsghdr __user *)optval); } if (inet->cmsg_flags & IP_CMSG_TTL) { int hlim = inet->mc_ttl; - put_cmsg(&msg, SOL_IP, IP_TTL, sizeof(hlim), &hlim); + __put_cmsg(&msg, SOL_IP, IP_TTL, + sizeof(hlim), &hlim, + (struct cmsghdr __user *)optval); } len -= msg.msg_controllen; return put_user(len, optlen); diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -370,9 +370,8 @@ out: return err; } - - -int datagram_recv_ctl(struct sock *sk, struct msghdr *msg, struct sk_buff *skb) +int __datagram_recv_ctl(struct sock *sk, struct msghdr *msg, + struct cmsghdr __user *cmsg, struct sk_buff *skb) { struct ipv6_pinfo *np = inet6_sk(sk); struct inet6_skb_parm *opt = IP6CB(skb); @@ -382,33 +381,33 @@ int datagram_recv_ctl(struct sock *sk, s src_info.ipi6_ifindex = opt->iif; ipv6_addr_copy(&src_info.ipi6_addr, &skb->nh.ipv6h->daddr); - put_cmsg(msg, SOL_IPV6, IPV6_PKTINFO, sizeof(src_info), &src_info); + __put_cmsg(msg, SOL_IPV6, IPV6_PKTINFO, sizeof(src_info), &src_info, cmsg); } if (np->rxopt.bits.rxhlim) { int hlim = skb->nh.ipv6h->hop_limit; - put_cmsg(msg, SOL_IPV6, IPV6_HOPLIMIT, sizeof(hlim), &hlim); + __put_cmsg(msg, SOL_IPV6, IPV6_HOPLIMIT, sizeof(hlim), &hlim, cmsg); } if (np->rxopt.bits.rxflow && (*(u32*)skb->nh.raw & IPV6_FLOWINFO_MASK)) { u32 flowinfo = *(u32*)skb->nh.raw & IPV6_FLOWINFO_MASK; - put_cmsg(msg, SOL_IPV6, IPV6_FLOWINFO, sizeof(flowinfo), &flowinfo); + __put_cmsg(msg, SOL_IPV6, IPV6_FLOWINFO, sizeof(flowinfo), &flowinfo, cmsg); } if (np->rxopt.bits.hopopts && opt->hop) { u8 *ptr = skb->nh.raw + opt->hop; - put_cmsg(msg, SOL_IPV6, IPV6_HOPOPTS, (ptr[1]+1)<<3, ptr); + __put_cmsg(msg, SOL_IPV6, IPV6_HOPOPTS, (ptr[1]+1)<<3, ptr, cmsg); } if (np->rxopt.bits.dstopts && opt->dst0) { u8 *ptr = skb->nh.raw + opt->dst0; - put_cmsg(msg, SOL_IPV6, IPV6_DSTOPTS, (ptr[1]+1)<<3, ptr); + __put_cmsg(msg, SOL_IPV6, IPV6_DSTOPTS, (ptr[1]+1)<<3, ptr, cmsg); } if (np->rxopt.bits.srcrt && opt->srcrt) { struct ipv6_rt_hdr *rthdr = (struct ipv6_rt_hdr *)(skb->nh.raw + opt->srcrt); - put_cmsg(msg, SOL_IPV6, IPV6_RTHDR, (rthdr->hdrlen+1) << 3, rthdr); + __put_cmsg(msg, SOL_IPV6, IPV6_RTHDR, (rthdr->hdrlen+1) << 3, rthdr, cmsg); } if (np->rxopt.bits.dstopts && opt->dst1) { u8 *ptr = skb->nh.raw + opt->dst1; - put_cmsg(msg, SOL_IPV6, IPV6_DSTOPTS, (ptr[1]+1)<<3, ptr); + __put_cmsg(msg, SOL_IPV6, IPV6_DSTOPTS, (ptr[1]+1)<<3, ptr, cmsg); } return 0; } diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c @@ -571,11 +571,11 @@ int ipv6_getsockopt(struct sock *sk, int { struct msghdr msg; struct sk_buff *skb; + struct cmsghdr __user *cmsg; if (sk->sk_type != SOCK_STREAM) return -ENOPROTOOPT; - msg.msg_control = optval; msg.msg_controllen = len; msg.msg_flags = 0; @@ -585,8 +585,10 @@ int ipv6_getsockopt(struct sock *sk, int atomic_inc(&skb->users); release_sock(sk); + cmsg = (struct cmsghdr __user *)optval; + if (skb) { - int err = datagram_recv_ctl(sk, &msg, skb); + int err = __datagram_recv_ctl(sk, &msg, cmsg, skb); kfree_skb(skb); if (err) return err; @@ -595,11 +597,13 @@ int ipv6_getsockopt(struct sock *sk, int struct in6_pktinfo src_info; src_info.ipi6_ifindex = np->mcast_oif; ipv6_addr_copy(&src_info.ipi6_addr, &np->daddr); - put_cmsg(&msg, SOL_IPV6, IPV6_PKTINFO, sizeof(src_info), &src_info); + __put_cmsg(&msg, SOL_IPV6, IPV6_PKTINFO, + sizeof(src_info), &src_info, cmsg); } if (np->rxopt.bits.rxhlim) { int hlim = np->mcast_hops; - put_cmsg(&msg, SOL_IPV6, IPV6_HOPLIMIT, sizeof(hlim), &hlim); + __put_cmsg(&msg, SOL_IPV6, IPV6_HOPLIMIT, + sizeof(hlim), &hlim, cmsg); } } len -= msg.msg_controllen; diff --git a/net/ipv6/netfilter.c b/net/ipv6/netfilter.c --- a/net/ipv6/netfilter.c +++ b/net/ipv6/netfilter.c @@ -1,12 +1,12 @@ #include #include +#include #ifdef CONFIG_NETFILTER #include #include #include -#include #include #include #include