From: Kees Cook <kees@kernel.org>
To: Vincent Mailhol <mailhol.vincent@wanadoo.fr>
Cc: David Laight <David.Laight@aculab.com>,
"Gustavo A . R . Silva" <gustavoars@kernel.org>,
linux-hardening@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH v2] overflow: optimize struct_size() calculation
Date: Tue, 10 Sep 2024 17:36:47 -0700 [thread overview]
Message-ID: <202409101729.C242EEC@keescook> (raw)
In-Reply-To: <20240910024952.1590207-1-mailhol.vincent@wanadoo.fr>
On Tue, Sep 10, 2024 at 11:49:52AM +0900, Vincent Mailhol wrote:
> If the offsetof() of a given flexible array member (fam) is smaller
> than the sizeof() of the containing struct, then the struct_size()
> macro reports a size which is too big.
>
> This occurs when the two conditions below are met:
>
> - there are padding bytes after the penultimate member (the member
> preceding the fam)
> - the alignment of the fam is less than or equal to the penultimate
> member's alignment
>
> In that case, the fam overlaps with the padding bytes of the
> penultimate member. This behaviour is not captured in the current
> struct_size() macro, potentially resulting in an overestimated size.
>
> Below example illustrates the issue:
>
> struct s {
> u64 foo;
> u32 count;
> u8 fam[] __counted_by(count);
> };
>
> Assuming a 64 bits architecture:
>
> - there are 4 bytes of padding after s.count (the penultimate
> member)
> - sizeof(struct s) is 16 bytes
> - the offset of s.fam is 12 bytes
> - the alignment of s.fam is 1 byte
>
> The sizes are as below:
>
> s.count current struct_size() actual size
> -----------------------------------------------------------------------
> 0 16 16
> 1 17 16
> 2 18 16
> 3 19 16
> 4 20 16
> 5 21 17
> . . .
> . . .
> . . .
> n sizeof(struct s) + n max(sizeof(struct s),
> offsetof(struct s, fam) + n)
>
> Change struct_size() from this pseudo code logic:
>
> sizeof(struct s) + sizeof(*s.fam) * s.count
>
> to that pseudo code logic:
>
> max(sizeof(struct s), offsetof(struct s, fam) + sizeof(*s.fam) * s.count)
>
> Here, the lowercase max*() macros can cause struct_size() to return a
> non constant integer expression which would break the DEFINE_FLEX()
> macro by making it declare a variable length array. Because of that,
> use the unsafe MAX() macro only if the expression is constant and use
> the safer max() otherwise.
>
> Reference: ISO/IEC 9899:2018 §6.7.2.1 "Structure and union specifiers" ¶18
>
> Signed-off-by: Vincent Mailhol <mailhol.vincent@wanadoo.fr>
> ---
>
> I also tried to think of whether the current struct_size() macro could
> be a security issue.
>
> The only example I can think of is if someone manually allocates the
> exact size but then use the current struct_size() macro.
>
> For example (reusing the struct s from above):
>
> u32 count = 5;
> struct s *s = kalloc(offsetof(typeof(*s), fam) + count);
> s->count = count;
> memset(s, 0, struct_size(s, fam, count)); /* 4 bytes buffer overflow */
>
> If we have concerns that above pattern may actually exist, then this
> patch should also go to stable. I personally think that the above is a
> bit convoluted and, as such, I only suggest this patch to go to next.
Yeah, this "over-estimation" has come up before, and my thinking as been
that while the above situation is certainly possible (but unlikely),
I've worried that the reverse situation (after this patch) is
potentially worse, where we allocate very precisely, but then manually
index too far:
struct s *s = kmalloc(struct_size(s, fam, count), gfp);
typeof(*s->fam) *element;
element = (void *)s + sizeof(*s);
for (i = 0; i < count; i++)
element[i] = ...;
And for a max 7 byte savings, I'm concerned we can get bit much worse in
the above situation. It *should* be unlikely, but I've especially seen a
lot of manually calculated games especially for structs that have
effectively multiple trailing flexible arrays (wifi likes to do this,
for example).
So while I don't have very concrete evidence, my sensation is that we're
in a more defensive position leaving it over-estimated.
--
Kees Cook
next prev parent reply other threads:[~2024-09-11 0:36 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-09-10 2:49 [PATCH v2] overflow: optimize struct_size() calculation Vincent Mailhol
2024-09-11 0:36 ` Kees Cook [this message]
2024-09-11 4:45 ` Vincent MAILHOL
2024-09-11 10:22 ` David Laight
[not found] ` <CAMZ6Rq+6EKoFEHMZhp_2dq2DPEP6zZgzDy0M3tegKK9wOphA6g@mail.gmail.com>
2024-09-11 16:46 ` David Laight
2024-09-13 4:45 ` Vincent Mailhol
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=202409101729.C242EEC@keescook \
--to=kees@kernel.org \
--cc=David.Laight@aculab.com \
--cc=gustavoars@kernel.org \
--cc=linux-hardening@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mailhol.vincent@wanadoo.fr \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox