Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

device property: Get rid of union aliasing

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, all Apple properties are considered as u8 arrays. To get a value
of any of them the caller must use device_property_read_u8_array().

What's union aliasing?
~~~~~~~~~~~~~~~~~~~~~~

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 <andriy.shevchenko@linux.intel.com>
Acked-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

authored by

Andy Shevchenko and committed by
Rafael J. Wysocki
63dcc709 67b8d5c7

+117 -47
+86 -18
drivers/base/property.c
··· 56 56 return NULL; 57 57 } 58 58 59 + static const void *property_get_pointer(const struct property_entry *prop) 60 + { 61 + switch (prop->type) { 62 + case DEV_PROP_U8: 63 + if (prop->is_array) 64 + return prop->pointer.u8_data; 65 + return &prop->value.u8_data; 66 + case DEV_PROP_U16: 67 + if (prop->is_array) 68 + return prop->pointer.u16_data; 69 + return &prop->value.u16_data; 70 + case DEV_PROP_U32: 71 + if (prop->is_array) 72 + return prop->pointer.u32_data; 73 + return &prop->value.u32_data; 74 + case DEV_PROP_U64: 75 + if (prop->is_array) 76 + return prop->pointer.u64_data; 77 + return &prop->value.u64_data; 78 + case DEV_PROP_STRING: 79 + if (prop->is_array) 80 + return prop->pointer.str; 81 + return &prop->value.str; 82 + default: 83 + return NULL; 84 + } 85 + } 86 + 87 + static void property_set_pointer(struct property_entry *prop, const void *pointer) 88 + { 89 + switch (prop->type) { 90 + case DEV_PROP_U8: 91 + if (prop->is_array) 92 + prop->pointer.u8_data = pointer; 93 + else 94 + prop->value.u8_data = *((u8 *)pointer); 95 + break; 96 + case DEV_PROP_U16: 97 + if (prop->is_array) 98 + prop->pointer.u16_data = pointer; 99 + else 100 + prop->value.u16_data = *((u16 *)pointer); 101 + break; 102 + case DEV_PROP_U32: 103 + if (prop->is_array) 104 + prop->pointer.u32_data = pointer; 105 + else 106 + prop->value.u32_data = *((u32 *)pointer); 107 + break; 108 + case DEV_PROP_U64: 109 + if (prop->is_array) 110 + prop->pointer.u64_data = pointer; 111 + else 112 + prop->value.u64_data = *((u64 *)pointer); 113 + break; 114 + case DEV_PROP_STRING: 115 + if (prop->is_array) 116 + prop->pointer.str = pointer; 117 + else 118 + prop->value.str = pointer; 119 + break; 120 + default: 121 + break; 122 + } 123 + } 124 + 59 125 static const void *pset_prop_find(const struct property_set *pset, 60 126 const char *propname, size_t length) 61 127 { ··· 131 65 prop = pset_prop_get(pset, propname); 132 66 if (!prop) 133 67 return ERR_PTR(-EINVAL); 134 - if (prop->is_array) 135 - pointer = prop->pointer.raw_data; 136 - else 137 - pointer = &prop->value.raw_data; 68 + pointer = property_get_pointer(prop); 138 69 if (!pointer) 139 70 return ERR_PTR(-ENODATA); 140 71 if (length > prop->length) ··· 761 698 762 699 static void property_entry_free_data(const struct property_entry *p) 763 700 { 701 + const void *pointer = property_get_pointer(p); 764 702 size_t i, nval; 765 703 766 704 if (p->is_array) { 767 - if (p->is_string && p->pointer.str) { 705 + if (p->type == DEV_PROP_STRING && p->pointer.str) { 768 706 nval = p->length / sizeof(const char *); 769 707 for (i = 0; i < nval; i++) 770 708 kfree(p->pointer.str[i]); 771 709 } 772 - kfree(p->pointer.raw_data); 773 - } else if (p->is_string) { 710 + kfree(pointer); 711 + } else if (p->type == DEV_PROP_STRING) { 774 712 kfree(p->value.str); 775 713 } 776 714 kfree(p->name); ··· 780 716 static int property_copy_string_array(struct property_entry *dst, 781 717 const struct property_entry *src) 782 718 { 783 - char **d; 719 + const char **d; 784 720 size_t nval = src->length / sizeof(*d); 785 721 int i; 786 722 ··· 798 734 } 799 735 } 800 736 801 - dst->pointer.raw_data = d; 737 + dst->pointer.str = d; 802 738 return 0; 803 739 } 804 740 805 741 static int property_entry_copy_data(struct property_entry *dst, 806 742 const struct property_entry *src) 807 743 { 744 + const void *pointer = property_get_pointer(src); 745 + const void *new; 808 746 int error; 809 747 810 748 if (src->is_array) { 811 749 if (!src->length) 812 750 return -ENODATA; 813 751 814 - if (src->is_string) { 752 + if (src->type == DEV_PROP_STRING) { 815 753 error = property_copy_string_array(dst, src); 816 754 if (error) 817 755 return error; 756 + new = dst->pointer.str; 818 757 } else { 819 - dst->pointer.raw_data = kmemdup(src->pointer.raw_data, 820 - src->length, GFP_KERNEL); 821 - if (!dst->pointer.raw_data) 758 + new = kmemdup(pointer, src->length, GFP_KERNEL); 759 + if (!new) 822 760 return -ENOMEM; 823 761 } 824 - } else if (src->is_string) { 825 - dst->value.str = kstrdup(src->value.str, GFP_KERNEL); 826 - if (!dst->value.str && src->value.str) 762 + } else if (src->type == DEV_PROP_STRING) { 763 + new = kstrdup(src->value.str, GFP_KERNEL); 764 + if (!new && src->value.str) 827 765 return -ENOMEM; 828 766 } else { 829 - dst->value.raw_data = src->value.raw_data; 767 + new = pointer; 830 768 } 831 769 832 770 dst->length = src->length; 833 771 dst->is_array = src->is_array; 834 - dst->is_string = src->is_string; 772 + dst->type = src->type; 773 + 774 + property_set_pointer(dst, new); 835 775 836 776 dst->name = kstrdup(src->name, GFP_KERNEL); 837 777 if (!dst->name)
+6 -2
drivers/firmware/efi/apple-properties.c
··· 13 13 * 14 14 * You should have received a copy of the GNU General Public License 15 15 * along with this program; if not, see <http://www.gnu.org/licenses/>. 16 + * 17 + * Note, all properties are considered as u8 arrays. 18 + * To get a value of any of them the caller must use device_property_read_u8_array(). 16 19 */ 17 20 18 21 #define pr_fmt(fmt) "apple-properties: " fmt ··· 99 96 entry[i].name = key; 100 97 entry[i].length = val_len - sizeof(val_len); 101 98 entry[i].is_array = !!entry[i].length; 102 - entry[i].pointer.raw_data = ptr + key_len + sizeof(val_len); 99 + entry[i].type = DEV_PROP_U8; 100 + entry[i].pointer.u8_data = ptr + key_len + sizeof(val_len); 103 101 104 102 if (dump_properties) { 105 103 dev_info(dev, "property: %s\n", entry[i].name); 106 104 print_hex_dump(KERN_INFO, pr_fmt(), DUMP_PREFIX_OFFSET, 107 - 16, 1, entry[i].pointer.raw_data, 105 + 16, 1, entry[i].pointer.u8_data, 108 106 entry[i].length, true); 109 107 } 110 108
+25 -27
include/linux/property.h
··· 178 178 * @name: Name of the property. 179 179 * @length: Length of data making up the value. 180 180 * @is_array: True when the property is an array. 181 - * @is_string: True when property is a string. 181 + * @type: Type of the data in unions. 182 182 * @pointer: Pointer to the property (an array of items of the given type). 183 183 * @value: Value of the property (when it is a single item of the given type). 184 184 */ ··· 186 186 const char *name; 187 187 size_t length; 188 188 bool is_array; 189 - bool is_string; 189 + enum dev_prop_type type; 190 190 union { 191 191 union { 192 - const void *raw_data; 193 192 const u8 *u8_data; 194 193 const u16 *u16_data; 195 194 const u32 *u32_data; ··· 196 197 const char * const *str; 197 198 } pointer; 198 199 union { 199 - unsigned long long raw_data; 200 200 u8 u8_data; 201 201 u16 u16_data; 202 202 u32 u32_data; ··· 211 213 * and structs. 212 214 */ 213 215 214 - #define PROPERTY_ENTRY_INTEGER_ARRAY(_name_, _type_, _val_) \ 215 - (struct property_entry) { \ 216 - .name = _name_, \ 217 - .length = ARRAY_SIZE(_val_) * sizeof(_type_), \ 218 - .is_array = true, \ 219 - .is_string = false, \ 220 - { .pointer = { ._type_##_data = _val_ } }, \ 216 + #define PROPERTY_ENTRY_INTEGER_ARRAY(_name_, _type_, _Type_, _val_) \ 217 + (struct property_entry) { \ 218 + .name = _name_, \ 219 + .length = ARRAY_SIZE(_val_) * sizeof(_type_), \ 220 + .is_array = true, \ 221 + .type = DEV_PROP_##_Type_, \ 222 + { .pointer = { ._type_##_data = _val_ } }, \ 221 223 } 222 224 223 225 #define PROPERTY_ENTRY_U8_ARRAY(_name_, _val_) \ 224 - PROPERTY_ENTRY_INTEGER_ARRAY(_name_, u8, _val_) 226 + PROPERTY_ENTRY_INTEGER_ARRAY(_name_, u8, U8, _val_) 225 227 #define PROPERTY_ENTRY_U16_ARRAY(_name_, _val_) \ 226 - PROPERTY_ENTRY_INTEGER_ARRAY(_name_, u16, _val_) 228 + PROPERTY_ENTRY_INTEGER_ARRAY(_name_, u16, U16, _val_) 227 229 #define PROPERTY_ENTRY_U32_ARRAY(_name_, _val_) \ 228 - PROPERTY_ENTRY_INTEGER_ARRAY(_name_, u32, _val_) 230 + PROPERTY_ENTRY_INTEGER_ARRAY(_name_, u32, U32, _val_) 229 231 #define PROPERTY_ENTRY_U64_ARRAY(_name_, _val_) \ 230 - PROPERTY_ENTRY_INTEGER_ARRAY(_name_, u64, _val_) 232 + PROPERTY_ENTRY_INTEGER_ARRAY(_name_, u64, U64, _val_) 231 233 232 234 #define PROPERTY_ENTRY_STRING_ARRAY(_name_, _val_) \ 233 235 (struct property_entry) { \ 234 236 .name = _name_, \ 235 237 .length = ARRAY_SIZE(_val_) * sizeof(const char *), \ 236 238 .is_array = true, \ 237 - .is_string = true, \ 239 + .type = DEV_PROP_STRING, \ 238 240 { .pointer = { .str = _val_ } }, \ 239 241 } 240 242 241 - #define PROPERTY_ENTRY_INTEGER(_name_, _type_, _val_) \ 242 - (struct property_entry) { \ 243 - .name = _name_, \ 244 - .length = sizeof(_type_), \ 245 - .is_string = false, \ 246 - { .value = { ._type_##_data = _val_ } }, \ 243 + #define PROPERTY_ENTRY_INTEGER(_name_, _type_, _Type_, _val_) \ 244 + (struct property_entry) { \ 245 + .name = _name_, \ 246 + .length = sizeof(_type_), \ 247 + .type = DEV_PROP_##_Type_, \ 248 + { .value = { ._type_##_data = _val_ } }, \ 247 249 } 248 250 249 251 #define PROPERTY_ENTRY_U8(_name_, _val_) \ 250 - PROPERTY_ENTRY_INTEGER(_name_, u8, _val_) 252 + PROPERTY_ENTRY_INTEGER(_name_, u8, U8, _val_) 251 253 #define PROPERTY_ENTRY_U16(_name_, _val_) \ 252 - PROPERTY_ENTRY_INTEGER(_name_, u16, _val_) 254 + PROPERTY_ENTRY_INTEGER(_name_, u16, U16, _val_) 253 255 #define PROPERTY_ENTRY_U32(_name_, _val_) \ 254 - PROPERTY_ENTRY_INTEGER(_name_, u32, _val_) 256 + PROPERTY_ENTRY_INTEGER(_name_, u32, U32, _val_) 255 257 #define PROPERTY_ENTRY_U64(_name_, _val_) \ 256 - PROPERTY_ENTRY_INTEGER(_name_, u64, _val_) 258 + PROPERTY_ENTRY_INTEGER(_name_, u64, U64, _val_) 257 259 258 260 #define PROPERTY_ENTRY_STRING(_name_, _val_) \ 259 261 (struct property_entry) { \ 260 262 .name = _name_, \ 261 263 .length = sizeof(_val_), \ 262 - .is_string = true, \ 264 + .type = DEV_PROP_STRING, \ 263 265 { .value = { .str = _val_ } }, \ 264 266 } 265 267