On Tue, 2023-08-22 at 10:16 +0000, Gerhard Roth wrote:
> On Tue, 2023-08-22 at 11:16 +0200, Martijn van Duren wrote:
> > On Mon, 2023-08-21 at 07:35 +0000, Gerhard Roth wrote:
> > > Hi Martijn,
> > > 
> > > last November you fixed ber.c so that sequences won't generate
> > > an uninitialized subelement.
> > > 
> > > This revealed another bug in ober_scanf_elements(): it couldn't
> > > process sequences with an empty list of subelements. The following
> > > code failed in ober_scanf_elements():
> > > 
> > >         struct ber_element      *root;
> > >         struct ber_element      *sub;
> > > 
> > >         if ((root = ober_add_sequence(NULL)) == NULL)
> > >                 err(1, "ober_add_sequence() failed");
> > > 
> > >         errno = 0;
> > >         if (ober_scanf_elements(root, "{e", &sub))
> > >                 err(1, "ober_scanf_elements() failed");
> > > 
> > >         printf("sub = %p\n", sub);
> > > 
> > > 
> > > The patch below fixes that.
> > > 
> > > Gerhard
> > > 
> > > 
> > > Index: lib/libutil/ber.c
> > > ===================================================================
> > > RCS file: /cvs/src/lib/libutil/ber.c,v
> > > retrieving revision 1.24
> > > diff -u -p -u -p -r1.24 ber.c
> > > --- lib/libutil/ber.c   3 Nov 2022 17:58:10 -0000       1.24
> > > +++ lib/libutil/ber.c   21 Aug 2023 07:24:21 -0000
> > > @@ -700,7 +700,8 @@ ober_scanf_elements(struct ber_element *
> > >  
> > >         va_start(ap, fmt);
> > >         while (*fmt) {
> > > -               if (ber == NULL && *fmt != '$' && *fmt != '}' && *fmt != 
> > > ')')
> > > +               if (ber == NULL && *fmt != '$' && *fmt != '}' && *fmt != 
> > > ')' &&
> > > +                   *fmt != 'e')
> > >                         goto fail;
> > 
> > I'm not sure about this part. An ober_scanf_elements of "{}e" on your
> > example above also fails. The 'e' element might not increment the ber
> > pointer, but I do think it should fail if an expected element is
> > missing.
> 
> You're right. And digging into it, only makes it look worse.
> 
> I think, the 'e' format should behave differently.
> 
> 1) in case of '{e' it is the first element of a sequence.
>       Empty sequences are allowed and we should not fail but
>       set the argument to NULL instead.
> 
> 2) in all other cases, 'e' is the next element in the list.
>       And here we should fail if there are less elements in the
>       list than expected.
> 
> Below is an updated diff that fixes the problem by remembering
> whether the last traversal was via 'be_sub' (fsub == 1) or 'be_next'
> and then behaves differently. Not very elegant although.

I'm not sure yet. Do you have a particular usecase for this?
Usually you would just do something like
ober_scanf_elements(elm, "e{", seq);
if (seq->be_sub != NULL) ...

> 
> > 
> > >                 switch (*fmt++) {
> > >                 case '$':
> > > @@ -797,7 +798,7 @@ ober_scanf_elements(struct ber_element *
> > >                         if (ber->be_encoding != BER_TYPE_SEQUENCE &&
> > >                             ber->be_encoding != BER_TYPE_SET)
> > >                                 goto fail;
> > > -                       if (ber->be_sub == NULL || level >= _MAX_SEQ-1)
> > > +                       if (level >= _MAX_SEQ-1)
> > 
> > This part is OK martijn@
> > 
> > >                                 goto fail;
> > >                         parent[++level] = ber;
> > >                         ber = ber->be_sub;
> > > 
> > 
> 
> Index: lib/libutil/ber.c
> ===================================================================
> RCS file: /cvs/src/lib/libutil/ber.c,v
> retrieving revision 1.24
> diff -u -p -u -p -r1.24 ber.c
> --- lib/libutil/ber.c 3 Nov 2022 17:58:10 -0000       1.24
> +++ lib/libutil/ber.c 22 Aug 2023 10:10:43 -0000
> @@ -695,12 +695,14 @@ ober_scanf_elements(struct ber_element *
>       off_t                   *pos;
>       struct ber_oid          *o;
>       struct ber_element      *parent[_MAX_SEQ], **e;
> +     int                      fsub = 0;
>  
>       memset(parent, 0, sizeof(struct ber_element *) * _MAX_SEQ);
>  
>       va_start(ap, fmt);
>       while (*fmt) {
> -             if (ber == NULL && *fmt != '$' && *fmt != '}' && *fmt != ')')
> +             if (ber == NULL && *fmt != '$' && *fmt != '}' && *fmt != ')' &&
> +                 *fmt != 'e')
>                       goto fail;
>               switch (*fmt++) {
>               case '$':
> @@ -732,6 +734,8 @@ ober_scanf_elements(struct ber_element *
>               case 'e':
>                       e = va_arg(ap, struct ber_element **);
>                       *e = ber;
> +                     if (fsub)
> +                             goto fail;
>                       ret++;
>                       continue;
>               case 'E':
> @@ -797,10 +801,11 @@ ober_scanf_elements(struct ber_element *
>                       if (ber->be_encoding != BER_TYPE_SEQUENCE &&
>                           ber->be_encoding != BER_TYPE_SET)
>                               goto fail;
> -                     if (ber->be_sub == NULL || level >= _MAX_SEQ-1)
> +                     if (level >= _MAX_SEQ-1)
>                               goto fail;
>                       parent[++level] = ber;
>                       ber = ber->be_sub;
> +                     fsub = 1;
>                       ret++;
>                       continue;
>               case '}':
> @@ -808,6 +813,7 @@ ober_scanf_elements(struct ber_element *
>                       if (level < 0 || parent[level] == NULL)
>                               goto fail;
>                       ber = parent[level--];
> +                     fsub = 0;
>                       ret++;
>                       break;
>               default:
> 
> 

Reply via email to