diff mbox series

[04/22] firmware: arm_scmi: Validate BASE_DISCOVER_LIST_PROTOCOLS reply

Message ID 20220330150551.2573938-5-cristian.marussi@arm.com (mailing list archive)
State New, archived
Headers show
Series SCMIv3.1 Miscellaneous changes | expand

Commit Message

Cristian Marussi March 30, 2022, 3:05 p.m. UTC
Do not blindly trust SCMI backend server reply about list of implemented
protocols, instead validate the reported length of the list of protocols
against the real payload size of the message reply.

Fixes: b6f20ff8bd9 ("firmware: arm_scmi: add common infrastructure and support for base protocol")
Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
---
 drivers/firmware/arm_scmi/base.c | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

Comments

Sudeep Holla April 28, 2022, 10:07 a.m. UTC | #1
On Wed, Mar 30, 2022 at 04:05:33PM +0100, Cristian Marussi wrote:
> Do not blindly trust SCMI backend server reply about list of implemented
> protocols, instead validate the reported length of the list of protocols
> against the real payload size of the message reply.
>
> Fixes: b6f20ff8bd9 ("firmware: arm_scmi: add common infrastructure and support for base protocol")
> Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
> ---
>  drivers/firmware/arm_scmi/base.c | 21 +++++++++++++++++++++
>  1 file changed, 21 insertions(+)
>
> diff --git a/drivers/firmware/arm_scmi/base.c b/drivers/firmware/arm_scmi/base.c
> index f279146f8110..c1165d1282ef 100644
> --- a/drivers/firmware/arm_scmi/base.c
> +++ b/drivers/firmware/arm_scmi/base.c
> @@ -189,6 +189,9 @@ scmi_base_implementation_list_get(const struct scmi_protocol_handle *ph,
>  	list = t->rx.buf + sizeof(*num_ret);
>
>  	do {
> +		size_t real_list_sz;
> +		u32 calc_list_sz;
> +
>  		/* Set the number of protocols to be skipped/already read */
>  		*num_skip = cpu_to_le32(tot_num_ret);
>
> @@ -202,6 +205,24 @@ scmi_base_implementation_list_get(const struct scmi_protocol_handle *ph,
>  			break;
>  		}
>
> +		if (t->rx.len < (sizeof(u32) * 2)) {
> +			dev_err(dev, "Truncated reply - rx.len:%zd\n",
> +				t->rx.len);
> +			ret = -EPROTO;
> +			break;
> +		}
> +
> +		real_list_sz = t->rx.len - sizeof(u32);
> +		calc_list_sz = ((loop_num_ret / sizeof(u32)) +
> +				!!(loop_num_ret % sizeof(u32))) * sizeof(u32);

Any reason this can't be (loop_num_ret - 1) / sizeof(u32) + 1 ?

--
Regards,
Sudeep
Cristian Marussi April 28, 2022, 1:45 p.m. UTC | #2
On Thu, Apr 28, 2022 at 11:07:29AM +0100, Sudeep Holla wrote:
> On Wed, Mar 30, 2022 at 04:05:33PM +0100, Cristian Marussi wrote:
> > Do not blindly trust SCMI backend server reply about list of implemented
> > protocols, instead validate the reported length of the list of protocols
> > against the real payload size of the message reply.
> >
> > Fixes: b6f20ff8bd9 ("firmware: arm_scmi: add common infrastructure and support for base protocol")
> > Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
> > ---
> >  drivers/firmware/arm_scmi/base.c | 21 +++++++++++++++++++++
> >  1 file changed, 21 insertions(+)
> >
> > diff --git a/drivers/firmware/arm_scmi/base.c b/drivers/firmware/arm_scmi/base.c
> > index f279146f8110..c1165d1282ef 100644
> > --- a/drivers/firmware/arm_scmi/base.c
> > +++ b/drivers/firmware/arm_scmi/base.c
> > @@ -189,6 +189,9 @@ scmi_base_implementation_list_get(const struct scmi_protocol_handle *ph,
> >  	list = t->rx.buf + sizeof(*num_ret);
> >
> >  	do {
> > +		size_t real_list_sz;
> > +		u32 calc_list_sz;
> > +
> >  		/* Set the number of protocols to be skipped/already read */
> >  		*num_skip = cpu_to_le32(tot_num_ret);
> >
> > @@ -202,6 +205,24 @@ scmi_base_implementation_list_get(const struct scmi_protocol_handle *ph,
> >  			break;
> >  		}
> >
> > +		if (t->rx.len < (sizeof(u32) * 2)) {
> > +			dev_err(dev, "Truncated reply - rx.len:%zd\n",
> > +				t->rx.len);
> > +			ret = -EPROTO;
> > +			break;
> > +		}
> > +
> > +		real_list_sz = t->rx.len - sizeof(u32);
> > +		calc_list_sz = ((loop_num_ret / sizeof(u32)) +
> > +				!!(loop_num_ret % sizeof(u32))) * sizeof(u32);
> 
> Any reason this can't be (loop_num_ret - 1) / sizeof(u32) + 1 ?
> 

At first sight could be fine with your easier version BUT what if loop_num_ret
is returned as zero ?

real_list_sz should be ZERO length and calc_list_sz

im my version:

calc_list_sz = ((0/4) +!!(0%4)) * 4   ===>> 0

while in the simplified one gets calculated wrong:

calc_list_sz = (0-1)/4 + 1 ====> 1

...moreover being both loop_num_ret and calc_list_sz unsigned I am even
not so sure about implicit casting messing things up evenm more :D

So I sticked to the more convoluted approach :D

....Have I missed something else ?

Thanks,
Cristian
Sudeep Holla April 28, 2022, 1:55 p.m. UTC | #3
On Thu, Apr 28, 2022 at 02:45:07PM +0100, Cristian Marussi wrote:
> On Thu, Apr 28, 2022 at 11:07:29AM +0100, Sudeep Holla wrote:
> > On Wed, Mar 30, 2022 at 04:05:33PM +0100, Cristian Marussi wrote:
> > > Do not blindly trust SCMI backend server reply about list of implemented
> > > protocols, instead validate the reported length of the list of protocols
> > > against the real payload size of the message reply.
> > >
> > > Fixes: b6f20ff8bd9 ("firmware: arm_scmi: add common infrastructure and support for base protocol")
> > > Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
> > > ---
> > >  drivers/firmware/arm_scmi/base.c | 21 +++++++++++++++++++++
> > >  1 file changed, 21 insertions(+)
> > >
> > > diff --git a/drivers/firmware/arm_scmi/base.c b/drivers/firmware/arm_scmi/base.c
> > > index f279146f8110..c1165d1282ef 100644
> > > --- a/drivers/firmware/arm_scmi/base.c
> > > +++ b/drivers/firmware/arm_scmi/base.c
> > > @@ -189,6 +189,9 @@ scmi_base_implementation_list_get(const struct scmi_protocol_handle *ph,
> > >  	list = t->rx.buf + sizeof(*num_ret);
> > >
> > >  	do {
> > > +		size_t real_list_sz;
> > > +		u32 calc_list_sz;
> > > +
> > >  		/* Set the number of protocols to be skipped/already read */
> > >  		*num_skip = cpu_to_le32(tot_num_ret);
> > >
> > > @@ -202,6 +205,24 @@ scmi_base_implementation_list_get(const struct scmi_protocol_handle *ph,
> > >  			break;
> > >  		}
> > >
> > > +		if (t->rx.len < (sizeof(u32) * 2)) {
> > > +			dev_err(dev, "Truncated reply - rx.len:%zd\n",
> > > +				t->rx.len);
> > > +			ret = -EPROTO;
> > > +			break;
> > > +		}
> > > +
> > > +		real_list_sz = t->rx.len - sizeof(u32);
> > > +		calc_list_sz = ((loop_num_ret / sizeof(u32)) +
> > > +				!!(loop_num_ret % sizeof(u32))) * sizeof(u32);
> > 
> > Any reason this can't be (loop_num_ret - 1) / sizeof(u32) + 1 ?
> > 
> 
> At first sight could be fine with your easier version BUT what if loop_num_ret
> is returned as zero ?
> 
> real_list_sz should be ZERO length and calc_list_sz
> 
> im my version:
> 
> calc_list_sz = ((0/4) +!!(0%4)) * 4   ===>> 0
> 
> while in the simplified one gets calculated wrong:
> 
> calc_list_sz = (0-1)/4 + 1 ====> 1
> 
> ...moreover being both loop_num_ret and calc_list_sz unsigned I am even
> not so sure about implicit casting messing things up evenm more :D
> 
> So I sticked to the more convoluted approach :D
> 
> ....Have I missed something else ?
>

Right, but shouldn't we break if it 0 much earlier. It must not happen with
your new logic and even if it does, wouldn't it be better to break earlier ?
Cristian Marussi April 28, 2022, 2:03 p.m. UTC | #4
On Thu, Apr 28, 2022 at 02:55:04PM +0100, Sudeep Holla wrote:
> On Thu, Apr 28, 2022 at 02:45:07PM +0100, Cristian Marussi wrote:
> > On Thu, Apr 28, 2022 at 11:07:29AM +0100, Sudeep Holla wrote:
> > > On Wed, Mar 30, 2022 at 04:05:33PM +0100, Cristian Marussi wrote:
> > > > Do not blindly trust SCMI backend server reply about list of implemented
> > > > protocols, instead validate the reported length of the list of protocols
> > > > against the real payload size of the message reply.
> > > >
> > > > Fixes: b6f20ff8bd9 ("firmware: arm_scmi: add common infrastructure and support for base protocol")
> > > > Signed-off-by: Cristian Marussi <cristian.marussi@arm.com>
> > > > ---
> > > >  drivers/firmware/arm_scmi/base.c | 21 +++++++++++++++++++++
> > > >  1 file changed, 21 insertions(+)
> > > >
> > > > diff --git a/drivers/firmware/arm_scmi/base.c b/drivers/firmware/arm_scmi/base.c
> > > > index f279146f8110..c1165d1282ef 100644
> > > > --- a/drivers/firmware/arm_scmi/base.c
> > > > +++ b/drivers/firmware/arm_scmi/base.c
> > > > @@ -189,6 +189,9 @@ scmi_base_implementation_list_get(const struct scmi_protocol_handle *ph,
> > > >  	list = t->rx.buf + sizeof(*num_ret);
> > > >
> > > >  	do {
> > > > +		size_t real_list_sz;
> > > > +		u32 calc_list_sz;
> > > > +
> > > >  		/* Set the number of protocols to be skipped/already read */
> > > >  		*num_skip = cpu_to_le32(tot_num_ret);
> > > >
> > > > @@ -202,6 +205,24 @@ scmi_base_implementation_list_get(const struct scmi_protocol_handle *ph,
> > > >  			break;
> > > >  		}
> > > >
> > > > +		if (t->rx.len < (sizeof(u32) * 2)) {
> > > > +			dev_err(dev, "Truncated reply - rx.len:%zd\n",
> > > > +				t->rx.len);
> > > > +			ret = -EPROTO;
> > > > +			break;
> > > > +		}
> > > > +
> > > > +		real_list_sz = t->rx.len - sizeof(u32);
> > > > +		calc_list_sz = ((loop_num_ret / sizeof(u32)) +
> > > > +				!!(loop_num_ret % sizeof(u32))) * sizeof(u32);
> > > 
> > > Any reason this can't be (loop_num_ret - 1) / sizeof(u32) + 1 ?
> > > 
> > 
> > At first sight could be fine with your easier version BUT what if loop_num_ret
> > is returned as zero ?
> > 
> > real_list_sz should be ZERO length and calc_list_sz
> > 
> > im my version:
> > 
> > calc_list_sz = ((0/4) +!!(0%4)) * 4   ===>> 0
> > 
> > while in the simplified one gets calculated wrong:
> > 
> > calc_list_sz = (0-1)/4 + 1 ====> 1
> > 
> > ...moreover being both loop_num_ret and calc_list_sz unsigned I am even
> > not so sure about implicit casting messing things up evenm more :D
> > 
> > So I sticked to the more convoluted approach :D
> > 
> > ....Have I missed something else ?
> >
> 
> Right, but shouldn't we break if it 0 much earlier. It must not happen with
> your new logic and even if it does, wouldn't it be better to break earlier ?
> 

Fine for me.

Thanks,
Cristian
diff mbox series

Patch

diff --git a/drivers/firmware/arm_scmi/base.c b/drivers/firmware/arm_scmi/base.c
index f279146f8110..c1165d1282ef 100644
--- a/drivers/firmware/arm_scmi/base.c
+++ b/drivers/firmware/arm_scmi/base.c
@@ -189,6 +189,9 @@  scmi_base_implementation_list_get(const struct scmi_protocol_handle *ph,
 	list = t->rx.buf + sizeof(*num_ret);
 
 	do {
+		size_t real_list_sz;
+		u32 calc_list_sz;
+
 		/* Set the number of protocols to be skipped/already read */
 		*num_skip = cpu_to_le32(tot_num_ret);
 
@@ -202,6 +205,24 @@  scmi_base_implementation_list_get(const struct scmi_protocol_handle *ph,
 			break;
 		}
 
+		if (t->rx.len < (sizeof(u32) * 2)) {
+			dev_err(dev, "Truncated reply - rx.len:%zd\n",
+				t->rx.len);
+			ret = -EPROTO;
+			break;
+		}
+
+		real_list_sz = t->rx.len - sizeof(u32);
+		calc_list_sz = ((loop_num_ret / sizeof(u32)) +
+				!!(loop_num_ret % sizeof(u32))) * sizeof(u32);
+		if (calc_list_sz != real_list_sz) {
+			dev_err(dev,
+				"Malformed reply - real_sz:%zd  calc_sz:%u\n",
+				real_list_sz, calc_list_sz);
+			ret = -EPROTO;
+			break;
+		}
+
 		for (loop = 0; loop < loop_num_ret; loop++)
 			protocols_imp[tot_num_ret + loop] = *(list + loop);