Received: by 2002:ac0:a594:0:0:0:0:0 with SMTP id m20-v6csp4260976imm; Mon, 14 May 2018 05:10:16 -0700 (PDT) X-Google-Smtp-Source: AB8JxZp063I/PpVVedDCRRJ0+UWuOF6cJcCkZqBhqg9LJtPOS2yfnBgmxYWaex/mzacrUPmLHtPt X-Received: by 2002:a17:902:189:: with SMTP id b9-v6mr9829019plb.204.1526299816254; Mon, 14 May 2018 05:10:16 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1526299816; cv=none; d=google.com; s=arc-20160816; b=cn8iE2cQZjFlRlaf77uSRU6Y4gg73bEqi/QkeOrtYH8ttTStwe6qH/GzL7YmgT8NuV TMCbnNf1M/VLR3ZkNUMTQVyD8xcPi6oIYCofecJgj5N6jxcoreAO6vaJh5lPMM0DMJhe aQ9Cm6nKQOC67Eur4sBqGTY8xen7ZalS4vCMnMxEMpXmjj6lurKHze/o/U/AOPCP+OqD 4gWjpLJWb7/Mp9b7jUilPCikKFgxO65kq62L2gCXlXsD9Pdt92axEDJUMZZmxxLjQG7W yC933OcEyUv6z+LECmxbyuwBHmRE2CFMgx3x77ERxje7ZJUIAYSr3KvJSTrrkvh+UBHC Z8hQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:cc:to:subject:message-id:date:from :references:in-reply-to:mime-version:dkim-signature :arc-authentication-results; bh=u3ySkxxTGXS+EfxD9jsZRGab9A11bM0P2edr9IniYMo=; b=Q8ek954tLze+IlRWftsWN9X3DfCJF4pTkl5obVY6qCknbJCp/ngIwq5b+PM1pJ6PAZ XMKIy7Rb43Hs83V1ryx/hUjmkDy+mHYNzkjP57i0iCv1MnGpcxk2eI9Gt7g2v12XnW7u BleIsTky0be/s5Q6FXr3Yq9dEVWWoVQIkC+pVFHmwQ4DJ1TEm3xh1EUTSd02TbS1cTiK ETCNjQQYgSnt4Ve/GcRKEr8spdCDwaNBIakuNDHRfzdock25mv6OlekB/IzK/FGTF7Dg vhoPgTC38sbdy33PCWbOumDF+3kkl1OSLrHvTLH3rjOkdZzJ5Go5vs0j4xFGLMWXqD4E Xmog== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=aKSI3bvl; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id a68-v6si9542993pli.158.2018.05.14.05.10.01; Mon, 14 May 2018 05:10:16 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=aKSI3bvl; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753157AbeENMI2 (ORCPT + 99 others); Mon, 14 May 2018 08:08:28 -0400 Received: from mail-it0-f50.google.com ([209.85.214.50]:55643 "EHLO mail-it0-f50.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752888AbeENMIY (ORCPT ); Mon, 14 May 2018 08:08:24 -0400 Received: by mail-it0-f50.google.com with SMTP id 144-v6so9809850iti.5 for ; Mon, 14 May 2018 05:08:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=mime-version:in-reply-to:references:from:date:message-id:subject:to :cc; bh=u3ySkxxTGXS+EfxD9jsZRGab9A11bM0P2edr9IniYMo=; b=aKSI3bvlrLGXd2uiibsQfAOROdoMllsJXnU8qXhHx4VNyNkzcY5hP3zoDfjmm8uq7y Vk70IwJWgobWVGyUrialktUBE4dT2kesjsLoN+KKvbQ7Y/5QkvHBZLygZBv7UzN6zBQg QLdjZn9zgnrO8VIVY1KyGXZZiM2c5YV9teixE= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:in-reply-to:references:from:date :message-id:subject:to:cc; bh=u3ySkxxTGXS+EfxD9jsZRGab9A11bM0P2edr9IniYMo=; b=fYmpwY/sCsJmUxdkE6aowwNV6WNO+OQxyz7zsD4IbPLBHGhrfVZxQ5QYHqEkjxWQyY YMQzluI2IxBAGpcjsjMrAo0GYlwuhwEucbXL3TSNkukjcz20dAg1Zs/r0nulON/xrbyT puM6uMlUMPEynR5vkWdVeZ3b1B2Kk4N1gVGvBj/95J2PILH2PQJzm34kHYjmSRKfQVOQ d1s5FJ/FjlYUcNqHQTwffiDT43mFmFFBw+APtR6I5+AebZ8oWFweraTM9chU6qeLs8PT AOvnakBZFzxSOl72CIRSZ4Gj8oHrgsLn9pkK32hs4vlkAwxuXyL/TWxSygYGAJJHmCyc vaRQ== X-Gm-Message-State: ALKqPwctMIfDrqE997JKFfQj+v5NGkmSj/7/dYD9iTAw/hNniC23HWDJ qh03vCaG3mWvV9ZvdHlFsPGccvb/He2JaWgTtXLzow== X-Received: by 2002:a24:e103:: with SMTP id n3-v6mr9300357ith.68.1526299703221; Mon, 14 May 2018 05:08:23 -0700 (PDT) MIME-Version: 1.0 Received: by 10.107.187.134 with HTTP; Mon, 14 May 2018 05:08:22 -0700 (PDT) In-Reply-To: <20180508131547.44366-1-andriy.shevchenko@linux.intel.com> References: <20180508131547.44366-1-andriy.shevchenko@linux.intel.com> From: Ard Biesheuvel Date: Mon, 14 May 2018 14:08:22 +0200 Message-ID: Subject: Re: [PATCH v2] device property: Get rid of union aliasing To: Andy Shevchenko Cc: Greg Kroah-Hartman , Linux Kernel Mailing List , linux-efi@vger.kernel.org, Sakari Ailus , Mika Westerberg , "Rafael J . Wysocki" , ACPI Devel Maling List , Lukas Wunner Content-Type: text/plain; charset="UTF-8" Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On 8 May 2018 at 15:15, Andy Shevchenko wrote: > The commit > > 318a19718261 ("device property: refactor built-in properties support") > > went way too far and brought a union aliasing. Partially revert it here > to get rid of union aliasing. > > Note, Apple properties support is still utilizing this trick. > > What union aliasing is? > ~~~~~~~~~~~~~~~~~~~~~~~ > The C99 standard in section 6.2.5 paragraph 20 defines union type as > "an overlapping nonempty set of member objects". It also states in > section 6.7.2.1 paragraph 14 that "the value of at most one of the > members can be stored in a union object at any time'. > > Union aliasing is a type punning mechanism using union members to store > as one type and read back as another. > > Why it's not good? > ~~~~~~~~~~~~~~~~~~ > Section 6.2.6.1 paragraph 6 says that a union object may not be a trap > representation, although its member objects may be. > > Meanwhile annex J.1 says that "the value of a union member other than > the last one stored into" is unspecified [removed in C11]. > > In TC3, a footnote is added which specifies that accessing a member of a > union other than the last one stored causes "the object representation" > to be re-interpreted in the new type and specifically refers to this as > "type punning". This conflicts to some degree with Annex J.1. > > While it's working in Linux with GCC, the use of union members to do > type punning is not clear area in the C standard and might lead to > unspecified behaviour. > > More information is available in this [1] blog post. > > [1]: https://davmac.wordpress.com/2010/02/26/c99-revisited/ > > Signed-off-by: Andy Shevchenko For the EFI change Acked-by: Ard Biesheuvel > --- > > - extend commit message to explain what union aliasing is and why it's not good > > drivers/base/property.c | 99 ++++++++++++++++++++----- > drivers/firmware/efi/apple-properties.c | 8 +- > include/linux/property.h | 52 +++++++------ > 3 files changed, 112 insertions(+), 47 deletions(-) > > diff --git a/drivers/base/property.c b/drivers/base/property.c > index 8f205f6461ed..de19d7cc073b 100644 > --- a/drivers/base/property.c > +++ b/drivers/base/property.c > @@ -56,6 +56,67 @@ pset_prop_get(const struct property_set *pset, const char *name) > return NULL; > } > > +static const void *property_get_pointer(const struct property_entry *prop) > +{ > + switch (prop->type) { > + case DEV_PROP_U8: > + if (prop->is_array) > + return prop->pointer.u8_data; > + return &prop->value.u8_data; > + case DEV_PROP_U16: > + if (prop->is_array) > + return prop->pointer.u16_data; > + return &prop->value.u16_data; > + case DEV_PROP_U32: > + if (prop->is_array) > + return prop->pointer.u32_data; > + return &prop->value.u32_data; > + case DEV_PROP_U64: > + if (prop->is_array) > + return prop->pointer.u64_data; > + return &prop->value.u64_data; > + default: > + if (prop->is_array) > + return prop->pointer.str; > + return &prop->value.str; > + } > +} > + > +static void property_set_pointer(struct property_entry *prop, const void *pointer) > +{ > + switch (prop->type) { > + case DEV_PROP_U8: > + if (prop->is_array) > + prop->pointer.u8_data = pointer; > + else > + prop->value.u8_data = *((u8 *)pointer); > + break; > + case DEV_PROP_U16: > + if (prop->is_array) > + prop->pointer.u16_data = pointer; > + else > + prop->value.u16_data = *((u16 *)pointer); > + break; > + case DEV_PROP_U32: > + if (prop->is_array) > + prop->pointer.u32_data = pointer; > + else > + prop->value.u32_data = *((u32 *)pointer); > + break; > + case DEV_PROP_U64: > + if (prop->is_array) > + prop->pointer.u64_data = pointer; > + else > + prop->value.u64_data = *((u64 *)pointer); > + break; > + default: > + if (prop->is_array) > + prop->pointer.str = pointer; > + else > + prop->value.str = pointer; > + } > +} > + > static const void *pset_prop_find(const struct property_set *pset, > const char *propname, size_t length) > { > @@ -65,10 +126,7 @@ static const void *pset_prop_find(const struct property_set *pset, > prop = pset_prop_get(pset, propname); > if (!prop) > return ERR_PTR(-EINVAL); > - if (prop->is_array) > - pointer = prop->pointer.raw_data; > - else > - pointer = &prop->value.raw_data; > + pointer = property_get_pointer(prop); > if (!pointer) > return ERR_PTR(-ENODATA); > if (length > prop->length) > @@ -698,16 +756,17 @@ EXPORT_SYMBOL_GPL(fwnode_property_get_reference_args); > > static void property_entry_free_data(const struct property_entry *p) > { > + const void *pointer = property_get_pointer(p); > size_t i, nval; > > if (p->is_array) { > - if (p->is_string && p->pointer.str) { > + if (p->type == DEV_PROP_STRING && p->pointer.str) { > nval = p->length / sizeof(const char *); > for (i = 0; i < nval; i++) > kfree(p->pointer.str[i]); > } > - kfree(p->pointer.raw_data); > - } else if (p->is_string) { > + kfree(pointer); > + } else if (p->type == DEV_PROP_STRING) { > kfree(p->value.str); > } > kfree(p->name); > @@ -716,7 +775,7 @@ static void property_entry_free_data(const struct property_entry *p) > static int property_copy_string_array(struct property_entry *dst, > const struct property_entry *src) > { > - char **d; > + const char **d; > size_t nval = src->length / sizeof(*d); > int i; > > @@ -734,40 +793,44 @@ static int property_copy_string_array(struct property_entry *dst, > } > } > > - dst->pointer.raw_data = d; > + dst->pointer.str = d; > return 0; > } > > static int property_entry_copy_data(struct property_entry *dst, > const struct property_entry *src) > { > + const void *pointer = property_get_pointer(src); > + const void *new; > int error; > > if (src->is_array) { > if (!src->length) > return -ENODATA; > > - if (src->is_string) { > + if (src->type == DEV_PROP_STRING) { > error = property_copy_string_array(dst, src); > if (error) > return error; > + new = dst->pointer.str; > } else { > - dst->pointer.raw_data = kmemdup(src->pointer.raw_data, > - src->length, GFP_KERNEL); > - if (!dst->pointer.raw_data) > + new = kmemdup(pointer, src->length, GFP_KERNEL); > + if (!new) > return -ENOMEM; > } > - } else if (src->is_string) { > - dst->value.str = kstrdup(src->value.str, GFP_KERNEL); > - if (!dst->value.str && src->value.str) > + } else if (src->type == DEV_PROP_STRING) { > + new = kstrdup(src->value.str, GFP_KERNEL); > + if (!new && src->value.str) > return -ENOMEM; > } else { > - dst->value.raw_data = src->value.raw_data; > + new = pointer; > } > > dst->length = src->length; > dst->is_array = src->is_array; > - dst->is_string = src->is_string; > + dst->type = src->type; > + > + property_set_pointer(dst, new); > > dst->name = kstrdup(src->name, GFP_KERNEL); > if (!dst->name) > diff --git a/drivers/firmware/efi/apple-properties.c b/drivers/firmware/efi/apple-properties.c > index adaa9a3714b9..b2c2d5a45f91 100644 > --- a/drivers/firmware/efi/apple-properties.c > +++ b/drivers/firmware/efi/apple-properties.c > @@ -13,6 +13,9 @@ > * > * You should have received a copy of the GNU General Public License > * along with this program; if not, see . > + * > + * FIXME: The approach is still based on union aliasing and should be > + * replaced by a proper resource provider. > */ > > #define pr_fmt(fmt) "apple-properties: " fmt > @@ -96,12 +99,13 @@ static void __init unmarshal_key_value_pairs(struct dev_header *dev_header, > entry[i].name = key; > entry[i].length = val_len - sizeof(val_len); > entry[i].is_array = !!entry[i].length; > - entry[i].pointer.raw_data = ptr + key_len + sizeof(val_len); > + entry[i].type = DEV_PROP_U8; > + entry[i].pointer.u8_data = ptr + key_len + sizeof(val_len); > > if (dump_properties) { > dev_info(dev, "property: %s\n", entry[i].name); > print_hex_dump(KERN_INFO, pr_fmt(), DUMP_PREFIX_OFFSET, > - 16, 1, entry[i].pointer.raw_data, > + 16, 1, entry[i].pointer.u8_data, > entry[i].length, true); > } > > diff --git a/include/linux/property.h b/include/linux/property.h > index 2eea4b310fc2..ac8a1ebc4c1b 100644 > --- a/include/linux/property.h > +++ b/include/linux/property.h > @@ -178,7 +178,7 @@ static inline int fwnode_property_read_u64(const struct fwnode_handle *fwnode, > * @name: Name of the property. > * @length: Length of data making up the value. > * @is_array: True when the property is an array. > - * @is_string: True when property is a string. > + * @type: Type of the data in unions. > * @pointer: Pointer to the property (an array of items of the given type). > * @value: Value of the property (when it is a single item of the given type). > */ > @@ -186,10 +186,9 @@ struct property_entry { > const char *name; > size_t length; > bool is_array; > - bool is_string; > + enum dev_prop_type type; > union { > union { > - const void *raw_data; > const u8 *u8_data; > const u16 *u16_data; > const u32 *u32_data; > @@ -197,7 +196,6 @@ struct property_entry { > const char * const *str; > } pointer; > union { > - unsigned long long raw_data; > u8 u8_data; > u16 u16_data; > u32 u32_data; > @@ -213,55 +211,55 @@ struct property_entry { > * and structs. > */ > > -#define PROPERTY_ENTRY_INTEGER_ARRAY(_name_, _type_, _val_) \ > -(struct property_entry) { \ > - .name = _name_, \ > - .length = ARRAY_SIZE(_val_) * sizeof(_type_), \ > - .is_array = true, \ > - .is_string = false, \ > - { .pointer = { ._type_##_data = _val_ } }, \ > +#define PROPERTY_ENTRY_INTEGER_ARRAY(_name_, _type_, _Type_, _val_) \ > +(struct property_entry) { \ > + .name = _name_, \ > + .length = ARRAY_SIZE(_val_) * sizeof(_type_), \ > + .is_array = true, \ > + .type = DEV_PROP_##_Type_, \ > + { .pointer = { ._type_##_data = _val_ } }, \ > } > > #define PROPERTY_ENTRY_U8_ARRAY(_name_, _val_) \ > - PROPERTY_ENTRY_INTEGER_ARRAY(_name_, u8, _val_) > + PROPERTY_ENTRY_INTEGER_ARRAY(_name_, u8, U8, _val_) > #define PROPERTY_ENTRY_U16_ARRAY(_name_, _val_) \ > - PROPERTY_ENTRY_INTEGER_ARRAY(_name_, u16, _val_) > + PROPERTY_ENTRY_INTEGER_ARRAY(_name_, u16, U16, _val_) > #define PROPERTY_ENTRY_U32_ARRAY(_name_, _val_) \ > - PROPERTY_ENTRY_INTEGER_ARRAY(_name_, u32, _val_) > + PROPERTY_ENTRY_INTEGER_ARRAY(_name_, u32, U32, _val_) > #define PROPERTY_ENTRY_U64_ARRAY(_name_, _val_) \ > - PROPERTY_ENTRY_INTEGER_ARRAY(_name_, u64, _val_) > + PROPERTY_ENTRY_INTEGER_ARRAY(_name_, u64, U64, _val_) > > #define PROPERTY_ENTRY_STRING_ARRAY(_name_, _val_) \ > (struct property_entry) { \ > .name = _name_, \ > .length = ARRAY_SIZE(_val_) * sizeof(const char *), \ > .is_array = true, \ > - .is_string = true, \ > + .type = DEV_PROP_STRING, \ > { .pointer = { .str = _val_ } }, \ > } > > -#define PROPERTY_ENTRY_INTEGER(_name_, _type_, _val_) \ > -(struct property_entry) { \ > - .name = _name_, \ > - .length = sizeof(_type_), \ > - .is_string = false, \ > - { .value = { ._type_##_data = _val_ } }, \ > +#define PROPERTY_ENTRY_INTEGER(_name_, _type_, _Type_, _val_) \ > +(struct property_entry) { \ > + .name = _name_, \ > + .length = sizeof(_type_), \ > + .type = DEV_PROP_##_Type_, \ > + { .value = { ._type_##_data = _val_ } }, \ > } > > #define PROPERTY_ENTRY_U8(_name_, _val_) \ > - PROPERTY_ENTRY_INTEGER(_name_, u8, _val_) > + PROPERTY_ENTRY_INTEGER(_name_, u8, U8, _val_) > #define PROPERTY_ENTRY_U16(_name_, _val_) \ > - PROPERTY_ENTRY_INTEGER(_name_, u16, _val_) > + PROPERTY_ENTRY_INTEGER(_name_, u16, U16, _val_) > #define PROPERTY_ENTRY_U32(_name_, _val_) \ > - PROPERTY_ENTRY_INTEGER(_name_, u32, _val_) > + PROPERTY_ENTRY_INTEGER(_name_, u32, U32, _val_) > #define PROPERTY_ENTRY_U64(_name_, _val_) \ > - PROPERTY_ENTRY_INTEGER(_name_, u64, _val_) > + PROPERTY_ENTRY_INTEGER(_name_, u64, U64, _val_) > > #define PROPERTY_ENTRY_STRING(_name_, _val_) \ > (struct property_entry) { \ > .name = _name_, \ > .length = sizeof(_val_), \ > - .is_string = true, \ > + .type = DEV_PROP_STRING, \ > { .value = { .str = _val_ } }, \ > } > > -- > 2.17.0 >