On 11/25/23 12:31, Kees Cook wrote:


On November 25, 2023 9:54:28 AM PST, "Gustavo A. R. Silva" 
<[email protected]> wrote:


On 11/24/23 09:28, Gustavo A. R. Silva wrote:


On 11/24/23 04:24, Joey Gouly wrote:
Hi all,

I just hit a boot crash on v6.7-rc2 (arm64, FVP model):

[..]

Checking `struct neighbour`:

     struct neighbour {
         struct neighbour __rcu    *next;
         struct neigh_table    *tbl;
     .. fields ..
         u8            primary_key[0];
     } __randomize_layout;

Due to the `__randomize_layout`, `primary_key` field is being placed before 
`tbl` (actually it's the same address since it's a 0 length array). That means 
the memcpy() corrupts the tbl pointer.

I think I just got unlucky with my CONFIG_RANDSTRUCT seed (I can provide it if 
needed), it doesn't look as if it's a new issue.

It seems the issue is caused by this change that was recently added to -rc2:

commit 1ee60356c2dc ("gcc-plugins: randstruct: Only warn about true flexible 
arrays")

Previously, one-element and zero-length arrays were treated as true flexible 
arrays
(however, they are "fake" flex arrays), and __randomize_layout would leave them
untouched at the end of the struct; the same for proper C99 flex-array members. 
But
after the commit above, that's no longer the case: Only C99 flex-array members 
will
behave correctly (remaining untouched at end of the struct), and the other two 
types
of arrays will be randomized.

mmh... it seems that commit 1ee60356c2dc only prevents one-element arrays from 
being
treated as flex arrays, while the code should still keep zero-length arrays 
untouched:

        if (typesize == NULL_TREE && TYPE_DOMAIN(fieldtype) != NULL_TREE &&
            TYPE_MAX_VALUE(TYPE_DOMAIN(fieldtype)) == NULL_TREE)
                return true;

-       if (typesize != NULL_TREE &&
-           (TREE_CONSTANT(typesize) && (!tree_to_uhwi(typesize) ||
-            tree_to_uhwi(typesize) == tree_to_uhwi(elemsize))))
-               return true;
-

This should be both the 0 and 1 checks. I think the original fix is correct: 
switch to a true flex array.

This code is new to me and I got a bit confused. Thanks for the clarification. 
:)

So, it'd be nice to apply this change:

https://lore.kernel.org/linux-hardening/[email protected]/



Sorry about the confusion.



I couldn't reproduce directly on v6.6 (the offsets for `tbl` and `primary_key` 
didn't overlap).
However I tried changing the zero-length-array to a flexible one:

     +    DECLARE_FLEX_ARRAY(u8, primary_key);
     +    u8        primary_key[0];

Was this line supposed to be "-"?


Then the field offsets ended up overlapping, and I also got the same crash on 
v6.6.

The right approach is to transform the zero-length array into a C99 flex-array 
member,
like this:

diff --git a/include/net/neighbour.h b/include/net/neighbour.h
index 07022bb0d44d..0d28172193fa 100644
--- a/include/net/neighbour.h
+++ b/include/net/neighbour.h
@@ -162,7 +162,7 @@ struct neighbour {
          struct rcu_head         rcu;
          struct net_device       *dev;
          netdevice_tracker       dev_tracker;
-       u8                      primary_key[0];
+       u8                      primary_key[];
   } __randomize_layout;

   struct neigh_ops {

In any case, I think we still should convert [0] to [ ].

I would expect the above to fix the problem. If it doesn't I'll need to take a 
closer look at the plugin...

I think this should fix the issue. Let me go create a proper patch for this.
I'll send it out, shortly.

--
Gustavo

Reply via email to