diff mbox series

[v4] net: 9p: avoid freeing uninit memory in p9pdu_vreadf

Message ID 20231206200913.16135-1-pchelkin@ispras.ru (mailing list archive)
State Not Applicable
Delegated to: Netdev Maintainers
Headers show
Series [v4] net: 9p: avoid freeing uninit memory in p9pdu_vreadf | expand

Checks

Context Check Description
netdev/series_format warning Single patches do not need cover letters; Target tree name not specified in the subject
netdev/tree_selection success Guessed tree name to be net-next
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 1115 this patch: 1115
netdev/cc_maintainers success CCed 9 of 9 maintainers
netdev/build_clang success Errors and warnings before: 1142 this patch: 1142
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success Fixes tag looks correct
netdev/build_allmodconfig_warn success Errors and warnings before: 1142 this patch: 1142
netdev/checkpatch warning WARNING: Too many leading tabs - consider code refactoring
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Fedor Pchelkin Dec. 6, 2023, 8:09 p.m. UTC
If some of p9pdu_readf() calls inside case 'T' in p9pdu_vreadf() fails,
the error path is not handled properly. *wnames or members of *wnames
array may be left uninitialized and invalidly freed.

Initialize *wnames to NULL in beginning of case 'T'. Initialize the first
*wnames array element to NULL and nullify the failing *wnames element so
that the error path freeing loop stops on the first NULL element and
doesn't proceed further.

Found by Linux Verification Center (linuxtesting.org).

Fixes: ace51c4dd2f9 ("9p: add new protocol support code")
Signed-off-by: Fedor Pchelkin <pchelkin@ispras.ru>
---
v2: I've missed that *wnames can also be left uninitialized. Please
ignore the patch v1. As an answer to Dominique's comment: my
organization marks this statement in all commits.
v3: Simplify the patch by using kcalloc() instead of array indices
manipulation per Christian Schoenebeck's remark. Update the commit
message accordingly.
v4: Per Christian's suggestion, apply another strategy: mark failing
array element as NULL and move in the freeing loop until it is found.
Update the commit message accordingly. If v4 is more appropriate than the
version at
https://github.com/martinetd/linux/commit/69cc23eb3a0b79538e9b5face200c4cd5cd32ae0
then please use it, otherwise, I don't think we can provide more
convenient solution here than the one already queued at github.

 net/9p/protocol.c | 17 +++++++++++++----
 1 file changed, 13 insertions(+), 4 deletions(-)

Comments

Christian Schoenebeck Dec. 7, 2023, 12:54 p.m. UTC | #1
On Wednesday, December 6, 2023 9:09:13 PM CET Fedor Pchelkin wrote:
> If some of p9pdu_readf() calls inside case 'T' in p9pdu_vreadf() fails,
> the error path is not handled properly. *wnames or members of *wnames
> array may be left uninitialized and invalidly freed.
> 
> Initialize *wnames to NULL in beginning of case 'T'. Initialize the first
> *wnames array element to NULL and nullify the failing *wnames element so
> that the error path freeing loop stops on the first NULL element and
> doesn't proceed further.
> 
> Found by Linux Verification Center (linuxtesting.org).
> 
> Fixes: ace51c4dd2f9 ("9p: add new protocol support code")
> Signed-off-by: Fedor Pchelkin <pchelkin@ispras.ru>
> ---
> v2: I've missed that *wnames can also be left uninitialized. Please
> ignore the patch v1. As an answer to Dominique's comment: my
> organization marks this statement in all commits.
> v3: Simplify the patch by using kcalloc() instead of array indices
> manipulation per Christian Schoenebeck's remark. Update the commit
> message accordingly.
> v4: Per Christian's suggestion, apply another strategy: mark failing
> array element as NULL and move in the freeing loop until it is found.
> Update the commit message accordingly. If v4 is more appropriate than the
> version at
> https://github.com/martinetd/linux/commit/69cc23eb3a0b79538e9b5face200c4cd5cd32ae0
> then please use it, otherwise, I don't think we can provide more
> convenient solution here than the one already queued at github.
> 
>  net/9p/protocol.c | 17 +++++++++++++----
>  1 file changed, 13 insertions(+), 4 deletions(-)
> 
> diff --git a/net/9p/protocol.c b/net/9p/protocol.c
> index 4e3a2a1ffcb3..0e6603b1ec90 100644
> --- a/net/9p/protocol.c
> +++ b/net/9p/protocol.c
> @@ -394,6 +394,8 @@ p9pdu_vreadf(struct p9_fcall *pdu, int proto_version, const char *fmt,
>  				uint16_t *nwname = va_arg(ap, uint16_t *);
>  				char ***wnames = va_arg(ap, char ***);
>  
> +				*wnames = NULL;
> +
>  				errcode = p9pdu_readf(pdu, proto_version,
>  								"w", nwname);
>  				if (!errcode) {
> @@ -403,6 +405,8 @@ p9pdu_vreadf(struct p9_fcall *pdu, int proto_version, const char *fmt,
>  							  GFP_NOFS);
>  					if (!*wnames)
>  						errcode = -ENOMEM;
> +					else
> +						(*wnames)[0] = NULL;
>  				}
>  
>  				if (!errcode) {
> @@ -414,8 +418,10 @@ p9pdu_vreadf(struct p9_fcall *pdu, int proto_version, const char *fmt,
>  								proto_version,
>  								"s",
>  								&(*wnames)[i]);
> -						if (errcode)
> +						if (errcode) {
> +							(*wnames)[i] = NULL;
>  							break;
> +						}

I just checked whether this could create a leak, but it looks clean, so LGTM:

Reviewed-by: Christian Schoenebeck <linux_oss@crudebyte.com>

Dominique, I would tend to use this v4 instead of v2. What do you think?

>  					}
>  				}
>  
> @@ -423,11 +429,14 @@ p9pdu_vreadf(struct p9_fcall *pdu, int proto_version, const char *fmt,
>  					if (*wnames) {
>  						int i;
>  
> -						for (i = 0; i < *nwname; i++)
> +						for (i = 0; i < *nwname; i++) {
> +							if (!(*wnames)[i])
> +								break;
>  							kfree((*wnames)[i]);
> +						}
> +						kfree(*wnames);
> +						*wnames = NULL;
>  					}
> -					kfree(*wnames);
> -					*wnames = NULL;
>  				}
>  			}
>  			break;
>
Simon Horman Dec. 11, 2023, 1:51 p.m. UTC | #2
On Wed, Dec 06, 2023 at 11:09:13PM +0300, Fedor Pchelkin wrote:
> If some of p9pdu_readf() calls inside case 'T' in p9pdu_vreadf() fails,
> the error path is not handled properly. *wnames or members of *wnames
> array may be left uninitialized and invalidly freed.
> 
> Initialize *wnames to NULL in beginning of case 'T'. Initialize the first
> *wnames array element to NULL and nullify the failing *wnames element so
> that the error path freeing loop stops on the first NULL element and
> doesn't proceed further.
> 
> Found by Linux Verification Center (linuxtesting.org).
> 
> Fixes: ace51c4dd2f9 ("9p: add new protocol support code")
> Signed-off-by: Fedor Pchelkin <pchelkin@ispras.ru>

Reviewed-by: Simon Horman <horms@kernel.org>
Dominique Martinet Dec. 11, 2023, 11:21 p.m. UTC | #3
Christian Schoenebeck wrote on Thu, Dec 07, 2023 at 01:54:02PM +0100:
> I just checked whether this could create a leak, but it looks clean, so LGTM:

Right, either version look good to me.
I don't have a hard preference here, I've finished testing and just
updated the patch -- thanks for your comments & review
(and thanks Simon as well!)
Vitaly Chikunov Jan. 7, 2024, 7:56 a.m. UTC | #4
Dominique,

On Tue, Dec 12, 2023 at 08:21:30AM +0900, Dominique Martinet wrote:
> Christian Schoenebeck wrote on Thu, Dec 07, 2023 at 01:54:02PM +0100:
> > I just checked whether this could create a leak, but it looks clean, so LGTM:
> 
> Right, either version look good to me.

Also, there was unnoticed bug in v2[1] - `int i` is moved to outer block
and `i` counld be used uninitialized inside of `if (errcode) {`.

Thanks,

[1] https://lore.kernel.org/all/20231205091952.24754-1-pchelkin@ispras.ru/

> I don't have a hard preference here, I've finished testing and just
> updated the patch -- thanks for your comments & review
> (and thanks Simon as well!)
> 
> -- 
> Dominique Martinet | Asmadeus
Fedor Pchelkin Jan. 7, 2024, 9:48 a.m. UTC | #5
On 24/01/07 10:56AM, Vitaly Chikunov wrote:
> Dominique,
> 
> On Tue, Dec 12, 2023 at 08:21:30AM +0900, Dominique Martinet wrote:
> > Christian Schoenebeck wrote on Thu, Dec 07, 2023 at 01:54:02PM +0100:
> > > I just checked whether this could create a leak, but it looks clean, so LGTM:
> > 
> > Right, either version look good to me.
> 
> Also, there was unnoticed bug in v2[1] - `int i` is moved to outer block
> and `i` counld be used uninitialized inside of `if (errcode) {`.

Could you elaborate, please? As I can see, `i` could be used
uninitialized in `if (errcode) {` only when `*wnames` is not NULL. But
when `*wnames` is not NULL, then `i` is initialized in the `for` loop. It
is a bit tricky and not obvious from the first glance (and not the best
decision after all), so with Christian's advice the patch was rewritten
to v4 which was eventually accepted.

The bug you've noticed exists in v1 of the patch, not v2.

> Thanks,
> 
> [1] https://lore.kernel.org/all/20231205091952.24754-1-pchelkin@ispras.ru/
Vitaly Chikunov Jan. 7, 2024, 10:14 a.m. UTC | #6
Fedor,

On Sun, Jan 07, 2024 at 12:48:11PM +0300, Fedor Pchelkin wrote:
> On 24/01/07 10:56AM, Vitaly Chikunov wrote:
> > 
> > On Tue, Dec 12, 2023 at 08:21:30AM +0900, Dominique Martinet wrote:
> > > Christian Schoenebeck wrote on Thu, Dec 07, 2023 at 01:54:02PM +0100:
> > > > I just checked whether this could create a leak, but it looks clean, so LGTM:
> > > 
> > > Right, either version look good to me.
> > 
> > Also, there was unnoticed bug in v2[1] - `int i` is moved to outer block
> > and `i` counld be used uninitialized inside of `if (errcode) {`.
> 
> Could you elaborate, please? As I can see, `i` could be used
> uninitialized in `if (errcode) {` only when `*wnames` is not NULL. But
> when `*wnames` is not NULL, then `i` is initialized in the `for` loop. It
> is a bit tricky and not obvious from the first glance (and not the best
> decision after all), so with Christian's advice the patch was rewritten
> to v4 which was eventually accepted.
> 
> The bug you've noticed exists in v1 of the patch, not v2.

You are right, it only affects v1. I didn't notice that important difference
in v2. My excuses!

Thanks,

> 
> > Thanks,
> > 
> > [1] https://lore.kernel.org/all/20231205091952.24754-1-pchelkin@ispras.ru/
Dominique Martinet Jan. 7, 2024, 10:26 a.m. UTC | #7
Vitaly Chikunov wrote on Sun, Jan 07, 2024 at 10:56:23AM +0300:
> On Tue, Dec 12, 2023 at 08:21:30AM +0900, Dominique Martinet wrote:
> > Christian Schoenebeck wrote on Thu, Dec 07, 2023 at 01:54:02PM +0100:
> > > I just checked whether this could create a leak, but it looks clean, so LGTM:
> > 
> > Right, either version look good to me.
> 
> Also, there was unnoticed bug in v2[1] - `int i` is moved to outer block
> and `i` counld be used uninitialized inside of `if (errcode) {`.

Thanks for pointing it out; I'm not sure if it's the same thing
Christian noticed but I believe what I had applied with his suggestion
of initializing i worked either way... But this is moot since I updated
to v4 afterwards:
this v4 has been merged in 6.7-rc7 as follow:
https://git.kernel.org/linus/ff49bf1867578f23a5ffdd38f927f6e1e16796c4

(the message-id also points to the v4 correctly, e.g.
https://lkml.kernel.org/r/20231206200913.16135-1-pchelkin@ispras.ru )


Thanks,
diff mbox series

Patch

diff --git a/net/9p/protocol.c b/net/9p/protocol.c
index 4e3a2a1ffcb3..0e6603b1ec90 100644
--- a/net/9p/protocol.c
+++ b/net/9p/protocol.c
@@ -394,6 +394,8 @@  p9pdu_vreadf(struct p9_fcall *pdu, int proto_version, const char *fmt,
 				uint16_t *nwname = va_arg(ap, uint16_t *);
 				char ***wnames = va_arg(ap, char ***);
 
+				*wnames = NULL;
+
 				errcode = p9pdu_readf(pdu, proto_version,
 								"w", nwname);
 				if (!errcode) {
@@ -403,6 +405,8 @@  p9pdu_vreadf(struct p9_fcall *pdu, int proto_version, const char *fmt,
 							  GFP_NOFS);
 					if (!*wnames)
 						errcode = -ENOMEM;
+					else
+						(*wnames)[0] = NULL;
 				}
 
 				if (!errcode) {
@@ -414,8 +418,10 @@  p9pdu_vreadf(struct p9_fcall *pdu, int proto_version, const char *fmt,
 								proto_version,
 								"s",
 								&(*wnames)[i]);
-						if (errcode)
+						if (errcode) {
+							(*wnames)[i] = NULL;
 							break;
+						}
 					}
 				}
 
@@ -423,11 +429,14 @@  p9pdu_vreadf(struct p9_fcall *pdu, int proto_version, const char *fmt,
 					if (*wnames) {
 						int i;
 
-						for (i = 0; i < *nwname; i++)
+						for (i = 0; i < *nwname; i++) {
+							if (!(*wnames)[i])
+								break;
 							kfree((*wnames)[i]);
+						}
+						kfree(*wnames);
+						*wnames = NULL;
 					}
-					kfree(*wnames);
-					*wnames = NULL;
 				}
 			}
 			break;