Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: Kees Cook <keescook@chromium.org>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Jakub Kicinski and committed by
David S. Miller
738136a0 eca485d2

+43 -9
+21
include/net/netlink.h
··· 932 932 } 933 933 934 934 /** 935 + * nlmsg_append - Add more data to a nlmsg in a skb 936 + * @skb: socket buffer to store message in 937 + * @size: length of message payload 938 + * 939 + * Append data to an existing nlmsg, used when constructing a message 940 + * with multiple fixed-format headers (which is rare). 941 + * Returns NULL if the tailroom of the skb is insufficient to store 942 + * the extra payload. 943 + */ 944 + static inline void *nlmsg_append(struct sk_buff *skb, u32 size) 945 + { 946 + if (unlikely(skb_tailroom(skb) < NLMSG_ALIGN(size))) 947 + return NULL; 948 + 949 + if (NLMSG_ALIGN(size) - size) 950 + memset(skb_tail_pointer(skb) + size, 0, 951 + NLMSG_ALIGN(size) - size); 952 + return __skb_put(skb, NLMSG_ALIGN(size)); 953 + } 954 + 955 + /** 935 956 * nlmsg_put_answer - Add a new callback based netlink message to an skb 936 957 * @skb: socket buffer to store message in 937 958 * @cb: netlink callback
+2
include/uapi/linux/netlink.h
··· 48 48 * @nlmsg_flags: Additional flags 49 49 * @nlmsg_seq: Sequence number 50 50 * @nlmsg_pid: Sending process port ID 51 + * @nlmsg_data: Message payload 51 52 */ 52 53 struct nlmsghdr { 53 54 __u32 nlmsg_len; ··· 56 55 __u16 nlmsg_flags; 57 56 __u32 nlmsg_seq; 58 57 __u32 nlmsg_pid; 58 + __u8 nlmsg_data[]; 59 59 }; 60 60 61 61 /* Flags values */
+20 -9
net/netlink/af_netlink.c
··· 2499 2499 flags |= NLM_F_ACK_TLVS; 2500 2500 2501 2501 skb = nlmsg_new(payload + tlvlen, GFP_KERNEL); 2502 - if (!skb) { 2503 - NETLINK_CB(in_skb).sk->sk_err = ENOBUFS; 2504 - sk_error_report(NETLINK_CB(in_skb).sk); 2505 - return; 2506 - } 2502 + if (!skb) 2503 + goto err_bad_put; 2507 2504 2508 2505 rep = nlmsg_put(skb, NETLINK_CB(in_skb).portid, nlh->nlmsg_seq, 2509 - NLMSG_ERROR, payload, flags); 2506 + NLMSG_ERROR, sizeof(*errmsg), flags); 2507 + if (!rep) 2508 + goto err_bad_put; 2510 2509 errmsg = nlmsg_data(rep); 2511 2510 errmsg->error = err; 2512 - unsafe_memcpy(&errmsg->msg, nlh, payload > sizeof(*errmsg) 2513 - ? nlh->nlmsg_len : sizeof(*nlh), 2514 - /* Bounds checked by the skb layer. */); 2511 + errmsg->msg = *nlh; 2512 + 2513 + if (!(flags & NLM_F_CAPPED)) { 2514 + if (!nlmsg_append(skb, nlmsg_len(nlh))) 2515 + goto err_bad_put; 2516 + 2517 + memcpy(errmsg->msg.nlmsg_data, nlh->nlmsg_data, 2518 + nlmsg_len(nlh)); 2519 + } 2515 2520 2516 2521 if (tlvlen) 2517 2522 netlink_ack_tlv_fill(in_skb, skb, nlh, err, extack); ··· 2524 2519 nlmsg_end(skb, rep); 2525 2520 2526 2521 nlmsg_unicast(in_skb->sk, skb, NETLINK_CB(in_skb).portid); 2522 + 2523 + return; 2524 + 2525 + err_bad_put: 2526 + NETLINK_CB(in_skb).sk->sk_err = ENOBUFS; 2527 + sk_error_report(NETLINK_CB(in_skb).sk); 2527 2528 } 2528 2529 EXPORT_SYMBOL(netlink_ack); 2529 2530