Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5fe467ee authored by Ivan Skytte Jørgensen's avatar Ivan Skytte Jørgensen Committed by David S. Miller
Browse files

[SCTP] Fix sctp_get{pl}addrs() API to work with 32-bit apps on 64-bit kernels.



The old socket options are marked with a _OLD suffix so that the
existing 32-bit apps on 32-bit kernels do not break.

Signed-off-by: default avatarIvan Skytte Jrgensen <isj-sctp@i1.dk>
Signed-off-by: default avatarSridhar Samudrala <sri@us.ibm.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent edb4a353
Loading
Loading
Loading
Loading
+16 −7
Original line number Original line Diff line number Diff line
@@ -103,16 +103,20 @@ enum sctp_optname {
#define SCTP_SOCKOPT_BINDX_REM	SCTP_SOCKOPT_BINDX_REM
#define SCTP_SOCKOPT_BINDX_REM	SCTP_SOCKOPT_BINDX_REM
	SCTP_SOCKOPT_PEELOFF, 	/* peel off association. */
	SCTP_SOCKOPT_PEELOFF, 	/* peel off association. */
#define SCTP_SOCKOPT_PEELOFF	SCTP_SOCKOPT_PEELOFF
#define SCTP_SOCKOPT_PEELOFF	SCTP_SOCKOPT_PEELOFF
	SCTP_GET_PEER_ADDRS_NUM, 	/* Get number of peer addresss. */
	SCTP_GET_PEER_ADDRS_NUM_OLD, 	/* Get number of peer addresss. */
#define SCTP_GET_PEER_ADDRS_NUM	SCTP_GET_PEER_ADDRS_NUM
#define SCTP_GET_PEER_ADDRS_NUM_OLD	SCTP_GET_PEER_ADDRS_NUM_OLD
	SCTP_GET_PEER_ADDRS_OLD, 	/* Get all peer addresss. */
#define SCTP_GET_PEER_ADDRS_OLD	SCTP_GET_PEER_ADDRS_OLD
	SCTP_GET_LOCAL_ADDRS_NUM_OLD, 	/* Get number of local addresss. */
#define SCTP_GET_LOCAL_ADDRS_NUM_OLD	SCTP_GET_LOCAL_ADDRS_NUM_OLD
	SCTP_GET_LOCAL_ADDRS_OLD, 	/* Get all local addresss. */
#define SCTP_GET_LOCAL_ADDRS_OLD	SCTP_GET_LOCAL_ADDRS_OLD
	SCTP_SOCKOPT_CONNECTX, /* CONNECTX requests. */
#define SCTP_SOCKOPT_CONNECTX	SCTP_SOCKOPT_CONNECTX
	SCTP_GET_PEER_ADDRS, 	/* Get all peer addresss. */
	SCTP_GET_PEER_ADDRS, 	/* Get all peer addresss. */
#define SCTP_GET_PEER_ADDRS	SCTP_GET_PEER_ADDRS
#define SCTP_GET_PEER_ADDRS	SCTP_GET_PEER_ADDRS
	SCTP_GET_LOCAL_ADDRS_NUM, 	/* Get number of local addresss. */
#define SCTP_GET_LOCAL_ADDRS_NUM	SCTP_GET_LOCAL_ADDRS_NUM
	SCTP_GET_LOCAL_ADDRS, 	/* Get all local addresss. */
	SCTP_GET_LOCAL_ADDRS, 	/* Get all local addresss. */
#define SCTP_GET_LOCAL_ADDRS	SCTP_GET_LOCAL_ADDRS
#define SCTP_GET_LOCAL_ADDRS	SCTP_GET_LOCAL_ADDRS
	SCTP_SOCKOPT_CONNECTX, /* CONNECTX requests. */
#define SCTP_SOCKOPT_CONNECTX	SCTP_SOCKOPT_CONNECTX
};
};


/*
/*
@@ -559,11 +563,16 @@ struct sctp_status {
 * SCTP_GET_LOCAL_ADDRS socket options used internally to implement
 * SCTP_GET_LOCAL_ADDRS socket options used internally to implement
 * sctp_getpaddrs() and sctp_getladdrs() API. 
 * sctp_getpaddrs() and sctp_getladdrs() API. 
 */
 */
struct sctp_getaddrs {
struct sctp_getaddrs_old {
	sctp_assoc_t            assoc_id;
	sctp_assoc_t            assoc_id;
	int			addr_num;
	int			addr_num;
	struct sockaddr		__user *addrs;
	struct sockaddr		__user *addrs;
};
};
struct sctp_getaddrs {
	sctp_assoc_t		assoc_id; /*input*/
	__u32			addr_num; /*output*/
	__u8			addrs[0]; /*output, variable size*/
};


/* These are bit fields for msghdr->msg_flags.  See section 5.1.  */
/* These are bit fields for msghdr->msg_flags.  See section 5.1.  */
/* On user space Linux, these live in <bits/socket.h> as an enum.  */
/* On user space Linux, these live in <bits/socket.h> as an enum.  */
+227 −25
Original line number Original line Diff line number Diff line
@@ -3159,8 +3159,9 @@ static int sctp_getsockopt_initmsg(struct sock *sk, int len, char __user *optval
	return 0;
	return 0;
}
}


static int sctp_getsockopt_peer_addrs_num(struct sock *sk, int len,
static int sctp_getsockopt_peer_addrs_num_old(struct sock *sk, int len,
					  char __user *optval, int __user *optlen)
					      char __user *optval,
					      int __user *optlen)
{
{
	sctp_assoc_t id;
	sctp_assoc_t id;
	struct sctp_association *asoc;
	struct sctp_association *asoc;
@@ -3185,23 +3186,28 @@ static int sctp_getsockopt_peer_addrs_num(struct sock *sk, int len,
	return cnt;
	return cnt;
}
}


static int sctp_getsockopt_peer_addrs(struct sock *sk, int len,
/* 
				      char __user *optval, int __user *optlen)
 * Old API for getting list of peer addresses. Does not work for 32-bit
 * programs running on a 64-bit kernel
 */
static int sctp_getsockopt_peer_addrs_old(struct sock *sk, int len,
					  char __user *optval,
					  int __user *optlen)
{
{
	struct sctp_association *asoc;
	struct sctp_association *asoc;
	struct list_head *pos;
	struct list_head *pos;
	int cnt = 0;
	int cnt = 0;
	struct sctp_getaddrs getaddrs;
	struct sctp_getaddrs_old getaddrs;
	struct sctp_transport *from;
	struct sctp_transport *from;
	void __user *to;
	void __user *to;
	union sctp_addr temp;
	union sctp_addr temp;
	struct sctp_sock *sp = sctp_sk(sk);
	struct sctp_sock *sp = sctp_sk(sk);
	int addrlen;
	int addrlen;


	if (len != sizeof(struct sctp_getaddrs))
	if (len != sizeof(struct sctp_getaddrs_old))
		return -EINVAL;
		return -EINVAL;


	if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs)))
	if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs_old)))
		return -EFAULT;
		return -EFAULT;


	if (getaddrs.addr_num <= 0) return -EINVAL;
	if (getaddrs.addr_num <= 0) return -EINVAL;
@@ -3225,13 +3231,67 @@ static int sctp_getsockopt_peer_addrs(struct sock *sk, int len,
		if (cnt >= getaddrs.addr_num) break;
		if (cnt >= getaddrs.addr_num) break;
	}
	}
	getaddrs.addr_num = cnt;
	getaddrs.addr_num = cnt;
	if (copy_to_user(optval, &getaddrs, sizeof(struct sctp_getaddrs)))
	if (copy_to_user(optval, &getaddrs, sizeof(struct sctp_getaddrs_old)))
		return -EFAULT;
		return -EFAULT;


	return 0;
	return 0;
}
}


static int sctp_getsockopt_local_addrs_num(struct sock *sk, int len,
static int sctp_getsockopt_peer_addrs(struct sock *sk, int len,
				      char __user *optval, int __user *optlen)
{
	struct sctp_association *asoc;
	struct list_head *pos;
	int cnt = 0;
	struct sctp_getaddrs getaddrs;
	struct sctp_transport *from;
	void __user *to;
	union sctp_addr temp;
	struct sctp_sock *sp = sctp_sk(sk);
	int addrlen;
	size_t space_left;
	int bytes_copied;

	if (len < sizeof(struct sctp_getaddrs))
		return -EINVAL;

	if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs)))
		return -EFAULT;

	/* For UDP-style sockets, id specifies the association to query.  */
	asoc = sctp_id2assoc(sk, getaddrs.assoc_id);
	if (!asoc)
		return -EINVAL;

	to = optval + offsetof(struct sctp_getaddrs,addrs);
	space_left = len - sizeof(struct sctp_getaddrs) - 
			offsetof(struct sctp_getaddrs,addrs);

	list_for_each(pos, &asoc->peer.transport_addr_list) {
		from = list_entry(pos, struct sctp_transport, transports);
		memcpy(&temp, &from->ipaddr, sizeof(temp));
		sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp);
		addrlen = sctp_get_af_specific(sk->sk_family)->sockaddr_len;
		if(space_left < addrlen)
			return -ENOMEM;
		temp.v4.sin_port = htons(temp.v4.sin_port);
		if (copy_to_user(to, &temp, addrlen))
			return -EFAULT;
		to += addrlen;
		cnt++;
		space_left -= addrlen;
	}

	if (put_user(cnt, &((struct sctp_getaddrs __user *)optval)->addr_num))
		return -EFAULT;
	bytes_copied = ((char __user *)to) - optval;
	if (put_user(bytes_copied, optlen))
		return -EFAULT;

	return 0;
}

static int sctp_getsockopt_local_addrs_num_old(struct sock *sk, int len,
					       char __user *optval,
					       char __user *optval,
					       int __user *optlen)
					       int __user *optlen)
{
{
@@ -3306,7 +3366,7 @@ static int sctp_getsockopt_local_addrs_num(struct sock *sk, int len,
/* Helper function that copies local addresses to user and returns the number
/* Helper function that copies local addresses to user and returns the number
 * of addresses copied.
 * of addresses copied.
 */
 */
static int sctp_copy_laddrs_to_user(struct sock *sk, __u16 port, int max_addrs,
static int sctp_copy_laddrs_to_user_old(struct sock *sk, __u16 port, int max_addrs,
					void __user *to)
					void __user *to)
{
{
	struct list_head *pos;
	struct list_head *pos;
@@ -3341,14 +3401,54 @@ static int sctp_copy_laddrs_to_user(struct sock *sk, __u16 port, int max_addrs,
	return cnt;
	return cnt;
}
}


static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
static int sctp_copy_laddrs_to_user(struct sock *sk, __u16 port,
				    void * __user *to, size_t space_left)
{
	struct list_head *pos;
	struct sctp_sockaddr_entry *addr;
	unsigned long flags;
	union sctp_addr temp;
	int cnt = 0;
	int addrlen;

	sctp_spin_lock_irqsave(&sctp_local_addr_lock, flags);
	list_for_each(pos, &sctp_local_addr_list) {
		addr = list_entry(pos, struct sctp_sockaddr_entry, list);
		if ((PF_INET == sk->sk_family) && 
		    (AF_INET6 == addr->a.sa.sa_family))
			continue;
		memcpy(&temp, &addr->a, sizeof(temp));
		sctp_get_pf_specific(sk->sk_family)->addr_v4map(sctp_sk(sk),
								&temp);
		addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len;
		if(space_left<addrlen)
			return -ENOMEM;
		temp.v4.sin_port = htons(port);
		if (copy_to_user(*to, &temp, addrlen)) {
			sctp_spin_unlock_irqrestore(&sctp_local_addr_lock,
						    flags);
			return -EFAULT;
		}
		*to += addrlen;
		cnt ++;
		space_left -= addrlen;
	}
	sctp_spin_unlock_irqrestore(&sctp_local_addr_lock, flags);

	return cnt;
}

/* Old API for getting list of local addresses. Does not work for 32-bit
 * programs running on a 64-bit kernel
 */
static int sctp_getsockopt_local_addrs_old(struct sock *sk, int len,
					   char __user *optval, int __user *optlen)
					   char __user *optval, int __user *optlen)
{
{
	struct sctp_bind_addr *bp;
	struct sctp_bind_addr *bp;
	struct sctp_association *asoc;
	struct sctp_association *asoc;
	struct list_head *pos;
	struct list_head *pos;
	int cnt = 0;
	int cnt = 0;
	struct sctp_getaddrs getaddrs;
	struct sctp_getaddrs_old getaddrs;
	struct sctp_sockaddr_entry *addr;
	struct sctp_sockaddr_entry *addr;
	void __user *to;
	void __user *to;
	union sctp_addr temp;
	union sctp_addr temp;
@@ -3357,10 +3457,10 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
	rwlock_t *addr_lock;
	rwlock_t *addr_lock;
	int err = 0;
	int err = 0;


	if (len != sizeof(struct sctp_getaddrs))
	if (len != sizeof(struct sctp_getaddrs_old))
		return -EINVAL;
		return -EINVAL;


	if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs)))
	if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs_old)))
		return -EFAULT;
		return -EFAULT;


	if (getaddrs.addr_num <= 0) return -EINVAL;
	if (getaddrs.addr_num <= 0) return -EINVAL;
@@ -3392,8 +3492,9 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
		addr = list_entry(bp->address_list.next,
		addr = list_entry(bp->address_list.next,
				  struct sctp_sockaddr_entry, list);
				  struct sctp_sockaddr_entry, list);
		if (sctp_is_any(&addr->a)) {
		if (sctp_is_any(&addr->a)) {
			cnt = sctp_copy_laddrs_to_user(sk, bp->port,
			cnt = sctp_copy_laddrs_to_user_old(sk, bp->port,
						       getaddrs.addr_num, to);
							   getaddrs.addr_num,
							   to);
			if (cnt < 0) {
			if (cnt < 0) {
				err = cnt;
				err = cnt;
				goto unlock;
				goto unlock;
@@ -3419,7 +3520,7 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len,


copy_getaddrs:
copy_getaddrs:
	getaddrs.addr_num = cnt;
	getaddrs.addr_num = cnt;
	if (copy_to_user(optval, &getaddrs, sizeof(struct sctp_getaddrs)))
	if (copy_to_user(optval, &getaddrs, sizeof(struct sctp_getaddrs_old)))
		err = -EFAULT;
		err = -EFAULT;


unlock:
unlock:
@@ -3427,6 +3528,99 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
	return err;
	return err;
}
}


static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
				       char __user *optval, int __user *optlen)
{
	struct sctp_bind_addr *bp;
	struct sctp_association *asoc;
	struct list_head *pos;
	int cnt = 0;
	struct sctp_getaddrs getaddrs;
	struct sctp_sockaddr_entry *addr;
	void __user *to;
	union sctp_addr temp;
	struct sctp_sock *sp = sctp_sk(sk);
	int addrlen;
	rwlock_t *addr_lock;
	int err = 0;
	size_t space_left;
	int bytes_copied;

	if (len <= sizeof(struct sctp_getaddrs))
		return -EINVAL;

	if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs)))
		return -EFAULT;

	/*
	 *  For UDP-style sockets, id specifies the association to query.
	 *  If the id field is set to the value '0' then the locally bound
	 *  addresses are returned without regard to any particular
	 *  association.
	 */
	if (0 == getaddrs.assoc_id) {
		bp = &sctp_sk(sk)->ep->base.bind_addr;
		addr_lock = &sctp_sk(sk)->ep->base.addr_lock;
	} else {
		asoc = sctp_id2assoc(sk, getaddrs.assoc_id);
		if (!asoc)
			return -EINVAL;
		bp = &asoc->base.bind_addr;
		addr_lock = &asoc->base.addr_lock;
	}

	to = optval + offsetof(struct sctp_getaddrs,addrs);
	space_left = len - sizeof(struct sctp_getaddrs) -
			 offsetof(struct sctp_getaddrs,addrs);

	sctp_read_lock(addr_lock);

	/* If the endpoint is bound to 0.0.0.0 or ::0, get the valid
	 * addresses from the global local address list.
	 */
	if (sctp_list_single_entry(&bp->address_list)) {
		addr = list_entry(bp->address_list.next,
				  struct sctp_sockaddr_entry, list);
		if (sctp_is_any(&addr->a)) {
			cnt = sctp_copy_laddrs_to_user(sk, bp->port,
						       &to, space_left);
			if (cnt < 0) {
				err = cnt;
				goto unlock;
			}
			goto copy_getaddrs;		
		}
	}

	list_for_each(pos, &bp->address_list) {
		addr = list_entry(pos, struct sctp_sockaddr_entry, list);
		memcpy(&temp, &addr->a, sizeof(temp));
		sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp);
		addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len;
		if(space_left < addrlen)
			return -ENOMEM; /*fixme: right error?*/
		temp.v4.sin_port = htons(temp.v4.sin_port);
		if (copy_to_user(to, &temp, addrlen)) {
			err = -EFAULT;
			goto unlock;
		}
		to += addrlen;
		cnt ++;
		space_left -= addrlen;
	}

copy_getaddrs:
	if (put_user(cnt, &((struct sctp_getaddrs __user *)optval)->addr_num))
		return -EFAULT;
	bytes_copied = ((char __user *)to) - optval;
	if (put_user(bytes_copied, optlen))
		return -EFAULT;

unlock:
	sctp_read_unlock(addr_lock);
	return err;
}

/* 7.1.10 Set Primary Address (SCTP_PRIMARY_ADDR)
/* 7.1.10 Set Primary Address (SCTP_PRIMARY_ADDR)
 *
 *
 * Requests that the local SCTP stack use the enclosed peer address as
 * Requests that the local SCTP stack use the enclosed peer address as
@@ -3807,12 +4001,20 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
	case SCTP_INITMSG:
	case SCTP_INITMSG:
		retval = sctp_getsockopt_initmsg(sk, len, optval, optlen);
		retval = sctp_getsockopt_initmsg(sk, len, optval, optlen);
		break;
		break;
	case SCTP_GET_PEER_ADDRS_NUM:
	case SCTP_GET_PEER_ADDRS_NUM_OLD:
		retval = sctp_getsockopt_peer_addrs_num(sk, len, optval,
		retval = sctp_getsockopt_peer_addrs_num_old(sk, len, optval,
							    optlen);
		break;
	case SCTP_GET_LOCAL_ADDRS_NUM_OLD:
		retval = sctp_getsockopt_local_addrs_num_old(sk, len, optval,
							     optlen);
		break;
	case SCTP_GET_PEER_ADDRS_OLD:
		retval = sctp_getsockopt_peer_addrs_old(sk, len, optval,
							optlen);
							optlen);
		break;
		break;
	case SCTP_GET_LOCAL_ADDRS_NUM:
	case SCTP_GET_LOCAL_ADDRS_OLD:
		retval = sctp_getsockopt_local_addrs_num(sk, len, optval,
		retval = sctp_getsockopt_local_addrs_old(sk, len, optval,
							 optlen);
							 optlen);
		break;
		break;
	case SCTP_GET_PEER_ADDRS:
	case SCTP_GET_PEER_ADDRS: