diff mbox series

[1/1] swupdate-progress: Terminate bar array string

Message ID 20200712100537.17077-2-toertel@gmail.com
State Changes Requested
Headers show
Series swupdate-progress: Terminate bar array string | expand

Commit Message

Mark Jonas July 12, 2020, 10:05 a.m. UTC
The bar array contains the progress bar. Its length is 60 characters
but space for a terminating \0 was forgotten. This causes problems when
feeding bar to fprintf().

Suggested-by: Tomas Frydrych <tomas@tomtain.com>
Signed-off-by: Mark Jonas <toertel@gmail.com>
---
 tools/swupdate-progress.c | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

Comments

Stefano Babic July 12, 2020, 10:28 a.m. UTC | #1
Hi Mark,

On 12.07.20 12:05, Mark Jonas wrote:
> The bar array contains the progress bar. Its length is 60 characters
> but space for a terminating \0 was forgotten. This causes problems when
> feeding bar to fprintf().
> 

Ok - so I understand the issue is the missing '\0' when bar reaches 100%.

> Suggested-by: Tomas Frydrych <tomas@tomtain.com>
> Signed-off-by: Mark Jonas <toertel@gmail.com>
> ---
>   tools/swupdate-progress.c | 14 ++++++++------
>   1 file changed, 8 insertions(+), 6 deletions(-)
> 
> diff --git a/tools/swupdate-progress.c b/tools/swupdate-progress.c
> index 5a2c888..a96793d 100644
> --- a/tools/swupdate-progress.c
> +++ b/tools/swupdate-progress.c
> @@ -179,7 +179,8 @@ int main(int argc, char **argv)
>   	int psplash_ok = 0;
>   	unsigned int curstep = 0;
>   	unsigned int percent = 0;
> -	char bar[60];
> +	const int bar_len = 60;
> +	char bar[bar_len+1];
>   	unsigned int filled_len;
>   	int opt_c = 0;
>   	int opt_w = 0;
> @@ -222,7 +223,7 @@ int main(int argc, char **argv)
>   			break;
>   		}
>   	}
> -		
> +
>   	rundir = getenv("PSPLASH_FIFO_DIR");
>   	if (!rundir)
>   		rundir = "/run";
> @@ -292,12 +293,13 @@ int main(int argc, char **argv)
>   		if ((msg.cur_step != curstep) && (curstep != 0))
>   			fprintf(stdout, "\n");
>   
> -		filled_len = sizeof(bar) * msg.cur_percent / 100;
> -		if (filled_len > sizeof(bar) - 1)
> -			filled_len = sizeof(bar) - 1;
> +		filled_len = bar_len * msg.cur_percent / 100;
> +		if (filled_len > bar_len)
> +			filled_len = bar_len;

Anyway, current code is writing at maximu 59 chars instead of 60. You 
are just adding a further byte, that is line is just a byte longer. But 
according to the issue, is it not enough to write once a trailing '\0' 
into the string, or better to initialize the bar array before the 
while() loop ? The current code should never write into the last byte of 
the bar array.

>   
>   		memset(bar,'=', filled_len);
> -		memset(&bar[filled_len], '-', sizeof(bar) - filled_len - 1);
> +		memset(&bar[filled_len], '-', bar_len - filled_len);

This does not change the behavior.

> +		bar[bar_len] = '\0';

This is what is missing and fixes the issue IMHO,  that it is enough to 
terminate the string.

>   
>   		fprintf(stdout, "[ %.60s ] %d of %d %d%% (%s)\r",
>   			bar,
> 

Best regards,
Stefano
Mark Jonas July 12, 2020, 10:49 a.m. UTC | #2
Hi Stefano,

> On 12.07.20 12:05, Mark Jonas wrote:
> > The bar array contains the progress bar. Its length is 60 characters
> > but space for a terminating \0 was forgotten. This causes problems when
> > feeding bar to fprintf().
> >
>
> Ok - so I understand the issue is the missing '\0' when bar reaches 100%.

I think the '\0' at the end of the bar array is always missing.

> > -             filled_len = sizeof(bar) * msg.cur_percent / 100;
> > -             if (filled_len > sizeof(bar) - 1)
> > -                     filled_len = sizeof(bar) - 1;
> > +             filled_len = bar_len * msg.cur_percent / 100;
> > +             if (filled_len > bar_len)
> > +                     filled_len = bar_len;
>
> Anyway, current code is writing at maximu 59 chars instead of 60. You
> are just adding a further byte, that is line is just a byte longer. But
> according to the issue, is it not enough to write once a trailing '\0'
> into the string, or better to initialize the bar array before the
> while() loop ? The current code should never write into the last byte of
> the bar array.

My understanding from the code was that the intention was that the bar
had a length of 60 visible characters. I considered the limit to 59 visible
characters a bug of the code. If you say that it was always the intention
to have a bar of 59 visible characters that is fine.

> >
> >               memset(bar,'=', filled_len);
> > -             memset(&bar[filled_len], '-', sizeof(bar) - filled_len - 1);
> > +             memset(&bar[filled_len], '-', bar_len - filled_len);
>
> This does not change the behavior.

Yes, that was the intention. But the bar now has 60 visible characters.

> > +             bar[bar_len] = '\0';
>
> This is what is missing and fixes the issue IMHO,  that it is enough to
> terminate the string.

Correct. If you want to keep a visible bar length of 59 characters only the
termination is missing.

Shall I send an updated patch which keeps a visible bar length of 59
characters?

I would prefer to always write the '\0' the after updating the bar to make
it easy for humans to understand that the bar[] is properly terminated.

Greetings,
Mark
Stefano Babic July 12, 2020, 11:04 a.m. UTC | #3
Hi Mark,

On 12.07.20 12:49, Mark Jonas wrote:
> Hi Stefano,
> 
>> On 12.07.20 12:05, Mark Jonas wrote:
>>> The bar array contains the progress bar. Its length is 60 characters
>>> but space for a terminating \0 was forgotten. This causes problems when
>>> feeding bar to fprintf().
>>>
>>
>> Ok - so I understand the issue is the missing '\0' when bar reaches 100%.
> 
> I think the '\0' at the end of the bar array is always missing.
> 

Agree, this is a bug, and bar was never initialized.

>>> -             filled_len = sizeof(bar) * msg.cur_percent / 100;
>>> -             if (filled_len > sizeof(bar) - 1)
>>> -                     filled_len = sizeof(bar) - 1;
>>> +             filled_len = bar_len * msg.cur_percent / 100;
>>> +             if (filled_len > bar_len)
>>> +                     filled_len = bar_len;
>>
>> Anyway, current code is writing at maximu 59 chars instead of 60. You
>> are just adding a further byte, that is line is just a byte longer. But
>> according to the issue, is it not enough to write once a trailing '\0'
>> into the string, or better to initialize the bar array before the
>> while() loop ? The current code should never write into the last byte of
>> the bar array.
> 
> My understanding from the code was that the intention was that the bar
> had a length of 60 visible characters. I considered the limit to 59 visible
> characters a bug of the code. If you say that it was always the intention
> to have a bar of 59 visible characters that is fine.

I just want to understand the issue - the length of the bar is not 
important. I cannot say why I choose 60, it maybe fits very well in a 
80-columns terminal. It was reduced to 59 to previous bugs, it is ok to 
set it to 60.

> 
>>>
>>>                memset(bar,'=', filled_len);
>>> -             memset(&bar[filled_len], '-', sizeof(bar) - filled_len - 1);
>>> +             memset(&bar[filled_len], '-', bar_len - filled_len);
>>
>> This does not change the behavior.
> 
> Yes, that was the intention. But the bar now has 60 visible characters.
> 

Ok, got it.

>>> +             bar[bar_len] = '\0';
>>
>> This is what is missing and fixes the issue IMHO,  that it is enough to
>> terminate the string.
> 
> Correct. If you want to keep a visible bar length of 59 characters only the
> termination is missing.
> 
> Shall I send an updated patch which keeps a visible bar length of 59
> characters?
> 

No, my intention was just to understand the issue and how it is fixed. I 
am fine with the patch.

> I would prefer to always write the '\0' the after updating the bar to make
> it easy for humans to understand that the bar[] is properly terminated.

Agree.

Regards,
Stefano

> 
> Greetings,
> Mark
>
Mark Jonas July 12, 2020, 12:36 p.m. UTC | #4
Hi Stefano,

> >> Ok - so I understand the issue is the missing '\0' when bar reaches 100%.
> >
> > I think the '\0' at the end of the bar array is always missing.
> >
>
> Agree, this is a bug, and bar was never initialized.
>
> >>> -             filled_len = sizeof(bar) * msg.cur_percent / 100;
> >>> -             if (filled_len > sizeof(bar) - 1)
> >>> -                     filled_len = sizeof(bar) - 1;
> >>> +             filled_len = bar_len * msg.cur_percent / 100;
> >>> +             if (filled_len > bar_len)
> >>> +                     filled_len = bar_len;
> >>
> >> Anyway, current code is writing at maximu 59 chars instead of 60. You
> >> are just adding a further byte, that is line is just a byte longer. But
> >> according to the issue, is it not enough to write once a trailing '\0'
> >> into the string, or better to initialize the bar array before the
> >> while() loop ? The current code should never write into the last byte of
> >> the bar array.
> >
> > My understanding from the code was that the intention was that the bar
> > had a length of 60 visible characters. I considered the limit to 59 visible
> > characters a bug of the code. If you say that it was always the intention
> > to have a bar of 59 visible characters that is fine.
>
> I just want to understand the issue - the length of the bar is not
> important. I cannot say why I choose 60, it maybe fits very well in a
> 80-columns terminal. It was reduced to 59 to previous bugs, it is ok to
> set it to 60.

Sorry, it seems I forgot to properly explain the motivation.

When looking at the code I saw that the bar was printed using the format
string "[ %.60s ]". From that I derived that the length of the bar should be 60
visible characters. Thus char bar[60] was too small.

During further analysis of the code it became hard for me to track the physical
length of the bar as sizeof(bar)-1. So I decided to introduce the constant
bar_len=60.

Right now after writing the explanation above and looking through my patch I
realized that there is a tiny flaw: The bar length is contained in the constant
bar_len as well as hard coded into the format string. I will fix this
in an update
of the patch.

        fprintf(stdout, "[ %.60s ] %d of %d %d%% (%s)\r",
            bar,
            msg.cur_step, msg.nsteps, msg.cur_percent,
                   msg.cur_image);

will become

        fprintf(stdout, "[ %.*s ] %d of %d %d%% (%s)\r",
            bar_len,
            bar,
            msg.cur_step, msg.nsteps, msg.cur_percent,
                   msg.cur_image);

Now it should be sufficient to change the constant bar_len to an arbitrary
value to change the physical length of the progress bar.

Greetings,
Mark
diff mbox series

Patch

diff --git a/tools/swupdate-progress.c b/tools/swupdate-progress.c
index 5a2c888..a96793d 100644
--- a/tools/swupdate-progress.c
+++ b/tools/swupdate-progress.c
@@ -179,7 +179,8 @@  int main(int argc, char **argv)
 	int psplash_ok = 0;
 	unsigned int curstep = 0;
 	unsigned int percent = 0;
-	char bar[60];
+	const int bar_len = 60;
+	char bar[bar_len+1];
 	unsigned int filled_len;
 	int opt_c = 0;
 	int opt_w = 0;
@@ -222,7 +223,7 @@  int main(int argc, char **argv)
 			break;
 		}
 	}
-		
+
 	rundir = getenv("PSPLASH_FIFO_DIR");
 	if (!rundir)
 		rundir = "/run";
@@ -292,12 +293,13 @@  int main(int argc, char **argv)
 		if ((msg.cur_step != curstep) && (curstep != 0))
 			fprintf(stdout, "\n");
 
-		filled_len = sizeof(bar) * msg.cur_percent / 100;
-		if (filled_len > sizeof(bar) - 1)
-			filled_len = sizeof(bar) - 1;
+		filled_len = bar_len * msg.cur_percent / 100;
+		if (filled_len > bar_len)
+			filled_len = bar_len;
 
 		memset(bar,'=', filled_len);
-		memset(&bar[filled_len], '-', sizeof(bar) - filled_len - 1);
+		memset(&bar[filled_len], '-', bar_len - filled_len);
+		bar[bar_len] = '\0';
 
 		fprintf(stdout, "[ %.60s ] %d of %d %d%% (%s)\r",
 			bar,