diff mbox series

[01/32] netlink: Avoid memcpy() across flexible array boundary

Message ID 20220504014440.3697851-2-keescook@chromium.org (mailing list archive)
State Handled Elsewhere
Headers show
Series Introduce flexible array struct memcpy() helpers | expand

Checks

Context Check Description
tedd_an/pre-ci_am success Success
tedd_an/checkpatch fail [01/32] netlink: Avoid memcpy() across flexible array boundary\WARNING:COMMIT_LOG_LONG_LINE: Possible unwrapped commit description (prefer a maximum 75 chars per line) #172: memcpy: detected field-spanning write (size 32) of single field "&errmsg->msg" (size 16) WARNING:BAD_SIGN_OFF: Non-standard signature: Fixed-by: #179: Fixed-by: Rasmus Villemoes <linux@rasmusvillemoes.dk> total: 0 errors, 2 warnings, 0 checks, 18 lines checked NOTE: For some of the reported defects, checkpatch may be able to mechanically convert to the typical style using --fix or --fix-inplace. /github/workspace/src/12836836.patch has style problems, please review. NOTE: Ignored message types: UNKNOWN_COMMIT_ID NOTE: If any of the errors are false positives, please report them to the maintainer, see CHECKPATCH in MAINTAINERS.
tedd_an/gitlint fail [01/32] netlink: Avoid memcpy() across flexible array boundary 7: B1 Line exceeds max length (88>80): "memcpy: detected field-spanning write (size 32) of single field "&errmsg->msg" (size 16)" 15: B1 Line exceeds max length (90>80): "Link: https://lore.kernel.org/lkml/d7251d92-150b-5346-6237-52afc154bb00@rasmusvillemoes.dk"
tedd_an/subjectprefix fail "Bluetooth: " is not specified in the subject
tedd_an/buildkernel success Build Kernel PASS
tedd_an/buildkernel32 success Build Kernel32 PASS
tedd_an/incremental_build success Pass
tedd_an/testrunnersetup success Test Runner Setup PASS
tedd_an/testrunnerl2cap-tester success Total: 40, Passed: 40 (100.0%), Failed: 0, Not Run: 0
tedd_an/testrunnerbnep-tester success Total: 1, Passed: 1 (100.0%), Failed: 0, Not Run: 0
tedd_an/testrunnermgmt-tester success Total: 493, Passed: 493 (100.0%), Failed: 0, Not Run: 0
tedd_an/testrunnerrfcomm-tester success Total: 10, Passed: 10 (100.0%), Failed: 0, Not Run: 0
tedd_an/testrunnersco-tester success Total: 12, Passed: 12 (100.0%), Failed: 0, Not Run: 0
tedd_an/testrunnersmp-tester success Total: 8, Passed: 8 (100.0%), Failed: 0, Not Run: 0
tedd_an/testrunneruserchan-tester success Total: 4, Passed: 4 (100.0%), Failed: 0, Not Run: 0

Commit Message

Kees Cook May 4, 2022, 1:44 a.m. UTC
In preparation for run-time memcpy() bounds checking, split the nlmsg
copying for error messages (which crosses a previous unspecified flexible
array boundary) in half. Avoids the future run-time warning:

memcpy: detected field-spanning write (size 32) of single field "&errmsg->msg" (size 16)

Creates an explicit flexible array at the end of nlmsghdr for the payload,
named "nlmsg_payload". There is no impact on UAPI; the sizeof(struct
nlmsghdr) does not change, but now the compiler can better reason about
where things are being copied.

Fixed-by: Rasmus Villemoes <linux@rasmusvillemoes.dk>
Link: https://lore.kernel.org/lkml/d7251d92-150b-5346-6237-52afc154bb00@rasmusvillemoes.dk
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Jakub Kicinski <kuba@kernel.org>
Cc: Rich Felker <dalias@aerifal.cx>
Cc: Eric Dumazet <edumazet@google.com>
Cc: netdev@vger.kernel.org
Signed-off-by: Kees Cook <keescook@chromium.org>
---
 include/uapi/linux/netlink.h | 1 +
 net/netlink/af_netlink.c     | 5 ++++-
 2 files changed, 5 insertions(+), 1 deletion(-)

Comments

bluez.test.bot@gmail.com May 4, 2022, 3:12 a.m. UTC | #1
This is automated email and please do not reply to this email!

Dear submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=638153

---Test result---

Test Summary:
CheckPatch                    FAIL      15.45 seconds
GitLint                       FAIL      7.70 seconds
SubjectPrefix                 FAIL      28.23 seconds
BuildKernel                   PASS      36.05 seconds
BuildKernel32                 PASS      34.02 seconds
Incremental Build with patchesPASS      324.75 seconds
TestRunner: Setup             PASS      541.23 seconds
TestRunner: l2cap-tester      PASS      19.53 seconds
TestRunner: bnep-tester       PASS      7.22 seconds
TestRunner: mgmt-tester       PASS      110.30 seconds
TestRunner: rfcomm-tester     PASS      10.98 seconds
TestRunner: sco-tester        PASS      10.26 seconds
TestRunner: smp-tester        PASS      10.29 seconds
TestRunner: userchan-tester   PASS      7.09 seconds

Details
##############################
Test: CheckPatch - FAIL - 15.45 seconds
Run checkpatch.pl script with rule in .checkpatch.conf
[01/32] netlink: Avoid memcpy() across flexible array boundary\WARNING:COMMIT_LOG_LONG_LINE: Possible unwrapped commit description (prefer a maximum 75 chars per line)
#172: 
memcpy: detected field-spanning write (size 32) of single field "&errmsg->msg" (size 16)

WARNING:BAD_SIGN_OFF: Non-standard signature: Fixed-by:
#179: 
Fixed-by: Rasmus Villemoes <linux@rasmusvillemoes.dk>

total: 0 errors, 2 warnings, 0 checks, 18 lines checked

NOTE: For some of the reported defects, checkpatch may be able to
      mechanically convert to the typical style using --fix or --fix-inplace.

/github/workspace/src/12836836.patch has style problems, please review.

NOTE: Ignored message types: UNKNOWN_COMMIT_ID

NOTE: If any of the errors are false positives, please report
      them to the maintainer, see CHECKPATCH in MAINTAINERS.

[02/32] Introduce flexible array struct memcpy() helpers\Traceback (most recent call last):
  File "scripts/spdxcheck.py", line 6, in <module>
    from ply import lex, yacc
ModuleNotFoundError: No module named 'ply'
WARNING:FILE_PATH_CHANGES: added, moved or deleted file(s), does MAINTAINERS need updating?
#300: 
new file mode 100644

ERROR:SPACING: need consistent spacing around '*' (ctx:WxV)
#523: FILE: include/linux/flex_array.h:219:
+	typeof(*(src)) *__fc_src = (src);				\
 	               ^

ERROR:SPACING: need consistent spacing around '*' (ctx:WxV)
#576: FILE: include/linux/flex_array.h:272:
+	typeof(**(alloc)) *__fd_alloc;					\
 	                  ^

ERROR:SPACING: need consistent spacing around '*' (ctx:WxV)
#754: FILE: include/linux/flex_array.h:450:
+	typeof((*__mtfd_alloc)dot_fas_member) *__mtfd_fas;		\
 	                                      ^

ERROR:SPACING: need consistent spacing around '*' (ctx:WxV)
#861: FILE: include/linux/flex_array.h:557:
+	typeof(*(bytes_written)) *__ftm_written = (bytes_written);	\
 	                         ^

ERROR:SPACING: need consistent spacing around '*' (ctx:WxV)
#910: FILE: include/linux/flex_array.h:606:
+	typeof(*(alloc_size)) *__ftmd_alloc_size = (alloc_size);	\
 	                      ^

total: 5 errors, 1 warnings, 662 lines checked

NOTE: For some of the reported defects, checkpatch may be able to
      mechanically convert to the typical style using --fix or --fix-inplace.

/github/workspace/src/12836837.patch has style problems, please review.

NOTE: Ignored message types: UNKNOWN_COMMIT_ID

NOTE: If any of the errors are false positives, please report
      them to the maintainer, see CHECKPATCH in MAINTAINERS.

[03/32] flex_array: Add Kunit tests\Traceback (most recent call last):
  File "scripts/spdxcheck.py", line 6, in <module>
    from ply import lex, yacc
ModuleNotFoundError: No module named 'ply'
WARNING:CONFIG_DESCRIPTION: please write a paragraph that describes the config symbol fully
#205: FILE: lib/Kconfig.debug:2565:
+config FLEX_ARRAY_KUNIT_TEST

WARNING:FILE_PATH_CHANGES: added, moved or deleted file(s), does MAINTAINERS need updating?
#228: 
new file mode 100644

total: 0 errors, 2 warnings, 554 lines checked

NOTE: For some of the reported defects, checkpatch may be able to
      mechanically convert to the typical style using --fix or --fix-inplace.

/github/workspace/src/12836839.patch has style problems, please review.

NOTE: Ignored message types: UNKNOWN_COMMIT_ID

NOTE: If any of the errors are false positives, please report
      them to the maintainer, see CHECKPATCH in MAINTAINERS.

[04/32] fortify: Add run-time WARN for cross-field memcpy()\WARNING:COMMIT_LOG_LONG_LINE: Possible unwrapped commit description (prefer a maximum 75 chars per line)
#178: 
  memcpy: detected field-spanning write (size N) of single field "var->dest" (size M)

total: 0 errors, 1 warnings, 96 lines checked

NOTE: For some of the reported defects, checkpatch may be able to
      mechanically convert to the typical style using --fix or --fix-inplace.

/github/workspace/src/12836838.patch has style problems, please review.

NOTE: Ignored message types: UNKNOWN_COMMIT_ID

NOTE: If any of the errors are false positives, please report
      them to the maintainer, see CHECKPATCH in MAINTAINERS.

[07/32] iwlwifi: calib: Use mem_to_flex_dup() with struct iwl_calib_result\WARNING:COMMIT_LOG_LONG_LINE: Possible unwrapped commit description (prefer a maximum 75 chars per line)
#176: 
memcpy: detected field-spanning write (size 8) of single field "&res->hdr" (size 4)

total: 0 errors, 1 warnings, 0 checks, 29 lines checked

NOTE: For some of the reported defects, checkpatch may be able to
      mechanically convert to the typical style using --fix or --fix-inplace.

/github/workspace/src/12836846.patch has style problems, please review.

NOTE: Ignored message types: UNKNOWN_COMMIT_ID

NOTE: If any of the errors are false positives, please report
      them to the maintainer, see CHECKPATCH in MAINTAINERS.


##############################
Test: GitLint - FAIL - 7.70 seconds
Run gitlint with rule in .gitlint
[01/32] netlink: Avoid memcpy() across flexible array boundary
7: B1 Line exceeds max length (88>80): "memcpy: detected field-spanning write (size 32) of single field "&errmsg->msg" (size 16)"
15: B1 Line exceeds max length (90>80): "Link: https://lore.kernel.org/lkml/d7251d92-150b-5346-6237-52afc154bb00@rasmusvillemoes.dk"

[02/32] Introduce flexible array struct memcpy() helpers
39: B3 Line contains hard tab characters (\t): "        ...		/* arbitrary members */"
40: B3 Line contains hard tab characters (\t): "        u16 part_count;	/* count of elements stored in "parts" below. */"
41: B3 Line contains hard tab characters (\t): "        ...		/* arbitrary members */"
42: B3 Line contains hard tab characters (\t): "        u32 parts[];	/* flexible array with elements of type u32. */"
49: B3 Line contains hard tab characters (\t): "        ...		/* arbitrary members */"
57: B3 Line contains hard tab characters (\t): "        ...		/* arbitrary members */"
58: B3 Line contains hard tab characters (\t): "        u16 part_count;	/* count of elements stored in "parts" below. */"
59: B3 Line contains hard tab characters (\t): "        ...		/* arbitrary members */"
61: B3 Line contains hard tab characters (\t): "            ...		/* other blob members */"

[04/32] fortify: Add run-time WARN for cross-field memcpy()
14: B1 Line exceeds max length (85>80): "  memcpy: detected field-spanning write (size N) of single field "var->dest" (size M)"
38: B3 Line contains hard tab characters (\t): "	void *a;"
39: B3 Line contains hard tab characters (\t): "	int b;"
40: B3 Line contains hard tab characters (\t): "	size_t array_size;"
41: B3 Line contains hard tab characters (\t): "	u32 c;"
42: B3 Line contains hard tab characters (\t): "	u8 flex_array[];"
60: B3 Line contains hard tab characters (\t): "	int foo;"
61: B3 Line contains hard tab characters (\t): "	char bar;"
62: B3 Line contains hard tab characters (\t): "	struct normal_flex_array embedded;"

[07/32] iwlwifi: calib: Use mem_to_flex_dup() with struct iwl_calib_result
11: B1 Line exceeds max length (83>80): "memcpy: detected field-spanning write (size 8) of single field "&res->hdr" (size 4)"


##############################
Test: SubjectPrefix - FAIL - 28.23 seconds
Check subject contains "Bluetooth" prefix
"Bluetooth: " is not specified in the subject
"Bluetooth: " is not specified in the subject
"Bluetooth: " is not specified in the subject
"Bluetooth: " is not specified in the subject
"Bluetooth: " is not specified in the subject
"Bluetooth: " is not specified in the subject
"Bluetooth: " is not specified in the subject
"Bluetooth: " is not specified in the subject
"Bluetooth: " is not specified in the subject
"Bluetooth: " is not specified in the subject
"Bluetooth: " is not specified in the subject
"Bluetooth: " is not specified in the subject
"Bluetooth: " is not specified in the subject
"Bluetooth: " is not specified in the subject
"Bluetooth: " is not specified in the subject
"Bluetooth: " is not specified in the subject
"Bluetooth: " is not specified in the subject
"Bluetooth: " is not specified in the subject
"Bluetooth: " is not specified in the subject
"Bluetooth: " is not specified in the subject
"Bluetooth: " is not specified in the subject
"Bluetooth: " is not specified in the subject
"Bluetooth: " is not specified in the subject
"Bluetooth: " is not specified in the subject
"Bluetooth: " is not specified in the subject
"Bluetooth: " is not specified in the subject
"Bluetooth: " is not specified in the subject
"Bluetooth: " is not specified in the subject
"Bluetooth: " is not specified in the subject
"Bluetooth: " is not specified in the subject
"Bluetooth: " is not specified in the subject



---
Regards,
Linux Bluetooth
Gustavo A. R. Silva May 4, 2022, 3:31 a.m. UTC | #2
On Tue, May 03, 2022 at 06:44:10PM -0700, Kees Cook wrote:
> In preparation for run-time memcpy() bounds checking, split the nlmsg
> copying for error messages (which crosses a previous unspecified flexible
> array boundary) in half. Avoids the future run-time warning:
> 
> memcpy: detected field-spanning write (size 32) of single field "&errmsg->msg" (size 16)
> 
> Creates an explicit flexible array at the end of nlmsghdr for the payload,
> named "nlmsg_payload". There is no impact on UAPI; the sizeof(struct
> nlmsghdr) does not change, but now the compiler can better reason about
> where things are being copied.
> 
> Fixed-by: Rasmus Villemoes <linux@rasmusvillemoes.dk>
> Link: https://lore.kernel.org/lkml/d7251d92-150b-5346-6237-52afc154bb00@rasmusvillemoes.dk
> Cc: "David S. Miller" <davem@davemloft.net>
> Cc: Jakub Kicinski <kuba@kernel.org>
> Cc: Rich Felker <dalias@aerifal.cx>
> Cc: Eric Dumazet <edumazet@google.com>
> Cc: netdev@vger.kernel.org
> Signed-off-by: Kees Cook <keescook@chromium.org>
> ---
>  include/uapi/linux/netlink.h | 1 +
>  net/netlink/af_netlink.c     | 5 ++++-
>  2 files changed, 5 insertions(+), 1 deletion(-)
> 
> diff --git a/include/uapi/linux/netlink.h b/include/uapi/linux/netlink.h
> index 855dffb4c1c3..47f9342d51bc 100644
> --- a/include/uapi/linux/netlink.h
> +++ b/include/uapi/linux/netlink.h
> @@ -47,6 +47,7 @@ struct nlmsghdr {
>  	__u16		nlmsg_flags;	/* Additional flags */
>  	__u32		nlmsg_seq;	/* Sequence number */
>  	__u32		nlmsg_pid;	/* Sending process port ID */
> +	__u8		nlmsg_payload[];/* Contents of message */
>  };
>  
>  /* Flags values */
> diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
> index 1b5a9c2e1c29..09346aee1022 100644
> --- a/net/netlink/af_netlink.c
> +++ b/net/netlink/af_netlink.c
> @@ -2445,7 +2445,10 @@ void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err,
>  			  NLMSG_ERROR, payload, flags);
>  	errmsg = nlmsg_data(rep);
>  	errmsg->error = err;
> -	memcpy(&errmsg->msg, nlh, payload > sizeof(*errmsg) ? nlh->nlmsg_len : sizeof(*nlh));
> +	errmsg->msg = *nlh;
> +	if (payload > sizeof(*errmsg))
> +		memcpy(errmsg->msg.nlmsg_payload, nlh->nlmsg_payload,
> +		       nlh->nlmsg_len - sizeof(*nlh));

They have nlmsg_len()[1] for the length of the payload without the header:

/**
 * nlmsg_len - length of message payload
 * @nlh: netlink message header
 */
static inline int nlmsg_len(const struct nlmsghdr *nlh)
{
	return nlh->nlmsg_len - NLMSG_HDRLEN;
}

(would that function use some sanitization, though? what if nlmsg_len is
somehow manipulated to be less than NLMSG_HDRLEN?...)

Also, it seems there is at least one more instance of this same issue:

diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c
index 16ae92054baa..d06184b94af5 100644
--- a/net/netfilter/ipset/ip_set_core.c
+++ b/net/netfilter/ipset/ip_set_core.c
@@ -1723,7 +1723,8 @@ call_ad(struct net *net, struct sock *ctnl, struct sk_buff *skb,
                                  nlh->nlmsg_seq, NLMSG_ERROR, payload, 0);
                errmsg = nlmsg_data(rep);
                errmsg->error = ret;
-               memcpy(&errmsg->msg, nlh, nlh->nlmsg_len);
+               errmsg->msg = *nlh;
+               memcpy(errmsg->msg.nlmsg_payload, nlh->nlmsg_payload, nlmsg_len(nlh));
                cmdattr = (void *)&errmsg->msg + min_len;

                ret = nla_parse(cda, IPSET_ATTR_CMD_MAX, cmdattr,

--
Gustavo

[1] https://elixir.bootlin.com/linux/v5.18-rc5/source/include/net/netlink.h#L577
Kees Cook May 4, 2022, 3:37 a.m. UTC | #3
On Tue, May 03, 2022 at 10:31:05PM -0500, Gustavo A. R. Silva wrote:
> On Tue, May 03, 2022 at 06:44:10PM -0700, Kees Cook wrote:
> [...]
> > diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
> > index 1b5a9c2e1c29..09346aee1022 100644
> > --- a/net/netlink/af_netlink.c
> > +++ b/net/netlink/af_netlink.c
> > @@ -2445,7 +2445,10 @@ void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err,
> >  			  NLMSG_ERROR, payload, flags);
> >  	errmsg = nlmsg_data(rep);
> >  	errmsg->error = err;
> > -	memcpy(&errmsg->msg, nlh, payload > sizeof(*errmsg) ? nlh->nlmsg_len : sizeof(*nlh));
> > +	errmsg->msg = *nlh;
> > +	if (payload > sizeof(*errmsg))
> > +		memcpy(errmsg->msg.nlmsg_payload, nlh->nlmsg_payload,
> > +		       nlh->nlmsg_len - sizeof(*nlh));
> 
> They have nlmsg_len()[1] for the length of the payload without the header:
> 
> /**
>  * nlmsg_len - length of message payload
>  * @nlh: netlink message header
>  */
> static inline int nlmsg_len(const struct nlmsghdr *nlh)
> {
> 	return nlh->nlmsg_len - NLMSG_HDRLEN;
> }

Oh, hm, yeah, that would be much cleaner. The relationship between
"payload" and nlmsg_len is confusing in here. :)

So, this should be simpler:

-	memcpy(&errmsg->msg, nlh, payload > sizeof(*errmsg) ? nlh->nlmsg_len : sizeof(*nlh));
+	errmsg->msg = *nlh;
+	memcpy(errmsg->msg.nlmsg_payload, nlh->nlmsg_payload, nlmsg_len(nlh));

It's actually this case that triggered my investigation in __bos(1)'s
misbehavior around sub-structs, since this case wasn't getting silenced:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101832

It still feels like it should be possible to get this right without
splitting the memcpy, though. Hmpf.

> (would that function use some sanitization, though? what if nlmsg_len is
> somehow manipulated to be less than NLMSG_HDRLEN?...)

Maybe something like:

static inline int nlmsg_len(const struct nlmsghdr *nlh)
{
	if (WARN_ON(nlh->nlmsg_len < NLMSG_HDRLEN))
		return 0;
	return nlh->nlmsg_len - NLMSG_HDRLEN;
}

> Also, it seems there is at least one more instance of this same issue:
> 
> diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c
> index 16ae92054baa..d06184b94af5 100644
> --- a/net/netfilter/ipset/ip_set_core.c
> +++ b/net/netfilter/ipset/ip_set_core.c
> @@ -1723,7 +1723,8 @@ call_ad(struct net *net, struct sock *ctnl, struct sk_buff *skb,
>                                   nlh->nlmsg_seq, NLMSG_ERROR, payload, 0);
>                 errmsg = nlmsg_data(rep);
>                 errmsg->error = ret;
> -               memcpy(&errmsg->msg, nlh, nlh->nlmsg_len);
> +               errmsg->msg = *nlh;
> +               memcpy(errmsg->msg.nlmsg_payload, nlh->nlmsg_payload, nlmsg_len(nlh));

Ah, yes, nice catch!

>                 cmdattr = (void *)&errmsg->msg + min_len;
> 
>                 ret = nla_parse(cda, IPSET_ATTR_CMD_MAX, cmdattr,
> 
> --
> Gustavo
> 
> [1] https://elixir.bootlin.com/linux/v5.18-rc5/source/include/net/netlink.h#L577
diff mbox series

Patch

diff --git a/include/uapi/linux/netlink.h b/include/uapi/linux/netlink.h
index 855dffb4c1c3..47f9342d51bc 100644
--- a/include/uapi/linux/netlink.h
+++ b/include/uapi/linux/netlink.h
@@ -47,6 +47,7 @@  struct nlmsghdr {
 	__u16		nlmsg_flags;	/* Additional flags */
 	__u32		nlmsg_seq;	/* Sequence number */
 	__u32		nlmsg_pid;	/* Sending process port ID */
+	__u8		nlmsg_payload[];/* Contents of message */
 };
 
 /* Flags values */
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index 1b5a9c2e1c29..09346aee1022 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -2445,7 +2445,10 @@  void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int err,
 			  NLMSG_ERROR, payload, flags);
 	errmsg = nlmsg_data(rep);
 	errmsg->error = err;
-	memcpy(&errmsg->msg, nlh, payload > sizeof(*errmsg) ? nlh->nlmsg_len : sizeof(*nlh));
+	errmsg->msg = *nlh;
+	if (payload > sizeof(*errmsg))
+		memcpy(errmsg->msg.nlmsg_payload, nlh->nlmsg_payload,
+		       nlh->nlmsg_len - sizeof(*nlh));
 
 	if (nlk_has_extack && extack) {
 		if (extack->_msg) {