svn commit: r217714 - head/sbin/fdisk

Warner Losh imp at bsdimp.com
Mon Jan 24 06:40:51 UTC 2011


Maxim,

In case it wasn't clear from Bruce's long explaination, parts of this 
are technically wrong and need to be reverted or fixed.

Warner

On 01/22/2011 00:47, Bruce Evans wrote:
> On Sat, 22 Jan 2011, Maxim Sobolev wrote:
>
>> Log:
>>  Warn user when value entered is greated than the amount supported
>>  by the MBR for the given parameter and set that parameter to the
>>  maximum value instead of just truncating the most significant part
>>  silently.
>>
>>  Could happen for example if the capacity of the device is more
>>  than 2TB, so that the number of sectors is greater than 2Mib.
>>
>>  MFC after:    1 month
>
> This improves some things, but has a high density of style bugs (many
> lines expanded beyond 80 columns...), and 2 errors in the limits.
>
> What's this 2Mib limit?  ibits are strange units for sector counts.
> The limit should be 4G, but there is probably lots of brokenness starting
> at 2G.  fdisk is quite broken at 2G, since it uses ints too much.
>
>> Modified: head/sbin/fdisk/fdisk.c
>> ============================================================================== 
>>
>> --- head/sbin/fdisk/fdisk.c    Sat Jan 22 01:48:12 2011    (r217713)
>> +++ head/sbin/fdisk/fdisk.c    Sat Jan 22 05:21:20 2011    (r217714)
>> @@ -586,9 +586,9 @@ change_part(int i)
>>             tcyl = DPCYL(partp->dp_scyl,partp->dp_ssect);
>>             thd = partp->dp_shd;
>>             tsec = DPSECT(partp->dp_ssect);
>> -            Decimal("beginning cylinder", tcyl, tmp);
>> -            Decimal("beginning head", thd, tmp);
>> -            Decimal("beginning sector", tsec, tmp);
>> +            Decimal("beginning cylinder", tcyl, tmp, 
>> sizeof(partp->dp_scyl));
>
> Cylinder numbers are 10 bits in the MBR.  They don't fit in the 
> starting and
> ending cylinder fields, so 2 bits of them are put in the corresponding 
> sector
> fields.  The sizeof here is always 1, giving only 8 bits and thus a wrong
> limit of 256.
>
>> +            Decimal("beginning head", thd, tmp, sizeof(partp->dp_shd));
>> +            Decimal("beginning sector", tsec, tmp, 
>> sizeof(partp->dp_ssect));
>
> Sector numbers are only 6 bits in the MBR.  The sizeof here is always 1,
> giving 8 bits and thus a wrong limit of 256.
>
>>             partp->dp_scyl = DOSCYL(tcyl);
>>             partp->dp_ssect = DOSSECT(tsec,tcyl);
>>             partp->dp_shd = thd;
>
> The DOSSECT() macro handles the details of merging cylinder bits into
> sector field.s
>
> Since these are the values to be written directly into the MBR, it is
> correct to limit them and not blindly truncate them.  Non-blindly 
> changing
> them is little better.  They should just be rejected.
>
> There is also a lower limit on sector numbers.  Sector 0 is invalid.
>
> Similarly for ending C/H/S numbers, except the breakage is larger in
> practice.  Starting cylinder numbers are usually 0, but ending cylinder
> numbers are usually 1023 and now you can't edit them to anything above
> 255, including null changes from 1023 and changes from invalid values
> to the least invalid value of 1023.  Older disks/devices/software may
> actually have between 256 and 1023 cylinders and need an MBR which 
> matches.
>
>> @@ -647,7 +647,7 @@ change_active(int which)
>> setactive:
>>     do {
>>         new = active;
>> -        Decimal("active partition", new, tmp);
>> +        Decimal("active partition", new, tmp, 0);
>>         if (new < 1 || new > 4) {
>>             printf("Active partition number must be in range 1-4."
>>                     "  Try again.\n");
>
> This has a lower limit too, and does the necessary and correct range
> checking for itself.  It now has to pass a bogus upper limit of 0 to
> Decimal() to stop checking there.  Decimal() should probably handle
> both limits like this does.
>
>> @@ -677,9 +677,9 @@ get_params_to_use()
>>     {
>>         do
>>         {
>> -            Decimal("BIOS's idea of #cylinders", dos_cyls, tmp);
>> -            Decimal("BIOS's idea of #heads", dos_heads, tmp);
>> -            Decimal("BIOS's idea of #sectors", dos_sectors, tmp);
>> +            Decimal("BIOS's idea of #cylinders", dos_cyls, tmp, 0);
>> +            Decimal("BIOS's idea of #heads", dos_heads, tmp, 0);
>> +            Decimal("BIOS's idea of #sectors", dos_sectors, tmp, 0);
>>             dos_cylsecs = dos_heads * dos_sectors;
>>             print_params();
>>         }
>
> I originally thought that the user values had to be accepted fairly
> blindly since they are multiplied out in places like here.  Now I see 
> that
> nothing has changed for these, but it probably should be changed to at
> least warn about them.
>
> There seems to be mo multiplication out involving dos_cyls.  There are
> warnings about it being out of range all over the place.  The size of the
> disk in sectors is mostly given not by these "dos" variables, but by
> the "undosed" variables cyls,  heads and sectors.  get_params() 
> initializes
> all the "dos" variables badly by setting them to the same as to "undosed"
> variables in all cases.  This is guranteed to give dos_cyls > 1023 on any
> hard disk less than about 12 years old.
>
> Note that the limits for the above are 1 more than the limits for 
> starting
> and ending C/H/S, except for dos_sectors, since these values are counts
> while the the others are offsets from 0 (except starting/ending sectors
> are offset froms 1).
>
>> @@ -915,11 +915,16 @@ ok(const char *str)
>> }
>>
>> static int
>> -decimal(const char *str, int *num, int deflt)
>> +decimal(const char *str, int *num, int deflt, int size)
>> {
>> -    int acc = 0, c;
>> +    long long acc = 0, maxval;
>
> Long long is an abomination, and there is no reason to use any type 
> larger
> than uint32_t here.
>
>> +    int c;
>>     char *cp;
>>
>> +    if (size == 0) {
>> +        size = sizeof(*num);
>> +    }
>> +    maxval = (long long)1 << (size * 8);
>
> This is not the maximum value, but a limit value which is 1 greater
> than the maximum.  OK, you need a type larger than uint32_t to go 1
> greater than its maximum, and you get that when `size' is 4.  A better
> interface passing the maximum (_not_ 1 greater) wouldn't have that
> problem.  And decimal() still returns int, so overflow occurs long
> before that UNIT32_MAX+1LL.  Sector numbers are 32 bits unsigned, so
> int is inadequate to describe them, but this program uses int for them
> in many other places, e.g., 'disksecs = cyls * heads * sectors' first
> has an overflowing multiplication and then assigns the overflowed value
> to "int disksecs" where it won't fit :-(.
>
>>     while (1) {
>>         printf("Supply a decimal value for \"%s\" [%d] ", str, deflt);
>>         fflush(stdout);
>> @@ -935,14 +940,20 @@ decimal(const char *str, int *num, int d
>>         if (!c)
>>             return 0;
>>         while ((c = *cp++)) {
>> -            if (c <= '9' && c >= '0')
>> -                acc = acc * 10 + c - '0';
>> -            else
>> +            if (c <= '9' && c >= '0') {
>> +                if (acc < maxval)
>> +                    acc = acc * 10 + c - '0';
>> +            } else
>>                 break;
>>         }
>
> Ugh, didn't people learn to use strtol() instead of home made parsing of
> numbers with overflow errors in 1985?  Now it has enough range checking
> to prevent overflow of (acc * 10) provided maxval is limited to
> LONG_LONG_MAX / 10.  Might need a slightly lower limit for adding c.
>
> decimal() doesn't seem to support negative numbers, so nothing needs to
> check for them.
>
> Bruce
>
>
>



More information about the svn-src-all mailing list