Current-Users archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: bad cd features descriptor



Hi,

On Thu, Jul 21, 2011 at 11:23 AM, Greg Troxel <gdt%ir.bbn.com@localhost> wrote:
>
> I have a USB stick with the "U3" misfeature, which has an emulated
> cdrom.  netbsd-5 crashes soon after this is inserted (when hald tries to
> get features/info, I think).  I looked at my crash dump and added sanity
> checking.  I was unaware of martin's commit:
>
>  revision 1.297
>  date: 2010/01/06 20:16:57;  author: martin;  state: Exp;  lines: +4 -2
>  branches:  1.297.2;
>  Some usb devices come with an internal emulated umass CD drive (containing
>  windows drivers). I have such a device that has 0 features - avoid wrappig
>  features_len to a very big unsiged 32bit number in this case.
>
> and while I think that commit is entirely sound there are more than just
> one input validation problem in the existing code.  I have the following
> diff to netbsd-5 that successfully complains about the device's failure
> to return something reasonable (0 features would ok, but there's an 8
> byte header that is also not returned) instead of crashing.
>
> In addition, I don't understand how the while loop about features_len
> being > 0xffff ever makes sense.  Clearly the point is to do another
> fetch of another buffer's worth, but it's the wrong test.  It cannot
> ever succeed, because features_len is checked to fit in 1024 bytes;
> between that and not understanding, I didn't change it.
>
> My change is essentially a bunch of sanity checks that error out on
> feature reading.
>
> Any objections to me committing this to current (and replacing the test
> in 1.297 with this)?
>
>
> --- cd.c.~1.283.4.3.~   2011-01-13 10:14:57.000000000 -0500
> +++ cd.c        2011-07-21 11:21:36.000000000 -0400
> @@ -3033,34 +3033,75 @@ mmc_getdiscinfo(struct scsipi_periph *pe
>
>                features_len = _4btol(gc->data_len);
>
> +               /*
> +                * features_len is the length of a struct
> +                * scsipi_get_conf_data, which has a header (8 bytes)
> +                * followed by some number of features.
> +                */
> +
> +               if (features_len < 8 || features_len > feat_tbl_len) {
> +                       printf("mmc_getdiscinfo: bad features_len %d (%d)\n",
> +                              features_len, feat_tbl_len);
> +                       goto done_features;
> +               }
> +
> +               /*
> +                * pos is the offset after the 8-byte header, and fpos
> +                * is a pointer to the feature being processed.
> +                */
>                pos  = 0;
>                fpos = &gc->feature_desc[0];
> -               while (pos < features_len - 4) {
> +               /* Check for enough space for a feature header. */
> +               while (pos + 8 + 4 < features_len) {
>                        gcf = (struct scsipi_get_conf_feature *) fpos;
>
>                        feature     = _2btol(gcf->featurecode);
>                        feature_cur = gcf->flags & 1;
>                        feature_len = gcf->additional_length;
>
> +                       /* Check that this feature fits, and is long-aligned. 
> */
> +                       if (feature_len < 0 ||
> +                           8 + pos + 4 + feature_len > feat_tbl_len ||
> +                           (feature_len & 3) != 0) {
> +                               printf("feature %d bad feature_len %d (pos %d 
> feat_tbl_len %d)\n",
> +                                      feature, feature_len, pos, 
> feat_tbl_len);
> +                               goto done_features;
> +                       }
> +
> +                       /* XXX How can this not take feature_len? */
>                        mmc_process_feature(mmc_discinfo,
>                                            feature, feature_cur,
>                                            gcf->feature_dependent);
>
> -                       last_feature = MAX(last_feature, feature);
> -#ifdef DIAGNOSTIC
> -                       /* assert((feature_len & 3) == 0); */
> -                       if ((feature_len & 3) != 0) {
> -                               printf("feature %d having length %d\n",
> -                                       feature, feature_len);
> +                       /*
> +                        * Support a subsequent get_conf_feature
> +                        * starting (in theory) after the one we read.
> +                        * XXX Why is this not just an assignment?  How
> +                        * can we be sure this won't loop?
> +                        */
> +                       if (last_feature < feature) {
> +                               printf("misordered last_feature %d feature 
> %d\n",
> +                                      last_feature, feature);
> +                               goto done_features;
>                        }
> -#endif
> +                       last_feature = MAX(last_feature, feature);
>
>                        pos  += 4 + feature_len;
>                        fpos += 4 + feature_len;
> +
> +#ifdef DIAGNOSTIC
> +                       KASSERT(pos + 8 <= feat_tbl_len);
> +#endif /* DIAGNOSTIC */
on a pure stylish pov., what's the point of these #ifdef ... #endif ?
AFAIK, KASSERT() should already be conditionally defined whether or
not DIAGNOSTIC.

 - Arnaud

> +               }
> +               if (pos + 8 != features_len) {
> +                       printf("excess feature space pos %d features_len 
> %d\n",
> +                              pos, features_len);
>                }
>                /* unlikely to ever grow past our 1kb buffer */
>        } while (features_len >= 0xffff);
>
> +done_features:
> +
>        /*
>         * Fixup CD-RW drives that are on crack.
>         *
>


Home | Main Index | Thread Index | Old Index