Commit 738136a0 authored by Jakub Kicinski's avatar Jakub Kicinski Committed by David S. Miller
Browse files

netlink: split up copies in the ack construction



Clean up the use of unsafe_memcpy() by adding a flexible array
at the end of netlink message header and splitting up the header
and data copies.

Reviewed-by: default avatarKees Cook <keescook@chromium.org>
Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent eca485d2
Loading
Loading
Loading
Loading
+21 −0
Original line number Diff line number Diff line
@@ -931,6 +931,27 @@ static inline struct nlmsghdr *nlmsg_put(struct sk_buff *skb, u32 portid, u32 se
	return __nlmsg_put(skb, portid, seq, type, payload, flags);
}

/**
 * nlmsg_append - Add more data to a nlmsg in a skb
 * @skb: socket buffer to store message in
 * @size: length of message payload
 *
 * Append data to an existing nlmsg, used when constructing a message
 * with multiple fixed-format headers (which is rare).
 * Returns NULL if the tailroom of the skb is insufficient to store
 * the extra payload.
 */
static inline void *nlmsg_append(struct sk_buff *skb, u32 size)
{
	if (unlikely(skb_tailroom(skb) < NLMSG_ALIGN(size)))
		return NULL;

	if (NLMSG_ALIGN(size) - size)
		memset(skb_tail_pointer(skb) + size, 0,
		       NLMSG_ALIGN(size) - size);
	return __skb_put(skb, NLMSG_ALIGN(size));
}

/**
 * nlmsg_put_answer - Add a new callback based netlink message to an skb
 * @skb: socket buffer to store message in
+2 −0
Original line number Diff line number Diff line
@@ -48,6 +48,7 @@ struct sockaddr_nl {
 * @nlmsg_flags: Additional flags
 * @nlmsg_seq:   Sequence number
 * @nlmsg_pid:   Sending process port ID
 * @nlmsg_data:  Message payload
 */
struct nlmsghdr {
	__u32		nlmsg_len;
@@ -55,6 +56,7 @@ struct nlmsghdr {
	__u16		nlmsg_flags;
	__u32		nlmsg_seq;
	__u32		nlmsg_pid;
	__u8		nlmsg_data[];
};

/* Flags values */
+20 −9
Original line number Diff line number Diff line
@@ -2499,19 +2499,24 @@ void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err,
		flags |= NLM_F_ACK_TLVS;

	skb = nlmsg_new(payload + tlvlen, GFP_KERNEL);
	if (!skb) {
		NETLINK_CB(in_skb).sk->sk_err = ENOBUFS;
		sk_error_report(NETLINK_CB(in_skb).sk);
		return;
	}
	if (!skb)
		goto err_bad_put;

	rep = nlmsg_put(skb, NETLINK_CB(in_skb).portid, nlh->nlmsg_seq,
			NLMSG_ERROR, payload, flags);
			NLMSG_ERROR, sizeof(*errmsg), flags);
	if (!rep)
		goto err_bad_put;
	errmsg = nlmsg_data(rep);
	errmsg->error = err;
	unsafe_memcpy(&errmsg->msg, nlh, payload > sizeof(*errmsg)
					 ? nlh->nlmsg_len : sizeof(*nlh),
		      /* Bounds checked by the skb layer. */);
	errmsg->msg = *nlh;

	if (!(flags & NLM_F_CAPPED)) {
		if (!nlmsg_append(skb, nlmsg_len(nlh)))
			goto err_bad_put;

		memcpy(errmsg->msg.nlmsg_data, nlh->nlmsg_data,
		       nlmsg_len(nlh));
	}

	if (tlvlen)
		netlink_ack_tlv_fill(in_skb, skb, nlh, err, extack);
@@ -2519,6 +2524,12 @@ void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err,
	nlmsg_end(skb, rep);

	nlmsg_unicast(in_skb->sk, skb, NETLINK_CB(in_skb).portid);

	return;

err_bad_put:
	NETLINK_CB(in_skb).sk->sk_err = ENOBUFS;
	sk_error_report(NETLINK_CB(in_skb).sk);
}
EXPORT_SYMBOL(netlink_ack);