On Sat, Aug 18, 2018 at 04:15:09PM +0100, Ricardo Mestre wrote:
> Hi,
> 
> [...]
> 
> 1) $ ac -w ./ac.c
> Segmentation fault (core dumped)
> 
> This one will happen if the structure is big enough to write memory
> out-of-bounds through strlcpy(3) inside update_user(). The easiest way to 
> check
> something is really wrong is to just check if secs is < 0 which should never
> happen. Maybe also adding || secs > LLONG_MAX as well wouldn't hurt? If we 
> also
> use -p sometimes we can even see part of the contents of that file.
> 
> $ ac -p -w ./ac.c
>         opyright (c) 1994 Christopher G. -1360794071379245.75
>         etain the above copyright
> *       -1441250938215251.25
>         user_list *Users = NULL;
>               static  -1653128621001371.00
>               -") == 0)
>               fp = stdin;
>               else if -1655483745007375.25
>               login time for 24hr period in de -1681363511581523.25
>               total                            2456170264876095.00
> 
> With the diff this will be displayed instead:
> 
> $ obj/ac -w ./ac.c
> ac: broken record. secs is -6052908641693484365

Nice find.

I think it would be better to replace the strlcpy with a
strncpy(3) and a comment explaining why, as the use of
strlcpy is the root cause of the segfault.  name isn't a
C string so we shouldn't use strlcpy here.

There's a similar bug in ac():

   447                  default:
   448                          /*
   449                           * if they came in on a pseudo-tty, then it is 
only
   450                           * a login session if the ut_host field is 
non-empty
   451                           */
   452                          if (*usr.ut_name) {
   453                                  if (strncmp(usr.ut_line, "tty", 3) != 0 
||
   454                                      strchr("pqrstuvwxyzPQRST", 
usr.ut_line[3]) != NULL ||
   455                                      *usr.ut_host != '\0')
   456                                          head = log_in(head, &usr);

Technically that strchr should be a memchr, I think, as
usr.ut_line is similarly not guaranteed to be NUL-terminated.

> 2) $ ac -w ./ac.c -d
> Segmentation fault (core dumped)
> 
> This will make it segfault on a different part inside of ac() function because
> of assigning a variable or checking its value with a null dereference. This
> happens because localtime(3) was given a bogus input too large and therefore
> returns NULL so we have to check that value before proceeding further.
> 
> After applying the diff this is what will be displayed:
> 
> $ obj/ac -w ./ac.c -d
> ac: localtime: Value too large to be stored in data type

This part of your diff is ok cheloha@.

Because the changes to address the two segfaults are distinct they
should go in separately.

> Comments? OK?
> 
> Index: ac.c
> ===================================================================
> RCS file: /cvs/src/usr.sbin/ac/ac.c,v
> retrieving revision 1.25
> diff -u -p -u -r1.25 ac.c
> --- ac.c      26 Apr 2018 12:42:51 -0000      1.25
> +++ ac.c      18 Aug 2018 14:41:04 -0000
> @@ -171,6 +171,9 @@ update_user(struct user_list *head, char
>  {
>       struct user_list *up;
>  
> +     if (secs < 0)
> +             errx(1, "broken record. secs is %lld", secs);
> +
>       for (up = head; up != NULL; up = up->next) {
>               if (strncmp(up->name, name, sizeof (up->name) - 1) == 0) {
>                       up->secs += secs;
> @@ -410,6 +413,8 @@ ac(FILE   *fp)
>               prev = usr.ut_time;
>               if (Flags & AC_D) {
>                       ltm = localtime(&usr.ut_time);
> +                     if (ltm == NULL)
> +                             err(1, "localtime");
>                       if (day >= 0 && day != ltm->tm_yday) {
>                               day = ltm->tm_yday;
>                               /*
> @@ -461,6 +466,8 @@ ac(FILE   *fp)
>  
>       if (Flags & AC_D) {
>               ltm = localtime(&usr.ut_time);
> +             if (ltm == NULL)
> +                     err(1, "localtime");
>               if (day >= 0 && day != ltm->tm_yday) {
>                       /*
>                        * print yesterday's total
> 

Reply via email to