All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] media firewire firedtv-avc fix a buffer overflow in avc_ca_pmt()
@ 2021-06-07  7:39 Yang Yanchao
  2021-06-07  9:26 ` Greg KH
  2021-06-07 14:28 ` Dan Carpenter
  0 siblings, 2 replies; 10+ messages in thread
From: Yang Yanchao @ 2021-06-07  7:39 UTC (permalink / raw)
  To: dan.carpenter
  Cc: linux-distros, linux-media, linux1394-devel, mchehab, security

For CVE-2021-3542:

1、read_pos will be added four times in the patch, 
so use "read_pos + 4 < length" and write_pos as well

2. The last four bits of c->operand are used for CRC, 
so "sizeof (C - > operand) - 4" is used

3. "read_pos+=2" is added after the end of read_pos, so add value (read_pos >= length)

4. In order to avoid memcpy crossing the boundary, es_ info_ length > length - read_ pos

5. When the date_length is a specific input of a construction,it will cause memcpy
 to exceed the boundary, "(MSG - > MSG [3] & 0x7F) + date_ length) > (sizeof(msg->msg) - 4)"

Signed-off-by: yangyanchao <yangyanchao6@huawei.com>
---
 drivers/media/firewire/firedtv-avc.c | 14 +++++++++++---
 drivers/media/firewire/firedtv-ci.c  |  2 ++
 2 files changed, 13 insertions(+), 3 deletions(-)
diff --git a/drivers/media/firewire/firedtv-avc.c b/drivers/media/firewire/firedtv-avc.c
index 3ef5df164..8c31cf90c 100644
--- a/drivers/media/firewire/firedtv-avc.c
+++ b/drivers/media/firewire/firedtv-avc.c
@@ -1169,7 +1169,11 @@ int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length)
 		read_pos += program_info_length;
 		write_pos += program_info_length;
 	}
-	while (read_pos < length) {
+	while (read_pos + 4 < length) {
+		if (write_pos + 4 >= sizeof(c->operand) - 4) {
+			ret = -EINVAL;
+			goto out;
+		}
 		c->operand[write_pos++] = msg[read_pos++];
 		c->operand[write_pos++] = msg[read_pos++];
 		c->operand[write_pos++] = msg[read_pos++];
@@ -1181,13 +1185,17 @@ int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length)
 		c->operand[write_pos++] = es_info_length >> 8;
 		c->operand[write_pos++] = es_info_length & 0xff;
 		if (es_info_length > 0) {
+			if (read_pos >= length) {
+				ret = -EINVAL;
+				goto out;
+			}
 			pmt_cmd_id = msg[read_pos++];
 			if (pmt_cmd_id != 1 && pmt_cmd_id != 4)
 				dev_err(fdtv->device, "invalid pmt_cmd_id %d at stream level\n",
 					pmt_cmd_id);
 
-			if (es_info_length > sizeof(c->operand) - 4 -
-					     write_pos) {
+			if (es_info_length > sizeof(c->operand) - 4 - write_pos ||
+			    es_info_length > length - read_pos) {
 				ret = -EINVAL;
 				goto out;
 			}
diff --git a/drivers/media/firewire/firedtv-ci.c b/drivers/media/firewire/firedtv-ci.c
index 8dc5a7495..0e7ffa156 100644
--- a/drivers/media/firewire/firedtv-ci.c
+++ b/drivers/media/firewire/firedtv-ci.c
@@ -135,6 +135,8 @@ static int fdtv_ca_pmt(struct firedtv *fdtv, void *arg)
 		data_length = 0;
 		for (i = 0; i < (msg->msg[3] & 0x7f); i++)
 			data_length = (data_length << 8) + msg->msg[data_pos++];
+		if (((msg->msg[3] & 0x7f) + date_length) > (sizeof(msg->msg) - 4))
+			return -EINVAL;
 	} else {
 		data_length = msg->msg[3];
 	}
-- 
2.23.0

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH] media firewire firedtv-avc fix a buffer overflow in avc_ca_pmt()
  2021-06-07  7:39 [PATCH] media firewire firedtv-avc fix a buffer overflow in avc_ca_pmt() Yang Yanchao
@ 2021-06-07  9:26 ` Greg KH
  2021-06-07 14:28 ` Dan Carpenter
  1 sibling, 0 replies; 10+ messages in thread
From: Greg KH @ 2021-06-07  9:26 UTC (permalink / raw)
  To: Yang Yanchao
  Cc: dan.carpenter, linux-distros, linux-media, linux1394-devel,
	mchehab, security

On Mon, Jun 07, 2021 at 03:39:00PM +0800, Yang Yanchao wrote:
> For CVE-2021-3542:

What does that mean?  We don't know what cve numbers refer to as there
is no way to really track and update the information with them.  Please
spell out the issue please.

> 
> 1???read_pos will be added four times in the patch, 
> so use "read_pos + 4 < length" and write_pos as well

what is "???" here?

> 
> 2. The last four bits of c->operand are used for CRC, 
> so "sizeof (C - > operand) - 4" is used
> 
> 3. "read_pos+=2" is added after the end of read_pos, so add value (read_pos >= length)
> 
> 4. In order to avoid memcpy crossing the boundary, es_ info_ length > length - read_ pos
> 
> 5. When the date_length is a specific input of a construction,it will cause memcpy
>  to exceed the boundary, "(MSG - > MSG [3] & 0x7F) + date_ length) > (sizeof(msg->msg) - 4)"

I do not understand, this is saying what you did, not _why_ you did it.
can you please rework this to make it more obvious what you are doing?

And shouldn't this be more than one patch?  A series of patches, each
fixing one thing?

And no need to put security@kernel.org on this now that you have sent it
to a public mailing list.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH] media firewire firedtv-avc fix a buffer overflow in avc_ca_pmt()
  2021-06-07  7:39 [PATCH] media firewire firedtv-avc fix a buffer overflow in avc_ca_pmt() Yang Yanchao
  2021-06-07  9:26 ` Greg KH
@ 2021-06-07 14:28 ` Dan Carpenter
  1 sibling, 0 replies; 10+ messages in thread
From: Dan Carpenter @ 2021-06-07 14:28 UTC (permalink / raw)
  To: Yang Yanchao
  Cc: linux-distros, linux-media, linux1394-devel, mchehab, security

Thanks for resending this patch, but you need to preserve the author
and Reported-by tags.

https://lore.kernel.org/linux-media/YHaulytonFcW+lyZ@mwanda/

You changed the check in fdtv_ca_pmt() but I don't understand why you
did that...  But looking at it again, I think neither of us was correct,
instead of "sizeof(msg->msg) - 4" it should be "- data_pos":

	if (data_length > sizeof(msg->msg) - data_pos)
		return -EINVAL;

I will resend a v2.

regards,
dan carpenter


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH] media: firewire: firedtv-avc: fix a buffer overflow in avc_ca_pmt()
  2021-10-11  7:04                   ` Salvatore Bonaccorso
@ 2021-10-11  9:42                     ` Dan Carpenter
  0 siblings, 0 replies; 10+ messages in thread
From: Dan Carpenter @ 2021-10-11  9:42 UTC (permalink / raw)
  To: Salvatore Bonaccorso, Mauro Carvalho Chehab, Hans Verkuil
  Cc: Linus Torvalds, Stefan Richter, Luo Likang,
	Linux Media Mailing List, linux1394-devel, Yang Yanchao,
	Security Officers

The bounds checking in avc_ca_pmt() is not strict enough.  It should
be checking "read_pos + 4" because it's reading 5 bytes.  If the
"es_info_length" is non-zero then it reads a 6th byte so there needs to
be an additional check for that.

I also added checks for the "write_pos".  I don't think these are
required because "read_pos" and "write_pos" are tied together so
checking one ought to be enough.  But they make the code easier to
understand for me.  The check on write_pos is:

	if (write_pos + 4 >= sizeof(c->operand) - 4) {

The first "+ 4" is because we're writing 5 bytes and the last " - 4"
is to leave space for the CRC.

The other problem is that "length" can be invalid.  It comes from
"data_length" in fdtv_ca_pmt().

Cc: stable@vger.kernel.org
Reported-by: Luo Likang <luolikang@nsfocus.com>
Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
---

Cherry picked from linux-next.

 drivers/media/firewire/firedtv-avc.c | 14 +++++++++++---
 drivers/media/firewire/firedtv-ci.c  |  2 ++
 2 files changed, 13 insertions(+), 3 deletions(-)

diff --git a/drivers/media/firewire/firedtv-avc.c b/drivers/media/firewire/firedtv-avc.c
index 2bf9467b917d..71991f8638e6 100644
--- a/drivers/media/firewire/firedtv-avc.c
+++ b/drivers/media/firewire/firedtv-avc.c
@@ -1165,7 +1165,11 @@ int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length)
 		read_pos += program_info_length;
 		write_pos += program_info_length;
 	}
-	while (read_pos < length) {
+	while (read_pos + 4 < length) {
+		if (write_pos + 4 >= sizeof(c->operand) - 4) {
+			ret = -EINVAL;
+			goto out;
+		}
 		c->operand[write_pos++] = msg[read_pos++];
 		c->operand[write_pos++] = msg[read_pos++];
 		c->operand[write_pos++] = msg[read_pos++];
@@ -1177,13 +1181,17 @@ int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length)
 		c->operand[write_pos++] = es_info_length >> 8;
 		c->operand[write_pos++] = es_info_length & 0xff;
 		if (es_info_length > 0) {
+			if (read_pos >= length) {
+				ret = -EINVAL;
+				goto out;
+			}
 			pmt_cmd_id = msg[read_pos++];
 			if (pmt_cmd_id != 1 && pmt_cmd_id != 4)
 				dev_err(fdtv->device, "invalid pmt_cmd_id %d at stream level\n",
 					pmt_cmd_id);
 
-			if (es_info_length > sizeof(c->operand) - 4 -
-					     write_pos) {
+			if (es_info_length > sizeof(c->operand) - 4 - write_pos ||
+			    es_info_length > length - read_pos) {
 				ret = -EINVAL;
 				goto out;
 			}
diff --git a/drivers/media/firewire/firedtv-ci.c b/drivers/media/firewire/firedtv-ci.c
index 9363d005e2b6..e0d57e09dab0 100644
--- a/drivers/media/firewire/firedtv-ci.c
+++ b/drivers/media/firewire/firedtv-ci.c
@@ -134,6 +134,8 @@ static int fdtv_ca_pmt(struct firedtv *fdtv, void *arg)
 	} else {
 		data_length = msg->msg[3];
 	}
+	if (data_length > sizeof(msg->msg) - data_pos)
+		return -EINVAL;
 
 	return avc_ca_pmt(fdtv, &msg->msg[data_pos], data_length);
 }
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH] media: firewire: firedtv-avc: fix a buffer overflow in avc_ca_pmt()
  2021-07-29 10:32   ` Greg KH
@ 2021-08-16  7:01     ` Salvatore Bonaccorso
  2021-08-16  7:27       ` [PATCH v2 RESEND] " Dan Carpenter
  0 siblings, 1 reply; 10+ messages in thread
From: Salvatore Bonaccorso @ 2021-08-16  7:01 UTC (permalink / raw)
  To: Greg KH
  Cc: Dan Carpenter, Stefan Richter, Luo Likang, security,
	linux-distros, Mauro Carvalho Chehab, linux-media,
	linux1394-devel

Hi,

On Thu, Jul 29, 2021 at 12:32:21PM +0200, Greg KH wrote:
> On Wed, Apr 14, 2021 at 11:57:59AM +0300, Dan Carpenter wrote:
> > The bounds checking in avc_ca_pmt() is not strict enough.  It should
> > be checking "read_pos + 4" because it's reading 5 bytes.  If the
> > "es_info_length" is non-zero then it reads a 6th byte so there needs to
> > be an additional check for that.
> > 
> > I also added checks for the "write_pos".  I don't think these are
> > required because "read_pos" and "write_pos" are tied together so
> > checking one ought to be enough.  But they make the code easier to
> > understand for me.
> > 
> > The other problem is that "length" can be invalid.  It comes from
> > "data_length" in fdtv_ca_pmt().
> > 
> > Cc: stable@vger.kernel.org
> > Reported-by: Luo Likang <luolikang@nsfocus.com>
> > Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
> > ---
> > This hardware isn't super common so there is no embargo.  Resending
> > through normal lists.
> > 
> > Oh, another thing is the data_length calculation in fdtv_ca_pmt() seems
> > very suspicous.  Reading more than 4 bytes in the loop will lead to
> > shift wrapping.
> > 
> >  drivers/media/firewire/firedtv-avc.c | 14 +++++++++++---
> >  drivers/media/firewire/firedtv-ci.c  |  2 ++
> >  2 files changed, 13 insertions(+), 3 deletions(-)
> > 
> > diff --git a/drivers/media/firewire/firedtv-avc.c b/drivers/media/firewire/firedtv-avc.c
> > index 2bf9467b917d..71991f8638e6 100644
> > --- a/drivers/media/firewire/firedtv-avc.c
> > +++ b/drivers/media/firewire/firedtv-avc.c
> > @@ -1165,7 +1165,11 @@ int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length)
> >  		read_pos += program_info_length;
> >  		write_pos += program_info_length;
> >  	}
> > -	while (read_pos < length) {
> > +	while (read_pos + 4 < length) {
> > +		if (write_pos + 4 >= sizeof(c->operand) - 4) {
> > +			ret = -EINVAL;
> > +			goto out;
> > +		}
> >  		c->operand[write_pos++] = msg[read_pos++];
> >  		c->operand[write_pos++] = msg[read_pos++];
> >  		c->operand[write_pos++] = msg[read_pos++];
> > @@ -1177,13 +1181,17 @@ int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length)
> >  		c->operand[write_pos++] = es_info_length >> 8;
> >  		c->operand[write_pos++] = es_info_length & 0xff;
> >  		if (es_info_length > 0) {
> > +			if (read_pos >= length) {
> > +				ret = -EINVAL;
> > +				goto out;
> > +			}
> >  			pmt_cmd_id = msg[read_pos++];
> >  			if (pmt_cmd_id != 1 && pmt_cmd_id != 4)
> >  				dev_err(fdtv->device, "invalid pmt_cmd_id %d at stream level\n",
> >  					pmt_cmd_id);
> >  
> > -			if (es_info_length > sizeof(c->operand) - 4 -
> > -					     write_pos) {
> > +			if (es_info_length > sizeof(c->operand) - 4 - write_pos ||
> > +			    es_info_length > length - read_pos) {
> >  				ret = -EINVAL;
> >  				goto out;
> >  			}
> > diff --git a/drivers/media/firewire/firedtv-ci.c b/drivers/media/firewire/firedtv-ci.c
> > index 9363d005e2b6..2d6992ac5dd6 100644
> > --- a/drivers/media/firewire/firedtv-ci.c
> > +++ b/drivers/media/firewire/firedtv-ci.c
> > @@ -134,6 +134,8 @@ static int fdtv_ca_pmt(struct firedtv *fdtv, void *arg)
> >  	} else {
> >  		data_length = msg->msg[3];
> >  	}
> > +	if (data_length > sizeof(msg->msg) - 4)
> > +		return -EINVAL;
> >  
> >  	return avc_ca_pmt(fdtv, &msg->msg[data_pos], data_length);
> >  }
> > -- 
> > 2.30.2
> > 
> 
> This patch seems to have gotten lost.  Any change of it getting applied?

As far I can see there was then a version 2 of the patch, but that one
got list somewhere. Friendly ping on this thread :)

Regards,
Salvatore

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH] media: firewire: firedtv-avc: fix a buffer overflow in avc_ca_pmt()
  2021-04-14  8:57 ` [PATCH] media: firewire: firedtv-avc: " Dan Carpenter
  2021-04-19 21:42   ` Kees Cook
  2021-07-19 10:25   ` Dan Carpenter
@ 2021-07-29 10:32   ` Greg KH
  2021-08-16  7:01     ` Salvatore Bonaccorso
  2 siblings, 1 reply; 10+ messages in thread
From: Greg KH @ 2021-07-29 10:32 UTC (permalink / raw)
  To: Dan Carpenter, Stefan Richter, Luo Likang, security,
	linux-distros, Mauro Carvalho Chehab, linux-media,
	linux1394-devel

On Wed, Apr 14, 2021 at 11:57:59AM +0300, Dan Carpenter wrote:
> The bounds checking in avc_ca_pmt() is not strict enough.  It should
> be checking "read_pos + 4" because it's reading 5 bytes.  If the
> "es_info_length" is non-zero then it reads a 6th byte so there needs to
> be an additional check for that.
> 
> I also added checks for the "write_pos".  I don't think these are
> required because "read_pos" and "write_pos" are tied together so
> checking one ought to be enough.  But they make the code easier to
> understand for me.
> 
> The other problem is that "length" can be invalid.  It comes from
> "data_length" in fdtv_ca_pmt().
> 
> Cc: stable@vger.kernel.org
> Reported-by: Luo Likang <luolikang@nsfocus.com>
> Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
> ---
> This hardware isn't super common so there is no embargo.  Resending
> through normal lists.
> 
> Oh, another thing is the data_length calculation in fdtv_ca_pmt() seems
> very suspicous.  Reading more than 4 bytes in the loop will lead to
> shift wrapping.
> 
>  drivers/media/firewire/firedtv-avc.c | 14 +++++++++++---
>  drivers/media/firewire/firedtv-ci.c  |  2 ++
>  2 files changed, 13 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/media/firewire/firedtv-avc.c b/drivers/media/firewire/firedtv-avc.c
> index 2bf9467b917d..71991f8638e6 100644
> --- a/drivers/media/firewire/firedtv-avc.c
> +++ b/drivers/media/firewire/firedtv-avc.c
> @@ -1165,7 +1165,11 @@ int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length)
>  		read_pos += program_info_length;
>  		write_pos += program_info_length;
>  	}
> -	while (read_pos < length) {
> +	while (read_pos + 4 < length) {
> +		if (write_pos + 4 >= sizeof(c->operand) - 4) {
> +			ret = -EINVAL;
> +			goto out;
> +		}
>  		c->operand[write_pos++] = msg[read_pos++];
>  		c->operand[write_pos++] = msg[read_pos++];
>  		c->operand[write_pos++] = msg[read_pos++];
> @@ -1177,13 +1181,17 @@ int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length)
>  		c->operand[write_pos++] = es_info_length >> 8;
>  		c->operand[write_pos++] = es_info_length & 0xff;
>  		if (es_info_length > 0) {
> +			if (read_pos >= length) {
> +				ret = -EINVAL;
> +				goto out;
> +			}
>  			pmt_cmd_id = msg[read_pos++];
>  			if (pmt_cmd_id != 1 && pmt_cmd_id != 4)
>  				dev_err(fdtv->device, "invalid pmt_cmd_id %d at stream level\n",
>  					pmt_cmd_id);
>  
> -			if (es_info_length > sizeof(c->operand) - 4 -
> -					     write_pos) {
> +			if (es_info_length > sizeof(c->operand) - 4 - write_pos ||
> +			    es_info_length > length - read_pos) {
>  				ret = -EINVAL;
>  				goto out;
>  			}
> diff --git a/drivers/media/firewire/firedtv-ci.c b/drivers/media/firewire/firedtv-ci.c
> index 9363d005e2b6..2d6992ac5dd6 100644
> --- a/drivers/media/firewire/firedtv-ci.c
> +++ b/drivers/media/firewire/firedtv-ci.c
> @@ -134,6 +134,8 @@ static int fdtv_ca_pmt(struct firedtv *fdtv, void *arg)
>  	} else {
>  		data_length = msg->msg[3];
>  	}
> +	if (data_length > sizeof(msg->msg) - 4)
> +		return -EINVAL;
>  
>  	return avc_ca_pmt(fdtv, &msg->msg[data_pos], data_length);
>  }
> -- 
> 2.30.2
> 

This patch seems to have gotten lost.  Any change of it getting applied?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH] media: firewire: firedtv-avc: fix a buffer overflow in avc_ca_pmt()
  2021-04-14  8:57 ` [PATCH] media: firewire: firedtv-avc: " Dan Carpenter
  2021-04-19 21:42   ` Kees Cook
@ 2021-07-19 10:25   ` Dan Carpenter
  2021-07-29 10:32   ` Greg KH
  2 siblings, 0 replies; 10+ messages in thread
From: Dan Carpenter @ 2021-07-19 10:25 UTC (permalink / raw)
  To: Stefan Richter, Luo Likang
  Cc: Mauro Carvalho Chehab, linux-media, linux1394-devel

This was marked as superseded in patchwork.  What are we going to apply
instead?

regards,
dan carpenter

On Wed, Apr 14, 2021 at 11:57:59AM +0300, Dan Carpenter wrote:
> The bounds checking in avc_ca_pmt() is not strict enough.  It should
> be checking "read_pos + 4" because it's reading 5 bytes.  If the
> "es_info_length" is non-zero then it reads a 6th byte so there needs to
> be an additional check for that.
> 
> I also added checks for the "write_pos".  I don't think these are
> required because "read_pos" and "write_pos" are tied together so
> checking one ought to be enough.  But they make the code easier to
> understand for me.
> 
> The other problem is that "length" can be invalid.  It comes from
> "data_length" in fdtv_ca_pmt().
> 
> Cc: stable@vger.kernel.org
> Reported-by: Luo Likang <luolikang@nsfocus.com>
> Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
> ---
> This hardware isn't super common so there is no embargo.  Resending
> through normal lists.
> 
> Oh, another thing is the data_length calculation in fdtv_ca_pmt() seems
> very suspicous.  Reading more than 4 bytes in the loop will lead to
> shift wrapping.
> 
>  drivers/media/firewire/firedtv-avc.c | 14 +++++++++++---
>  drivers/media/firewire/firedtv-ci.c  |  2 ++
>  2 files changed, 13 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/media/firewire/firedtv-avc.c b/drivers/media/firewire/firedtv-avc.c
> index 2bf9467b917d..71991f8638e6 100644
> --- a/drivers/media/firewire/firedtv-avc.c
> +++ b/drivers/media/firewire/firedtv-avc.c
> @@ -1165,7 +1165,11 @@ int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length)
>  		read_pos += program_info_length;
>  		write_pos += program_info_length;
>  	}
> -	while (read_pos < length) {
> +	while (read_pos + 4 < length) {
> +		if (write_pos + 4 >= sizeof(c->operand) - 4) {
> +			ret = -EINVAL;
> +			goto out;
> +		}
>  		c->operand[write_pos++] = msg[read_pos++];
>  		c->operand[write_pos++] = msg[read_pos++];
>  		c->operand[write_pos++] = msg[read_pos++];
> @@ -1177,13 +1181,17 @@ int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length)
>  		c->operand[write_pos++] = es_info_length >> 8;
>  		c->operand[write_pos++] = es_info_length & 0xff;
>  		if (es_info_length > 0) {
> +			if (read_pos >= length) {
> +				ret = -EINVAL;
> +				goto out;
> +			}
>  			pmt_cmd_id = msg[read_pos++];
>  			if (pmt_cmd_id != 1 && pmt_cmd_id != 4)
>  				dev_err(fdtv->device, "invalid pmt_cmd_id %d at stream level\n",
>  					pmt_cmd_id);
>  
> -			if (es_info_length > sizeof(c->operand) - 4 -
> -					     write_pos) {
> +			if (es_info_length > sizeof(c->operand) - 4 - write_pos ||
> +			    es_info_length > length - read_pos) {
>  				ret = -EINVAL;
>  				goto out;
>  			}
> diff --git a/drivers/media/firewire/firedtv-ci.c b/drivers/media/firewire/firedtv-ci.c
> index 9363d005e2b6..2d6992ac5dd6 100644
> --- a/drivers/media/firewire/firedtv-ci.c
> +++ b/drivers/media/firewire/firedtv-ci.c
> @@ -134,6 +134,8 @@ static int fdtv_ca_pmt(struct firedtv *fdtv, void *arg)
>  	} else {
>  		data_length = msg->msg[3];
>  	}
> +	if (data_length > sizeof(msg->msg) - 4)
> +		return -EINVAL;
>  
>  	return avc_ca_pmt(fdtv, &msg->msg[data_pos], data_length);
>  }
> -- 
> 2.30.2

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH] media firewire firedtv-avc fix a buffer overflow in avc_ca_pmt()
@ 2021-06-07  7:38 Yang Yanchao
  0 siblings, 0 replies; 10+ messages in thread
From: Yang Yanchao @ 2021-06-07  7:38 UTC (permalink / raw)
  To: dan.carpenter
  Cc: linux-distros, linux-media, linux1394-devel, mchehab, security

For CVE-2021-3542:

1、read_pos will be added four times in the patch, 
so use "read_pos + 4 < length" and write_pos as well

2. The last four bits of c->operand are used for CRC, 
so "sizeof (C - > operand) - 4" is used

3. "read_pos+=2" is added after the end of read_pos, so add value (read_pos >= length)

4. In order to avoid memcpy crossing the boundary, es_ info_ length > length - read_ pos

5. When the date_length is a specific input of a construction,it will cause memcpy
 to exceed the boundary, "(MSG - > MSG [3] & 0x7F) + date_ length) > (sizeof(msg->msg) - 4)"

Signed-off-by: yangyanchao <yangyanchao6@huawei.com>
---
 drivers/media/firewire/firedtv-avc.c | 14 +++++++++++---
 drivers/media/firewire/firedtv-ci.c  |  2 ++
 2 files changed, 13 insertions(+), 3 deletions(-)
diff --git a/drivers/media/firewire/firedtv-avc.c b/drivers/media/firewire/firedtv-avc.c
index 3ef5df164..8c31cf90c 100644
--- a/drivers/media/firewire/firedtv-avc.c
+++ b/drivers/media/firewire/firedtv-avc.c
@@ -1169,7 +1169,11 @@ int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length)
 		read_pos += program_info_length;
 		write_pos += program_info_length;
 	}
-	while (read_pos < length) {
+	while (read_pos + 4 < length) {
+		if (write_pos + 4 >= sizeof(c->operand) - 4) {
+			ret = -EINVAL;
+			goto out;
+		}
 		c->operand[write_pos++] = msg[read_pos++];
 		c->operand[write_pos++] = msg[read_pos++];
 		c->operand[write_pos++] = msg[read_pos++];
@@ -1181,13 +1185,17 @@ int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length)
 		c->operand[write_pos++] = es_info_length >> 8;
 		c->operand[write_pos++] = es_info_length & 0xff;
 		if (es_info_length > 0) {
+			if (read_pos >= length) {
+				ret = -EINVAL;
+				goto out;
+			}
 			pmt_cmd_id = msg[read_pos++];
 			if (pmt_cmd_id != 1 && pmt_cmd_id != 4)
 				dev_err(fdtv->device, "invalid pmt_cmd_id %d at stream level\n",
 					pmt_cmd_id);
 
-			if (es_info_length > sizeof(c->operand) - 4 -
-					     write_pos) {
+			if (es_info_length > sizeof(c->operand) - 4 - write_pos ||
+			    es_info_length > length - read_pos) {
 				ret = -EINVAL;
 				goto out;
 			}
diff --git a/drivers/media/firewire/firedtv-ci.c b/drivers/media/firewire/firedtv-ci.c
index 8dc5a7495..0e7ffa156 100644
--- a/drivers/media/firewire/firedtv-ci.c
+++ b/drivers/media/firewire/firedtv-ci.c
@@ -135,6 +135,8 @@ static int fdtv_ca_pmt(struct firedtv *fdtv, void *arg)
 		data_length = 0;
 		for (i = 0; i < (msg->msg[3] & 0x7f); i++)
 			data_length = (data_length << 8) + msg->msg[data_pos++];
+		if (((msg->msg[3] & 0x7f) + date_length) > (sizeof(msg->msg) - 4))
+			return -EINVAL;
 	} else {
 		data_length = msg->msg[3];
 	}
-- 
2.23.0

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH] media: firewire: firedtv-avc: fix a buffer overflow in avc_ca_pmt()
  2021-04-14  8:57 ` [PATCH] media: firewire: firedtv-avc: " Dan Carpenter
@ 2021-04-19 21:42   ` Kees Cook
  2021-07-19 10:25   ` Dan Carpenter
  2021-07-29 10:32   ` Greg KH
  2 siblings, 0 replies; 10+ messages in thread
From: Kees Cook @ 2021-04-19 21:42 UTC (permalink / raw)
  To: Dan Carpenter
  Cc: Stefan Richter, Luo Likang, security, linux-distros,
	Mauro Carvalho Chehab, linux-media, linux1394-devel

On Wed, Apr 14, 2021 at 11:57:59AM +0300, Dan Carpenter wrote:
> The bounds checking in avc_ca_pmt() is not strict enough.  It should
> be checking "read_pos + 4" because it's reading 5 bytes.  If the
> "es_info_length" is non-zero then it reads a 6th byte so there needs to
> be an additional check for that.
> 
> I also added checks for the "write_pos".  I don't think these are
> required because "read_pos" and "write_pos" are tied together so
> checking one ought to be enough.  But they make the code easier to
> understand for me.
> 
> The other problem is that "length" can be invalid.  It comes from
> "data_length" in fdtv_ca_pmt().
> 
> Cc: stable@vger.kernel.org
> Reported-by: Luo Likang <luolikang@nsfocus.com>
> Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>

Thanks for the report and the fix!

As a quick note on alternative mitigations, it seems that
CONFIG_UBSAN_BOUNDS would have caught this at runtime too. (i.e.
c->operand[]'s size is known at build time, so out of bounds
indexing should be detected.)

-Kees

> ---
> This hardware isn't super common so there is no embargo.  Resending
> through normal lists.
> 
> Oh, another thing is the data_length calculation in fdtv_ca_pmt() seems
> very suspicous.  Reading more than 4 bytes in the loop will lead to
> shift wrapping.
> 
>  drivers/media/firewire/firedtv-avc.c | 14 +++++++++++---
>  drivers/media/firewire/firedtv-ci.c  |  2 ++
>  2 files changed, 13 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/media/firewire/firedtv-avc.c b/drivers/media/firewire/firedtv-avc.c
> index 2bf9467b917d..71991f8638e6 100644
> --- a/drivers/media/firewire/firedtv-avc.c
> +++ b/drivers/media/firewire/firedtv-avc.c
> @@ -1165,7 +1165,11 @@ int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length)
>  		read_pos += program_info_length;
>  		write_pos += program_info_length;
>  	}
> -	while (read_pos < length) {
> +	while (read_pos + 4 < length) {
> +		if (write_pos + 4 >= sizeof(c->operand) - 4) {
> +			ret = -EINVAL;
> +			goto out;
> +		}
>  		c->operand[write_pos++] = msg[read_pos++];
>  		c->operand[write_pos++] = msg[read_pos++];
>  		c->operand[write_pos++] = msg[read_pos++];
> @@ -1177,13 +1181,17 @@ int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length)
>  		c->operand[write_pos++] = es_info_length >> 8;
>  		c->operand[write_pos++] = es_info_length & 0xff;
>  		if (es_info_length > 0) {
> +			if (read_pos >= length) {
> +				ret = -EINVAL;
> +				goto out;
> +			}
>  			pmt_cmd_id = msg[read_pos++];
>  			if (pmt_cmd_id != 1 && pmt_cmd_id != 4)
>  				dev_err(fdtv->device, "invalid pmt_cmd_id %d at stream level\n",
>  					pmt_cmd_id);
>  
> -			if (es_info_length > sizeof(c->operand) - 4 -
> -					     write_pos) {
> +			if (es_info_length > sizeof(c->operand) - 4 - write_pos ||
> +			    es_info_length > length - read_pos) {
>  				ret = -EINVAL;
>  				goto out;
>  			}
> diff --git a/drivers/media/firewire/firedtv-ci.c b/drivers/media/firewire/firedtv-ci.c
> index 9363d005e2b6..2d6992ac5dd6 100644
> --- a/drivers/media/firewire/firedtv-ci.c
> +++ b/drivers/media/firewire/firedtv-ci.c
> @@ -134,6 +134,8 @@ static int fdtv_ca_pmt(struct firedtv *fdtv, void *arg)
>  	} else {
>  		data_length = msg->msg[3];
>  	}
> +	if (data_length > sizeof(msg->msg) - 4)
> +		return -EINVAL;
>  
>  	return avc_ca_pmt(fdtv, &msg->msg[data_pos], data_length);
>  }
> -- 
> 2.30.2
> 

-- 
Kees Cook

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH] media: firewire: firedtv-avc: fix a buffer overflow in avc_ca_pmt()
       [not found] <000001d73031$d5304480$7f90cd80$@nsfocus.com>
@ 2021-04-14  8:57 ` Dan Carpenter
  2021-04-19 21:42   ` Kees Cook
                     ` (2 more replies)
  0 siblings, 3 replies; 10+ messages in thread
From: Dan Carpenter @ 2021-04-14  8:57 UTC (permalink / raw)
  To: Stefan Richter, Luo Likang
  Cc: security, linux-distros, Mauro Carvalho Chehab, linux-media,
	linux1394-devel

The bounds checking in avc_ca_pmt() is not strict enough.  It should
be checking "read_pos + 4" because it's reading 5 bytes.  If the
"es_info_length" is non-zero then it reads a 6th byte so there needs to
be an additional check for that.

I also added checks for the "write_pos".  I don't think these are
required because "read_pos" and "write_pos" are tied together so
checking one ought to be enough.  But they make the code easier to
understand for me.

The other problem is that "length" can be invalid.  It comes from
"data_length" in fdtv_ca_pmt().

Cc: stable@vger.kernel.org
Reported-by: Luo Likang <luolikang@nsfocus.com>
Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
---
This hardware isn't super common so there is no embargo.  Resending
through normal lists.

Oh, another thing is the data_length calculation in fdtv_ca_pmt() seems
very suspicous.  Reading more than 4 bytes in the loop will lead to
shift wrapping.

 drivers/media/firewire/firedtv-avc.c | 14 +++++++++++---
 drivers/media/firewire/firedtv-ci.c  |  2 ++
 2 files changed, 13 insertions(+), 3 deletions(-)

diff --git a/drivers/media/firewire/firedtv-avc.c b/drivers/media/firewire/firedtv-avc.c
index 2bf9467b917d..71991f8638e6 100644
--- a/drivers/media/firewire/firedtv-avc.c
+++ b/drivers/media/firewire/firedtv-avc.c
@@ -1165,7 +1165,11 @@ int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length)
 		read_pos += program_info_length;
 		write_pos += program_info_length;
 	}
-	while (read_pos < length) {
+	while (read_pos + 4 < length) {
+		if (write_pos + 4 >= sizeof(c->operand) - 4) {
+			ret = -EINVAL;
+			goto out;
+		}
 		c->operand[write_pos++] = msg[read_pos++];
 		c->operand[write_pos++] = msg[read_pos++];
 		c->operand[write_pos++] = msg[read_pos++];
@@ -1177,13 +1181,17 @@ int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length)
 		c->operand[write_pos++] = es_info_length >> 8;
 		c->operand[write_pos++] = es_info_length & 0xff;
 		if (es_info_length > 0) {
+			if (read_pos >= length) {
+				ret = -EINVAL;
+				goto out;
+			}
 			pmt_cmd_id = msg[read_pos++];
 			if (pmt_cmd_id != 1 && pmt_cmd_id != 4)
 				dev_err(fdtv->device, "invalid pmt_cmd_id %d at stream level\n",
 					pmt_cmd_id);
 
-			if (es_info_length > sizeof(c->operand) - 4 -
-					     write_pos) {
+			if (es_info_length > sizeof(c->operand) - 4 - write_pos ||
+			    es_info_length > length - read_pos) {
 				ret = -EINVAL;
 				goto out;
 			}
diff --git a/drivers/media/firewire/firedtv-ci.c b/drivers/media/firewire/firedtv-ci.c
index 9363d005e2b6..2d6992ac5dd6 100644
--- a/drivers/media/firewire/firedtv-ci.c
+++ b/drivers/media/firewire/firedtv-ci.c
@@ -134,6 +134,8 @@ static int fdtv_ca_pmt(struct firedtv *fdtv, void *arg)
 	} else {
 		data_length = msg->msg[3];
 	}
+	if (data_length > sizeof(msg->msg) - 4)
+		return -EINVAL;
 
 	return avc_ca_pmt(fdtv, &msg->msg[data_pos], data_length);
 }
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2021-10-11  9:43 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-06-07  7:39 [PATCH] media firewire firedtv-avc fix a buffer overflow in avc_ca_pmt() Yang Yanchao
2021-06-07  9:26 ` Greg KH
2021-06-07 14:28 ` Dan Carpenter
  -- strict thread matches above, loose matches on Subject: below --
2021-06-07  7:38 Yang Yanchao
     [not found] <000001d73031$d5304480$7f90cd80$@nsfocus.com>
2021-04-14  8:57 ` [PATCH] media: firewire: firedtv-avc: " Dan Carpenter
2021-04-19 21:42   ` Kees Cook
2021-07-19 10:25   ` Dan Carpenter
2021-07-29 10:32   ` Greg KH
2021-08-16  7:01     ` Salvatore Bonaccorso
2021-08-16  7:27       ` [PATCH v2 RESEND] " Dan Carpenter
2021-09-01 10:40         ` Dan Carpenter
2021-09-12 13:14           ` Salvatore Bonaccorso
2021-09-12 18:26             ` Linus Torvalds
2021-09-13 13:23               ` Mauro Carvalho Chehab
2021-09-19 18:45                 ` Salvatore Bonaccorso
2021-10-11  7:04                   ` Salvatore Bonaccorso
2021-10-11  9:42                     ` [PATCH] " Dan Carpenter

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.