From: Kees Cook <kees@kernel.org>
To: Dan Carpenter <dan.carpenter@linaro.org>
Cc: David Lechner <dlechner@baylibre.com>,
Jonathan Cameron <jic23@jic23.retrosnub.co.uk>,
linux-iio@vger.kernel.org
Subject: Re: [bug report] iio: pressure: bmp280: drop sensor_data array
Date: Fri, 9 May 2025 09:58:13 -0700 [thread overview]
Message-ID: <202505090942.48EBF01B@keescook> (raw)
In-Reply-To: <aB2Xam2JQ_eU9Bat@stanley.mountain>
On Fri, May 09, 2025 at 08:49:30AM +0300, Dan Carpenter wrote:
> Let me add Kees to the CC list because he'll want to know this as well.
>
> On Wed, May 07, 2025 at 08:33:07AM -0500, David Lechner wrote:
> > On 5/7/25 2:41 AM, Dan Carpenter wrote:
> > > On Wed, May 07, 2025 at 07:35:27AM +0100, Jonathan Cameron wrote:
> > >>
> > >>
> > >> On 6 May 2025 19:35:08 BST, Dan Carpenter <dan.carpenter@linaro.org> wrote:
> > >>> On Tue, May 06, 2025 at 09:25:00AM -0500, David Lechner wrote:
> > >>>> On 5/6/25 7:32 AM, Dan Carpenter wrote:
> > >>>>> Hello David Lechner,
> > >>>>>
> > >>>>> Commit 4e6c3c4801a6 ("iio: pressure: bmp280: drop sensor_data array")
> > >>>>> from Apr 22, 2025 (linux-next), leads to the following Smatch static
> > >>>>> checker warning:
> > >>>>>
> > >>>>> drivers/iio/pressure/bmp280-core.c:1280 bme280_trigger_handler()
> > >>>>> warn: check that 'buffer' doesn't leak information (struct has a hole after 'comp_humidity')
> > >>>>>
> > >>>>> drivers/iio/pressure/bmp280-core.c
> > >>>>> 1225 static irqreturn_t bme280_trigger_handler(int irq, void *p)
> > >>>>> 1226 {
> > >>>>> 1227 struct iio_poll_func *pf = p;
> > >>>>> 1228 struct iio_dev *indio_dev = pf->indio_dev;
> > >>>>> 1229 struct bmp280_data *data = iio_priv(indio_dev);
> > >>>>> 1230 u32 adc_temp, adc_press, adc_humidity;
> > >>>>> 1231 s32 t_fine;
> > >>>>> 1232 struct {
> > >>>>> 1233 u32 comp_press;
> > >>>>> 1234 s32 comp_temp;
> > >>>>> 1235 u32 comp_humidity;
> > >>>>> 1236 aligned_s64 timestamp;
> > >>>>>
> > >>>>> There is a 4 byte hole between comp_humidity and timestamp.
> > >>>>
> > >>>> Yes, this was the intention of this patch.
> > >>>>
> > >>>>>
> > >>>>> 1237 } buffer;
> > >>>>> 1238 int ret;
> > >>>>> 1239
> > >>>>
> > >>>> ...
> > >>>>
> > >>>>> 1279
> > >>>>> --> 1280 iio_push_to_buffers_with_ts(indio_dev, &buffer, sizeof(buffer),
> > >>>>> ^^^^^^^^^^^^^^^^^^^^^^^
> > >>>>> So I believe it leads to an information leaks here.
> > >>>>
> > >>>> Aha, so we should e.g. do memset() to fill the hole first.
> > >>>>
> > >>>
> > >>> That works, or you could initialize it with = { }.
> > >>
> > >> I tried to track this down the other day.
> > >> What are compilers guaranteeing around
> > >> that vs { 0 } and holes? The c spec has only recently standardised on { }.
> > >>
> > >> I'd love to stop using memset for these.
> > >
> > > The rule in the C standard is that if the initializer sets every struct
> > > member then it will NOT zero out struct holes. But if there are any
> > > unset struct members then the holes are zeroed. So = { } will always
> > > work. You really have to try hard to invent a scenario where = { 0 }
> > > won't fill the struct holes (a one member struct with a weird alignment).
> > >
> > > regards,
> > > dan carpenter
> > >
> >
> > I was curious about this too and came across a blog post [1] that claims that
> > with clang compiler and certain optimization levels, { } and { 0 } still aren't
> > good enough, which is why I went with the memset().
> >
> > [1]: https://interrupt.memfault.com/blog/c-struct-padding-initialization
>
> Huh...
>
> "Strategy 2, explicitly setting each struct member". This isn't
> "supposed" to initialize struct holes according to the C current
> standard.
>
> It's discouraging that = { 0 } and { } don't work. -O1 is not supported
> in the kernel so that's not an emergency. I don't know about -Os?
tl;dr: Use { }.
I didn't think -Os was supported either?
The stackinit KUnit tests will do all these checks, FWIW. It's a little
difficult to decode the results, but this is consistent with the
findings. Looking at a similar case to above, the "small_hole" struct
test:
struct test_small_hole {
size_t one;
char two;
/* 3 byte padding hole here. */
int three;
unsigned long four;
};
Tested with various initializers:
#define INIT_STRUCT_none(var_type) /**/
#define INIT_STRUCT_zero(var_type) = { }
#define INIT_STRUCT_old_zero(var_type) = { 0 }
#define __static_partial { .two = 0, }
#define __static_all { .one = 0, \
.two = 0, \
.three = 0, \
.four = 0, \
}
#define __dynamic_partial { .two = arg->two, }
#define __dynamic_all { .one = arg->one, \
.two = arg->two, \
.three = arg->three, \
.four = arg->four, \
}
#define __runtime_partial var.two = 0
#define __runtime_all var.one = 0; \
var.two = 0; \
var.three = 0; \
var.four = 0
We can see a default build (-O2), "PASSED" means all zero, and "SKIPPED"
means "not all zero, but we expected that given the Kconfig involved":
(Running "./tools/testing/kunit/kunit.py run stackinit")
[09:44:47] [PASSED] test_small_hole_zero
[09:44:47] [PASSED] test_small_hole_old_zero
[09:44:47] [PASSED] test_small_hole_dynamic_partial
[09:44:47] [PASSED] test_small_hole_assigned_dynamic_partial
[09:44:47] [PASSED] test_small_hole_static_partial
[09:44:47] [PASSED] test_small_hole_assigned_static_partial
These are "= { }", "= { 0 }", and partial member initialization.
[09:44:47] [SKIPPED] test_small_hole_static_all
[09:44:47] [SKIPPED] test_small_hole_assigned_static_all
[09:44:47] [SKIPPED] test_small_hole_dynamic_all
[09:44:47] [SKIPPED] test_small_hole_assigned_dynamic_all
These are the full member initialization, which, yes, leaves the padding
uninitialized. :(
[09:44:47] [SKIPPED] test_small_hole_runtime_partial
[09:44:47] [SKIPPED] test_small_hole_runtime_all
These set members from runtime values instead of static values, and
padding is left uninitialized.
[09:44:47] [SKIPPED] test_small_hole_none
No init, obviously left uninitialized
[09:44:47] [PASSED] test_small_hole_assigned_copy
This is a full object copy (from a source with initialized padding),
so the result is also initialized.
Building with CONFIG_INIT_STACK_ALL_ZERO=y changes all this, of course.
In that case, everything passes:
./tools/testing/kunit/kunit.py run --kconfig_add CONFIG_INIT_STACK_ALL_ZERO=y stackinit
--
Kees Cook
prev parent reply other threads:[~2025-05-09 16:58 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-05-06 12:32 [bug report] iio: pressure: bmp280: drop sensor_data array Dan Carpenter
2025-05-06 14:25 ` David Lechner
2025-05-06 18:35 ` Dan Carpenter
2025-05-07 6:35 ` Jonathan Cameron
2025-05-07 7:41 ` Dan Carpenter
2025-05-07 13:33 ` David Lechner
2025-05-09 5:49 ` Dan Carpenter
2025-05-09 10:01 ` Dan Carpenter
2025-05-09 16:58 ` Kees Cook [this message]
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=202505090942.48EBF01B@keescook \
--to=kees@kernel.org \
--cc=dan.carpenter@linaro.org \
--cc=dlechner@baylibre.com \
--cc=jic23@jic23.retrosnub.co.uk \
--cc=linux-iio@vger.kernel.org \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.