diff mbox series

[1/2] iio: adc: ad4130: Fix comparison of channel setups

Message ID 584b8bae1ad158fc86bd1cd9bd3dcae54b58093e.1738258777.git.u.kleine-koenig@baylibre.com (mailing list archive)
State Changes Requested
Headers show
Series iio: adc: ad*: Fix comparisons using memcmp | expand

Commit Message

Uwe Kleine-König Jan. 30, 2025, 5:45 p.m. UTC
Checking the binary representation of two structs (of the same type)
for equality doesn't have the same semantic as comparing all members for
equality. The former might find a difference where the latter doesn't in
the presence of padding or when ambiguous types like float or bool are
involved. (Floats typically have different representations for single
values, like -0.0 vs +0.0, or 0.5 * 2² vs 0.25 * 2³. The type bool has
at least 8 bits and the raw values 1 and 2 (probably) both evaluate to
true, but memcmp finds a difference.)

When searching for a channel that already has the configuration we need,
the comparison by member is the one that is needed.

Convert the comparison accordingly to compare the members one after
another. Also add a BUILD_BUG guard to (somewhat) ensure that when
struct ad4130_setup_info is expanded, the comparison is adapted, too.

Fixes: 62094060cf3a ("iio: adc: ad4130: add AD4130 driver")
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@baylibre.com>
---
 drivers/iio/adc/ad4130.c | 33 +++++++++++++++++++++++++++++++--
 1 file changed, 31 insertions(+), 2 deletions(-)

Comments

Andy Shevchenko Jan. 30, 2025, 6:08 p.m. UTC | #1
On Thu, Jan 30, 2025 at 06:45:01PM +0100, Uwe Kleine-König wrote:
> Checking the binary representation of two structs (of the same type)
> for equality doesn't have the same semantic as comparing all members for
> equality. The former might find a difference where the latter doesn't in
> the presence of padding or when ambiguous types like float or bool are
> involved. (Floats typically have different representations for single
> values, like -0.0 vs +0.0, or 0.5 * 2² vs 0.25 * 2³. The type bool has
> at least 8 bits and the raw values 1 and 2 (probably) both evaluate to
> true, but memcmp finds a difference.)
> 
> When searching for a channel that already has the configuration we need,
> the comparison by member is the one that is needed.
> 
> Convert the comparison accordingly to compare the members one after
> another. Also add a BUILD_BUG guard to (somewhat) ensure that when

Why do you use BUILD_BUG_ON() instead of static_assert()?

> struct ad4130_setup_info is expanded, the comparison is adapted, too.
Andy Shevchenko Jan. 30, 2025, 6:14 p.m. UTC | #2
On Thu, Jan 30, 2025 at 06:45:01PM +0100, Uwe Kleine-König wrote:

...

> +	BUILD_BUG_ON(sizeof(*a) !=
> +		     sizeof(struct {
> +				    unsigned int iout0_val;
> +				    unsigned int iout1_val;
> +				    unsigned int burnout;
> +				    unsigned int pga;
> +				    unsigned int fs;
> +				    u32 ref_sel;
> +				    enum ad4130_filter_mode filter_mode;
> +				    bool ref_bufp;
> +				    bool ref_bufm;
> +			    }));

Is I shuffle the fields (for whatever reason) this may give false positive
warnings. I think this BUILD_BUG_ON() is unreliable and ugly looking
(static_assert() won't help much here either), so on the second though I think
it's better to simply add a comments in both places (here and near to the
structure definition) to explain that these needs to be in sync.

> +	if (a->iout0_val != b->iout0_val ||
> +	    a->iout1_val != b->iout1_val ||
> +	    a->burnout != b->burnout ||
> +	    a->pga != b->pga ||
> +	    a->fs != b->fs ||
> +	    a->ref_sel != b->ref_sel ||
> +	    a->filter_mode != b->filter_mode ||
> +	    a->ref_bufp != b->ref_bufp ||
> +	    a->ref_bufm != b->ref_bufm)
> +		return false;
> +
> +	return true;
diff mbox series

Patch

diff --git a/drivers/iio/adc/ad4130.c b/drivers/iio/adc/ad4130.c
index de32cc9d18c5..ae321df426b5 100644
--- a/drivers/iio/adc/ad4130.c
+++ b/drivers/iio/adc/ad4130.c
@@ -591,6 +591,36 @@  static irqreturn_t ad4130_irq_handler(int irq, void *private)
 	return IRQ_HANDLED;
 }
 
+static bool ad4130_setup_info_eq(struct ad4130_setup_info *a,
+				 struct ad4130_setup_info *b)
+{
+	BUILD_BUG_ON(sizeof(*a) !=
+		     sizeof(struct {
+				    unsigned int iout0_val;
+				    unsigned int iout1_val;
+				    unsigned int burnout;
+				    unsigned int pga;
+				    unsigned int fs;
+				    u32 ref_sel;
+				    enum ad4130_filter_mode filter_mode;
+				    bool ref_bufp;
+				    bool ref_bufm;
+			    }));
+
+	if (a->iout0_val != b->iout0_val ||
+	    a->iout1_val != b->iout1_val ||
+	    a->burnout != b->burnout ||
+	    a->pga != b->pga ||
+	    a->fs != b->fs ||
+	    a->ref_sel != b->ref_sel ||
+	    a->filter_mode != b->filter_mode ||
+	    a->ref_bufp != b->ref_bufp ||
+	    a->ref_bufm != b->ref_bufm)
+		return false;
+
+	return true;
+}
+
 static int ad4130_find_slot(struct ad4130_state *st,
 			    struct ad4130_setup_info *target_setup_info,
 			    unsigned int *slot, bool *overwrite)
@@ -604,8 +634,7 @@  static int ad4130_find_slot(struct ad4130_state *st,
 		struct ad4130_slot_info *slot_info = &st->slots_info[i];
 
 		/* Immediately accept a matching setup info. */
-		if (!memcmp(target_setup_info, &slot_info->setup,
-			    sizeof(*target_setup_info))) {
+		if (ad4130_setup_info_eq(target_setup_info, &slot_info->setup)) {
 			*slot = i;
 			return 0;
 		}