Re: Fwd: iosm: detected field-spanning write for XMM7360

From: Kees Cook
Date: Tue Jun 20 2023 - 15:32:42 EST


[regressions list to Bcc]

On Tue, Jun 20, 2023 at 11:12:40AM +0200, Linux regression tracking (Thorsten Leemhuis) wrote:
> On 20.06.23 10:44, Bagas Sanjaya wrote:
> > I notice a regression report on Bugzilla [1]. Quoting from it:
> [...]
> >> [Sa Jun 17 20:10:09 2023] memcpy: detected field-spanning write (size 16) of single field "&adth->dg" at drivers/net/wwan/iosm/iosm_ipc_mux_codec.c:852 (size 8)
> [...]
> > [1]: https://bugzilla.kernel.org/show_bug.cgi?id=217569

This looks like a legitimate bug.

Here are the structures used by drivers/net/wwan/iosm/iosm_ipc_mux_codec.c
leading up to the memcpy() triggering this warning at line 852, with
commentary from me added:

struct mux_adb {
struct sk_buff *dest_skb;
u8 *buf; // <- unbounded array
...
};

/**
* struct mux_adth - Structure of the Aggregated Datagram Table Header.
...
* @dg: datagramm table with variable length // variable length you say? :)
*/
struct mux_adth {
...
struct mux_adth_dg dg; // <- destination of memcpy
};

struct mux_adth_dg {
__le32 datagram_index;
__le16 datagram_length;
u8 service_class;
u8 reserved;
}; // 8 byte structure

static void ipc_mux_ul_encode_adth(struct iosm_mux *ipc_mux,
struct mux_adb *ul_adb, int *out_offset)
{
...
struct mux_adth *adth;
...
// Assignment of fixed-sized structure on an
// unbounded string (i.e. minimum size
// constraint is now defined by structure
// layout, which is a good thing.)
adth = (struct mux_adth *)&ul_adb->buf[offset];
...
// This appears to be preparing for having
// _multiple_ "dg" structs at the end of
// struct mux_adth_dg, not just 1. (And, if so,
// should be using the struct_size(), or really,
// given the later subtraction, the flex_size(),
// helper.)
adth_dg_size = offsetof(struct mux_adth, dg) +
ul_adb->dg_count[i] * sizeof(*dg);
...
adth_dg_size -= offsetof(struct mux_adth, dg);
memcpy(&adth->dg, ul_adb->dg[i], adth_dg_size);


&adth->dg is 8 bytes, so the warning message is correct. However, it
seems like "ul_adb->dg_count[i]" contains a _count_ of "dg" structures
to copy, and, in the reported case, is "2".

The fix appears to be adjusting struct mux_adth with:

- struct mux_adth_dg dg;
+ struct mux_adth_dg dg[];

But, since this is an implicit "1-element array to flexible array"
conversion[1], we need to double-check "sizeof" uses.

I only see 3 cases of sizeof(struct mux_adth). This one removes the
"extra" "dg" element for bounds checking:

if (le16_to_cpu(adth->table_length) < (sizeof(struct mux_adth) -
sizeof(struct mux_adth_dg)))

This one adds it back in after subtracting 1 too many:

nr_of_dg = (le16_to_cpu(adth->table_length) -
sizeof(struct mux_adth) +
sizeof(struct mux_adth_dg)) /
sizeof(struct mux_adth_dg);

As does this one:

nr_of_dg = (le16_to_cpu(adth->table_length) -
sizeof(struct mux_adth) +
sizeof(struct mux_adth_dg)) /
sizeof(struct mux_adth_dg);

So they can be simplified to avoid the extra math.

I'll send a patch...

-Kees

[1] https://github.com/KSPP/linux/issues/79

--
Kees Cook