Re: [Linux kernel ipv6_setsockopt integer overflow]
On Thu, 3 Feb 2005, qobaiashi wrote:
There's no integer overflow here since there's the test for optlen < 0 in
linux/net/socket.c
>
> there exists an integer bug in the ipv6 implementation of the linux kernel.
> (at least in 2.4.20 and 2.6.4 )
> in /linux/net/ipv6/ipv6_sockglue.c:
>
>
> int ipv6_setsockopt(struct sock *sk, int level, int optname, char *optval,
> int optlen)
> {
> struct ipv6_pinfo *np = inet6_sk(sk);
> int val, valbool;
> int retv = -ENOPROTOOPT;
>
> if (level == SOL_IP && sk->sk_type != SOCK_RAW)
> return udp_prot.setsockopt(sk, level, optname, optval,optlen);
>
> if(level!=SOL_IPV6)
> goto out;
>
> if (optval == NULL)
> val=0;
> else if (get_user(val, (int *) optval))
> return -EFAULT;
>
> valbool = (val!=0);
>
> lock_sock(sk);
>
> switch (optname) {
> [...]
>
> case IPV6_PKTOPTIONS:
> {
> struct ipv6_txoptions *opt = NULL;
> struct msghdr msg;
> struct flowi fl;
> int junk;
>
> fl.fl6_flowlabel = 0;
> fl.oif = sk->sk_bound_dev_if;
>
> [1] if (optlen == 0)
> goto update;
>
> /* 1K is probably excessive
> * 1K is surely not enough, 2K per standard header is 16K.
> */
> retv = -EINVAL;
> [2] if (optlen > 64*1024)
> break;
>
> [3] opt = sock_kmalloc(sk, sizeof(*opt) + optlen, GFP_KERNEL);
> retv = -ENOBUFS; sizeof(*opt)+0xfffffff8
> if (opt == NULL)
> break;
>
> [4] memset(opt, 0, sizeof(*opt));
> opt->tot_len = sizeof(*opt) + optlen;
> retv = -EFAULT;
> [5] if (copy_from_user(opt+1, optval, optlen))
> [...]
>
> details:
>
> condition [1] and [2] are easily passed for a value like -100, then at [3]
> sock_kmalloc allocates a too small object of the size (sizeof(*opt) + (-100))
> which is then overflowed in [4] and [5] leading to a dos of the kernel...
>
> that's it
> over and out!
>
>
--
Sincerely Your, Dan.