lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date: Sat, 30 Sep 2023 14:56:01 -0700
From: Kees Cook <keescook@...omium.org>
To: Kent Overstreet <kent.overstreet@...ux.dev>
Cc: Andrew Morton <akpm@...ux-foundation.org>,
	Brian Foster <bfoster@...hat.com>,
	kernel test robot <lkp@...el.com>,
	Linux Memory Management List <linux-mm@...ck.org>,
	linux-fsdevel@...r.kernel.org, linux-bcachefs@...r.kernel.org,
	linux-hardening@...r.kernel.org
Subject: Re: [linux-next:master] BUILD REGRESSION
 df964ce9ef9fea10cf131bf6bad8658fde7956f6

Hi Kent,

Andrew pointed this out to me, and it's a FORTIFY issue under a W=1 build:

On Sat, Sep 30, 2023 at 01:25:34PM +0800, kernel test robot wrote:
> tree/branch: https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git master
> branch HEAD: df964ce9ef9fea10cf131bf6bad8658fde7956f6  Add linux-next specific files for 20230929
> 
> Error/Warning reports:
> 
> [...]
> https://lore.kernel.org/oe-kbuild-all/202309192314.VBsjiIm5-lkp@intel.com

   fs/bcachefs/extents.c: In function 'bch2_bkey_append_ptr':
   include/linux/fortify-string.h:57:33: warning: writing 8 bytes into a region of size 0 [-Wstringop-overflow=]
      57 | #define __underlying_memcpy     __builtin_memcpy
         |                                 ^
   include/linux/fortify-string.h:648:9: note: in expansion of macro '__underlying_memcpy'
     648 |         __underlying_##op(p, q, __fortify_size);                        \
         |         ^~~~~~~~~~~~~
   include/linux/fortify-string.h:693:26: note: in expansion of macro '__fortify_memcpy_chk'
     693 | #define memcpy(p, q, s)  __fortify_memcpy_chk(p, q, s,                  \
         |                          ^~~~~~~~~~~~~~~~~~~~
   fs/bcachefs/extents.c:235:17: note: in expansion of macro 'memcpy'
     235 |                 memcpy((void *) &k->v + bkey_val_bytes(&k->k),
         |                 ^~~~~~
   fs/bcachefs/bcachefs_format.h:287:33: note: destination object 'v' of size 0
     287 |                 struct bch_val  v;
         |                                 ^

The problem here is the struct bch_val is explicitly declared as a
zero-sized array, so the compiler becomes unhappy. :) Converting bch_val
to a flexible array will just kick the can down the road, since this is
going to run into -Wflex-array-member-not-at-end soon too since bch_val
overlaps with other structures:

struct bch_inode_v3 {
        struct bch_val          v;

        __le64                  bi_journal_seq;
...
};

As a container_of() target, this is fine -- leave it a zero-sized
array. The problem is using it as a destination for memcpy, etc, since
the compiler will believe it to be 0 sized. Instead, we need to impart
a type of some kind so that the compiler can actually unambiguously
reason about sizes. The memcpy() in the warning is targeting bch_val,
so I think the best fix is to correctly handle the different types.

So just to have everything in front of me, here's a summary of what I'm
seeing in the code:

struct bkey {
        /* Size of combined key and value, in u64s */
        __u8            u64s;
...
};

/* Empty placeholder struct, for container_of() */
struct bch_val {
        __u64           __nothing[0];
};

struct bkey_i {
        __u64                   _data[0];

        struct bkey     k;
        struct bch_val  v;
};

static inline void bch2_bkey_append_ptr(struct bkey_i *k, struct bch_extent_ptr ptr)
{
        EBUG_ON(bch2_bkey_has_device(bkey_i_to_s(k), ptr.dev));

        switch (k->k.type) {
        case KEY_TYPE_btree_ptr:
        case KEY_TYPE_btree_ptr_v2:
        case KEY_TYPE_extent:
                EBUG_ON(bkey_val_u64s(&k->k) >= BKEY_EXTENT_VAL_U64s_MAX);

                ptr.type = 1 << BCH_EXTENT_ENTRY_ptr;

                memcpy((void *) &k->v + bkey_val_bytes(&k->k),
                       &ptr,
                       sizeof(ptr));
                k->k.u64s++;
                break;
        default:
                BUG();
        }
}

So this is appending u64s into the region that start with bkey_i. Could
this gain a u64 flexible array?

struct bkey_i {
        __u64                   _data[0];

        struct bkey     k;
        struct bch_val  v;
	__u64		ptrs[];
};

Then the memcpy() could be just a direct assignment:

		k->ptrs[bkey_val_u64s(&k->k)] = (u64)ptr;
                k->k.u64s++;

Alternatively, perhaps struct bkey could be the one to grow this flexible
array, and then it could eventually be tracked with __counted_by (but
not today since it expects to count the array element count, not a whole
struct size):

struct bkey {
        /* Size of combined key and value, in u64s */
        __u8            u64s;
...
	__u64		data[] __counted_by(.u64s - BKEY_U64s);
};

And bch_val could be dropped...

Then the memcpy becomes:

                k->k.u64s++;
                k->k.data[bkey_val_u64s(&k->k)] = (u64)ptr;

It just seems like there is a lot of work happening that could really
just type casting or unions...

What do you think?

-- 
Kees Cook

Powered by blists - more mailing lists