[Bug 271310] potential NULL dereference in fsck_ffs's changeino()

From: <bugzilla-noreply_at_freebsd.org>
Date: Mon, 08 May 2023 08:55:23 UTC

            Bug ID: 271310
           Summary: potential NULL dereference in fsck_ffs's changeino()
           Product: Base System
           Version: CURRENT
          Hardware: Any
                OS: Any
            Status: New
          Severity: Affects Some People
          Priority: ---
         Component: bin
          Assignee: bugs@FreeBSD.org
          Reporter: rtm@lcs.mit.edu

Created attachment 242049
  --> https://bugs.freebsd.org/bugzilla/attachment.cgi?id=242049&action=edit
a broken FS image that causes a NULL dereference in fsck's changeino()

Running fsck_ffs -y on the attached image causes this line in
sbin/fsck_ffs/dir.c changeino() to dereference NULL:

                getinoinfo(dir)->i_depth = depth;

I think the reason is that the link count of the i-node in question
(i-number 3, a directory) has its high bit set, so that it looks
negative in di_nlink (which is int16_t) in this code in checkinode()
in pass1.c:

        if (mode == IFDIR) {
                if (DIP(dp, di_size) == 0) {
                        inoinfo(inumber)->ino_state = DCLEAR;
                } else if (DIP(dp, di_nlink) <= 0) {
                        inoinfo(inumber)->ino_state = DZLINK;
                } else {
                        inoinfo(inumber)->ino_state = DSTATE;
                        cacheino(dp, inumber);

As a result, ino_state is set to DZLINK, and cacheino() is not called,
which is why getinoinfo() eventually returns NULL.

Later, in pass4(), I think the expectation is that ino_linkcnt will be
zero, and clri() will be called. But in fact ino_linkcnt is not zero
(it's negative), so adjust() is called, which effectively expects
cacheino() to have been called.

                        case DZLINK:
                                if (inoinfo(inumber)->ino_linkcnt == 0) {
                                        clri(&idesc, "UNREF", 1);
                                /* fall through */

                        case FSTATE:
                        case DFOUND:
                                n = inoinfo(inumber)->ino_linkcnt;
                                if (n) {
                                        adjust(&idesc, (short)n);

A backtrace from fsck_ffs -y fsck13a.img :

Program received signal SIGSEGV, Segmentation fault.
Address not mapped to object.
0x000000000020b165 in changeino (dir=3, name=0x201ecf "..", newnum=4, depth=2)
at dir.c:712
712                     getinoinfo(dir)->i_depth = depth;
(gdb) where
#0  0x000000000020b165 in changeino (dir=3, name=0x201ecf "..", newnum=4, 
    depth=2) at dir.c:712
#1  0x000000000020a553 in linkup (orphan=3, parentdir=0, name=0x0) at dir.c:664
#2  0x0000000000209acd in adjust (idesc=0x7fffffffe7d0, lcnt=-32765)
    at dir.c:470
#3  0x000000000022025e in pass4 () at pass4.c:94
#4  0x0000000000219ae9 in checkfilesys (filesys=0x7fffffffed79 "junk")
    at main.c:484
#5  0x0000000000218f42 in main (argc=1, argv=0x7fffffffea28) at main.c:210

You are receiving this mail because:
You are the assignee for the bug.