[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
kernel/997: Crash in chkdq (/usr/src/sys/ufs/ufs/ufs_quota.c) on alpha
>Number: 997
>Category: kernel
>Synopsis: Crash in chkdq (/usr/src/sys/ufs/ufs/ufs_quota.c) on alpha
>Confidential: no
>Severity: critical
>Priority: medium
>Responsible: bugs
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Thu Dec 2 08:50:01 MST 1999
>Last-Modified:
>Originator: Goran Bengtson
>Organization:
Chalmers Univ. of Technology, Sweden
>Release: OpenBSD 2.6
>Environment:
System : OpenBSD 2.6
Architecture: OpenBSD.alpha
Machine : alpha
>Description:
A kernel (2.6-current, or 2.6) built with QUOTA and FFS_SOFTUPDATES,
running on an alpha with mounted filesystems using softupdates will
crash at line 137 in file /usr/src/sys/ufs/ufs/ufs_quota.c (routine
chkdq).
Line is (ufs_quota.c rev. 1.8):
if ((flags & FORCE) == 0 && cred->cr_uid != 0) {
The reason is the change in /usr/src/sys/ufs/ufs/dinode.h on
Nov 17'th when di_blocks in struct dinode was changed from int32_t
to u_int32_t.
The reason behind the change may be good, but di_blocks is used
(directly or as result of defines in /usr/src/sys/ufs/ufs/inode.h,
e.g. "#define i_ffs_blocks i_din.ffs_din.di_blocks"
together with signed variable in several kernel filesystem-related
routines. This may lead to unexpexted behaviour unless the code is
checked, at least if the value of di_blocks is large enough to
motivate it beeing unsigned.
One such problem cause the crash above, where chkdq was called from
line 237 in file /usr/src/sys/ufs/ffs/ffs_inode.c (rev. 1.10).
Line is:
(void) chkdq(oip, -oip->i_ffs_blocks, NOCRED, 0);
The second formal parameter of chkdq is declared to be a long. Since
oip->i_ffs_blocks is u_int32_t, negating it may not give the expected
result on any platform, and on an alpha, converting the result to a
long will actually result in a positive argument passed to chkdq,
independent of the value of oip->i_ffs_blocks (se code below). The
crash is caused by this positive argument in combination with
NOCRED == ((struct ucred *)-1). chkdq assumes a positive second
argument only to occur together with a valid cred-pointer.
Not that I'm an ANSI-C expert, but I think the result of negating a
u_int32_t is (allowed to be) implementation dependent even on
platforms where (sizeof long) == sizeof(u_int32_t).
Just to illustrate the conversion problem:
#include <stdio.h>
#include <stdlib.h>
main()
{
printf("%016lx\n",(long)-(unsigned int)1);
printf("%016lx\n",-(long)(unsigned int)1);
}
gives (on the alpha):
00000000ffffffff
ffffffffffffffff
>How-To-Repeat:
Using the system... update's periodic activity usually triggers the
crash before the system is completely up.
>Fix:
For the alpha, changing the line (line 237 in
/usr/src/sys/ufs/ffs/ffs_inode.c, rev. 1.10) to:
(void) chkdq(oip, -(long)oip->i_ffs_blocks, NOCRED, 0);
fix the problem. For platforms with sizeof(long) == sizeof(u_int32_t)
this should work as long as oip->i_ffs_blocks < 2147483648.
However, since di_blocks is used together with signed variables in
other routines, changing di_blocks back to int32_t may be a better
solution until all uses of di_blocks are examined.
>Audit-Trail:
>Unformatted: