Lock in $30 Savings on PRO—Offer Ends Soon! ⏳

Enhancing spatial safety: Fixing thousands of -...

Enhancing spatial safety: Fixing thousands of -Wflex-array-member-not-at-end warnings

The introduction of the new -Wflex-array-member-not-at-end compiler option, released in GCC-14, has revealed approximately 60,000 warnings in the Linux kernel. Among them, some legitimate bugs have been uncovered.

In this presentation, we will explore in detail the different strategies we are employing to resolve all these warnings. These methods have already helped us resolve about 30% of them. Our ultimate goal in the Kernel Self-Protection Project is to globally enable this option in mainline, further enhancing the security of the kernel in the spatial safety domain.

Gustavo AR SILVA

Kernel Recipes

September 29, 2024
Tweet

More Decks by Kernel Recipes

Other Decks in Technology

Transcript

  1. 1 Enhancing spatial safety: fixing thousands of -Wflex-array-member-not-at-end warnings Gustavo

    A. R. Silva [email protected] fosstodon.org/@gustavoars Supported by The Linux Foundation & Alpha-Omega Kernel Recipes September 25, 2024 Paris, France
  2. Who am I? • Upstream first – 8 years. •

    Upstream Linux Kernel Engineer. • Kernel hardening. • Proactive security. • Kernel Self-Protection Project (KSPP). • Google Open Source Security Team (GOSST). • Linux Kernel division. By @shidokou
  3. • Introduction – C99 flexible-array members (FAMs) – The new

    -Wflex-array-member-not-at-end compiler option • The challenge of -Wflex-array-member-not-at-end – What’s wrong with FAMs in the middle? – How did we get here? - A brief history – Fixing thousands of -Wfamnae warnings • Conclusions Agenda
  4. – The last member of a struct. Quick review of

    C99 flexible-array members struct flex { ... size_t count; struct foo fam[]; };
  5. – The last member of a struct. – The flex

    struct usually contains a counter member. Extended review of C99 flexible-array members struct flex { ... size_t count; struct foo fam[]; };
  6. – The last member of a struct. – The flex

    struct usually contains a counter member. – struct flex may not be a member of another struct. Extended review of C99 flexible-array members struct flex { ... size_t count; struct foo fam[]; };
  7. – The last member of a struct. – The flex

    struct usually contains a counter member. – struct flex may not be a member of another struct. Extended review of C99 flexible-array members struct flex { ... size_t count; struct foo fam[] __counted_by(count); };
  8. – The last member of a struct. – The flex

    struct usually contains a counter member. – struct flex may not be a member of another struct. – Run-time bounds-checking coverage on FAMs. Extended review of C99 flexible-array members struct flex { ... size_t count; struct foo fam[] __counted_by(count); };
  9. – Developed by Qing Zhao last year (2023) – Released

    in GCC 14 The new -Wflex-array-member-not-at-end
  10. – Warns about FAMs in the middle of composite structs.

    The new -Wflex-array-member-not-at-end struct flex { ... size_t count; struct foo fam[] __counted_by(count); }; struct composite { ... struct flex middle; ... };
  11. – Warns about FAMs in the middle of composite structs.

    The new -Wflex-array-member-not-at-end struct flex { ... size_t count; struct foo fam[] __counted_by(count); }; struct composite { ... struct flex middle; ... };
  12. – Warns about FAMs in the middle of composite structs.

    The new -Wflex-array-member-not-at-end struct flex { ... size_t count; struct foo fam[] __counted_by(count); }; struct composite { ... struct flex middle; /* -Wfamnae warning! */ ... };
  13. – Flex struct in a composite struct is an extension.

    What’s wrong with FAMs in the middle?
  14. – Flex struct in a composite struct is an extension.

    • the last member What’s wrong with FAMs in the middle? struct composite { ... struct flex last; };
  15. – Flex struct in a composite struct is an extension.

    • the last member • not the last member What’s wrong with FAMs in the middle? struct composite { ... struct flex middle; ... }; struct composite { ... struct flex last; };
  16. – Flex struct in a composite struct is an extension.

    • the last member • not the last member – This is deprecated now. What’s wrong with FAMs in the middle? struct composite { ... struct flex middle; ... }; struct composite { ... struct flex last; };
  17. – Flex struct in a composite struct is an extension.

    • the last member • not the last member – This is deprecated now. What’s wrong with FAMs in the middle? struct composite { ... struct flex middle; ... }; struct composite { ... struct flex last; };
  18. – “Compilers do not handle such a case consistently. Any

    code relying on this case should be modified to ensure that flexible array members only end up at the ends of structures.” -GCC Docs. What’s wrong with FAMs in the middle? struct composite { ... struct flex middle; ... };
  19. – Flexible-Array Transformations - [1] & [0] to C99 [

    ] • It took us 5 years (2019 – 2024) How did we get here? - A brief history
  20. – Flexible-Array Transformations - [1] & [0] to C99 [

    ] • It took us 5 years (2019 – 2024) – [1], [0], [ ] & [N] trailing arrays & fortified memcpy() • Fixed __builtin_object_size() • Fixed __builtin_dynamic_object_size() How did we get here? - A brief history
  21. – Flexible-Array Transformations - [1] & [0] to C99 [

    ] • It took us 5 years (2019 – 2024) – [1], [0], [ ] & [N] trailing arrays & fortified memcpy() • Fixed __builtin_object_size() • Fixed __builtin_dynamic_object_size() • -fstrict-flex-arrays[=n] – Clang 16 & GCC 13 • -fstrict-flex-arrays=3 enabled in Linux 6.5 – Only C99 FAMs are considered flex arrays or VLOs. How did we get here? - A brief history
  22. – The counted_by attribute – Clang 18 & GCC 15

    How did we get here? - A brief history
  23. – The counted_by attribute – Clang 18 & GCC 15

    • Use __builtin_dynamic_object_size() in fortified memcpy(). • counted_by annotations in progress. How did we get here? - A brief history
  24. – The counted_by attribute – Clang 18 & GCC 15

    • Use __builtin_dynamic_object_size() in fortified memcpy(). • counted_by annotations in progress. • Ideally, every FAM should be annotated. How did we get here? - A brief history
  25. – A bit more than 60,000 warnings in total Fixing

    thousands of -Wfamnae warnings in Linux struct flex { ... size_t count; struct foo fam[] __counted_by(count); }; struct composite { ... struct flex middle; /* -Wfamnae warning */ ... };
  26. – A bit more than 60,000 warnings in total –

    650 unique ones. Fixing thousands of -Wfamnae warnings in Linux struct flex { ... size_t count; struct foo fam[] __counted_by(count); }; struct composite { ... struct flex middle; /* -Wfamnae warning */ ... };
  27. – A bit more than 60,000 warnings in total –

    650 unique ones. – Some patterns emerged. Fixing thousands of -Wfamnae warnings in Linux struct flex { ... size_t count; struct foo fam[] __counted_by(count); }; struct composite { ... struct flex middle; /* -Wfamnae warning */ ... };
  28. -Wflex-array-member-not-at-end Case 1: FAMs not used at all. struct wl1251_cmd_header

    { u16 id; u16 status; /* payload */ u8 data[]; } __packed; struct cmd_read_write_memory { struct wl1251_cmd_header header; /* -Wfamnae warning */ u32 addr; u32 size; u8 value[MAX_READ_SIZE]; } __packed;
  29. -Wflex-array-member-not-at-end Case 1: FAMs not used at all. struct cmd_read_write_memory

    { struct wl1251_cmd_header header; /* -Wfamnae warning */ u32 addr; u32 size; u8 value[MAX_READ_SIZE]; } __packed; struct wl1251_cmd_header { u16 id; u16 status; /* payload */ u8 data[]; } __packed;
  30. -Wflex-array-member-not-at-end Case 1: FAMs not used at all. struct wl1251_cmd_header

    { u16 id; u16 status; /* payload */ u8 data[]; } __packed; struct cmd_read_write_memory { struct wl1251_cmd_header header; /* -Wfamnae warning */ u32 addr; u32 size; u8 value[MAX_READ_SIZE]; } __packed;
  31. -Wflex-array-member-not-at-end Case 1: FAMs not used at all. struct cmd_read_write_memory

    { struct wl1251_cmd_header header; /* -Wfamnae warning */ u32 addr; u32 size; u8 value[MAX_READ_SIZE]; } __packed; struct wl1251_cmd_header { u16 id; u16 status; /* payload */ u8 data[]; } __packed;
  32. -Wflex-array-member-not-at-end Case 1: FAMs not used at all. struct cmd_read_write_memory

    { struct wl1251_cmd_header header; /* -Wfamnae warning */ u32 addr; u32 size; u8 value[MAX_READ_SIZE]; } __packed; struct wl1251_cmd_header { u16 id; u16 status; /* payload */ u8 data[]; } __packed;
  33. -Wflex-array-member-not-at-end Case 1: FAMs not used at all. struct cmd_read_write_memory

    { struct wl1251_cmd_header header; /* -Wfamnae warning */ u32 addr; u32 size; u8 value[MAX_READ_SIZE]; } __packed; struct wl1251_cmd_header { u16 id; u16 status; /* payload */ u8 data[]; } __packed;
  34. -Wflex-array-member-not-at-end Case 1: FAMs not used at all. – f4b09b29f8b4

    (“wifi: ti: Avoid a hundred -Wflex-array...”) struct wl1251_cmd_header { u16 id; u16 status; - /* payload */ - u8 data[]; } __packed;
  35. -Wflex-array-member-not-at-end Case 2: FAMs never accessed through the composite struct.

    struct flex { int a; int b; size_t count; struct foo fam[]; }; struct composite { ... struct flex middle; /* -Wfamnae warning */ ... } *p; ... do_something_with(p->middle.a, p->middle.b);
  36. -Wflex-array-member-not-at-end Case 2: FAMs never accessed through the composite struct.

    struct flex { int a; int b; size_t count; struct foo fam[]; }; struct composite { ... struct flex middle; /* -Wfamnae warning */ ... } *p; ... /* We may access the rest of the members in struct flex */ do_something_with(p->middle.a, p->middle.b);
  37. -Wflex-array-member-not-at-end Case 2: FAMs never accessed through the composite struct.

    – What can we do about it? struct flex { int a; int b; size_t count; struct foo fam[]; }; struct composite { ... struct flex middle; /* -Wfamnae warning */ ... };
  38. -Wflex-array-member-not-at-end Case 2: FAMs never accessed through the composite struct.

    struct flex { /* original struct */ int a; int b; size_t count; struct foo fam[] __counted_by(count); }; struct flex_hdr { int a; int b; size_t count; };
  39. -Wflex-array-member-not-at-end Case 2: FAMs never accessed through the composite struct.

    – Two separate structs: original struct & header struct struct flex { /* original struct */ int a; int b; size_t count; struct foo fam[] __counted_by(count); }; struct flex_hdr { int a; int b; size_t count; };
  40. -Wflex-array-member-not-at-end Case 2: FAMs never accessed through the composite struct.

    – Two separate structs: original struct & header struct – New header struct named after original flex struct. struct flex { /* original struct */ int a; int b; size_t count; struct foo fam[] __counted_by(count); }; struct flex_hdr { int a; int b; size_t count; };
  41. -Wflex-array-member-not-at-end Case 2: FAMs never accessed through the composite struct.

    – Two separate structs: original struct & header struct – New header struct named after original flex struct. struct flex { /* original struct */ int a; int b; size_t count; struct foo fam[] __counted_by(count); }; struct flex_hdr { /* All members in struct flex except the FAM */ int a; int b; size_t count; };
  42. -Wflex-array-member-not-at-end Case 2: FAMs never accessed through the composite struct.

    – Two separate structs: original struct & header struct – New header struct named after original flex struct. struct flex { /* original struct */ int a; int b; size_t count; struct foo fam[] __counted_by(count); }; struct flex_hdr { /* All members in struct flex except the FAM */ int a; int b; size_t count; };
  43. -Wflex-array-member-not-at-end Case 2: FAMs never accessed through the composite struct.

    – Two separate structs: original struct & header struct struct composite { /* BEFORE */ ... struct flex middle; /* -Wfamnae warning :( */ ... }; struct composite { /* AFTER */ ... struct flex_hdr middle; ... };
  44. -Wflex-array-member-not-at-end Case 2: FAMs never accessed through the composite struct.

    – Two separate structs: original struct & header struct struct composite { /* BEFORE */ ... struct flex middle; /* -Wfamnae warning :( */ ... }; struct composite { /* AFTER */ ... struct flex_hdr middle; ... };
  45. -Wflex-array-member-not-at-end Case 2: FAMs never accessed through the composite struct.

    – Two separate structs: original struct & header struct struct composite { /* BEFORE */ ... struct flex middle; /* -Wfamnae warning :( */ ... }; struct composite { /* AFTER */ ... struct flex_hdr middle; /* Life’s beautiful! ^.^ */ ... };
  46. -Wflex-array-member-not-at-end Case 2: FAMs never accessed through the composite struct.

    – What’s the problem with this? struct flex { /* original struct */ int a; int b; size_t count; struct foo fam[] __counted_by(count); }; struct flex_hdr { /* All members in struct flex except the FAM */ int a; int b; size_t count; };
  47. -Wflex-array-member-not-at-end Case 2: FAMs never accessed through the composite struct.

    – Duplicate code. struct flex { /* original struct */ int a; int b; size_t count; struct foo fam[] __counted_by(count); }; struct flex_hdr { /* All members in struct flex except the FAM */ int a; int b; size_t count; };
  48. -Wflex-array-member-not-at-end Case 2: FAMs never accessed through the composite struct.

    – Duplicate code. struct flex { /* original struct */ int a; int b; size_t count; struct foo fam[] __counted_by(count); }; struct flex_hdr { /* All members in struct flex except the FAM */ int a; int b; size_t count; };
  49. -Wflex-array-member-not-at-end Case 2: FAMs never accessed through the composite struct.

    – Duplicate code. – Maintain two independent but basically identical structs. struct flex { /* original struct */ int a; int b; size_t count; struct foo fam[] __counted_by(count); }; struct flex_hdr { /* All members in struct flex except the FAM */ int a; int b; size_t count; };
  50. -Wflex-array-member-not-at-end Case 2: FAMs never accessed through the composite struct.

    – Use struct_group_tagged()/__struct_group() struct flex { /* BEFORE */ int a; int b; size_t count; struct foo fam[] __counted_by(count); }; struct composite { /* BEFORE */ ... struct flex middle; /* -Wfamnae warning */ ... } *p;
  51. -Wflex-array-member-not-at-end Case 2: FAMs never accessed through the composite struct.

    – Use struct_group_tagged()/__struct_group() struct flex { /* AFTER */ /* New members must be added within the struct_group() macro below. */ struct_group_tagged(flex_hdr, hdr, int a; int b; size_t count; ); struct foo fam[] __counted_by(count); }; struct composite { /* BEFORE */ ... struct flex middle; /* -Wfamnae warning */ ... } *p;
  52. -Wflex-array-member-not-at-end Case 2: FAMs never accessed through the composite struct.

    – Use struct_group_tagged()/__struct_group() struct flex { /* AFTER */ /* New members must be added within the struct_group() macro below. */ struct_group_tagged(flex_hdr, hdr, int a; int b; size_t count; ); struct foo fam[] __counted_by(count); }; struct composite { /* AFTER */ ... struct flex_hdr middle; /* FAM is gone! ^.^ */ ... } *p;
  53. The struct_group() family of helpers Created by Kees Cook and

    Keith Packard #define struct_group_tagged(TAG, NAME, MEMBERS...) \ union { \ struct { MEMBERS }; \ struct TAG { MEMBERS } NAME; \ }
  54. The struct_group() family of helpers Created by Kees Cook and

    Keith Packard – Access each member directly or via the named struct. #define struct_group_tagged(TAG, NAME, MEMBERS...) \ union { \ struct { MEMBERS }; \ struct TAG { MEMBERS } NAME; \ }
  55. The struct_group() family of helpers Created by Kees Cook and

    Keith Packard – Access each member directly or via the named struct. – Create a new struct type and define an identifier for the group #define struct_group_tagged(TAG, NAME, MEMBERS...) \ union { \ struct { MEMBERS }; \ struct TAG { MEMBERS } NAME; \ }
  56. The struct_group() family of helpers Created by Kees Cook and

    Keith Packard – Access each member directly or via the named struct. – Create a new struct type and define an identifier for the group – via which we can gain bounds-checking. #define struct_group_tagged(TAG, NAME, MEMBERS...) \ union { \ struct { MEMBERS }; \ struct TAG { MEMBERS } NAME; \ }
  57. -Wflex-array-member-not-at-end Case 2: FAMs never accessed through the composite struct.

    – Use struct_group_tagged()/__struct_group() struct flex { /* AFTER */ /* New members must be added within the struct_group() macro below. */ struct_group_tagged(flex_hdr, hdr, int a; int b; size_t count; ); struct foo fam[] __counted_by(count); }; struct composite { /* AFTER */ ... struct flex_hdr middle; /* FAM is gone! ^.^ */ ... } *p; p->middle.a p->middle.b p->middle.count p->middle.hdr.a p->middle.hdr.b p->middle.hdr.count
  58. -Wflex-array-member-not-at-end Case 2: FAMs never accessed through the composite struct.

    – 5c4250092fad (“wifi: mwl8k: Avoid -Wflex-array-…”) struct mwl8k_cmd_pkt { - __le16 code; - __le16 length; - __u8 seq_num; - __u8 macid; - __le16 result; + __struct_group(mwl8k_cmd_pkt_hdr, hdr, __packed, + __le16 code; + __le16 length; + __u8 seq_num; + __u8 macid; + __le16 result; + ); char payload[]; } __packed;
  59. -Wflex-array-member-not-at-end Case 2: FAMs never accessed through the composite struct.

    – 5c4250092fad (“wifi: mwl8k: Avoid -Wflex-array-…”) struct mwl8k_cmd_pkt { - __le16 code; - __le16 length; - __u8 seq_num; - __u8 macid; - __le16 result; + __struct_group(mwl8k_cmd_pkt_hdr, hdr, __packed, + __le16 code; + __le16 length; + __u8 seq_num; + __u8 macid; + __le16 result; + ); char payload[]; } __packed;
  60. -Wflex-array-member-not-at-end – 5c4250092fad (“wifi: mwl8k: Avoid -Wflex-array-…”) – Replace mwl8k_cmd_pkt

    with mwl8k_cmd_pkt_hdr struct mwl8k_cmd_rf_antenna { - struct mwl8k_cmd_pkt header; + struct mwl8k_cmd_pkt_hdr header; __le16 antenna; __le16 mode; } __packed;
  61. -Wflex-array-member-not-at-end – 5c4250092fad (“wifi: mwl8k: Avoid -Wflex-array-…”) – Replace mwl8k_cmd_pkt

    with mwl8k_cmd_pkt_hdr struct mwl8k_cmd_rf_antenna { - struct mwl8k_cmd_pkt header; + struct mwl8k_cmd_pkt_hdr header; __le16 antenna; __le16 mode; } __packed;
  62. Case 3: Implicit unions between FAMs and fixed-size arrays of

    the same element type. -Wflex-array-member-not-at-end
  63. Case 3: Implicit unions between FAMs and fixed-size arrays of

    the same element type. -Wflex-array-member-not-at-end struct flex_struct { ... size_t count; struct foo flex_array[] __counted_by(count); }; struct composite_struct { ... struct flex_struct flex_in_the_middle; struct foo fixed_array[MAX_LENGTH]; ... } __packed;
  64. Case 3: Implicit unions between FAMs and fixed-size arrays of

    the same element type. -Wflex-array-member-not-at-end struct flex_struct { ... size_t count; struct foo flex_array[] __counted_by(count); }; struct composite_struct { ... struct flex_struct flex_in_the_middle; struct foo fixed_array[MAX_LENGTH]; ... } __packed;
  65. Case 3: Implicit unions between FAMs and fixed-size arrays of

    the same element type. -Wflex-array-member-not-at-end struct flex_struct { ... size_t count; struct foo flex_array[] __counted_by(count); }; struct composite_struct { ... struct flex_struct flex_in_the_middle; struct foo fixed_array[MAX_LENGTH]; ... } __packed; • flex_array and fixed_array share the same address in memory - in the best scenario. • Both form an implicit union.
  66. -Wflex-array-member-not-at-end Case 3: Implicit unions between FAMs and fixed-size arrays

    of the same element type. struct ima_digest_data { /* flexible struct */ + /* New members must be added within the __struct_group() macro below. */ + __struct_group(ima_digest_data_hdr, hdr, __packed, u8 algo; u8 length; ... + ); u8 digest[]; } __packed; /* implicit union: FAM & fixed-size array*/ struct ima_max_digest_data { - struct ima_digest_data hdr; + struct ima_digest_data_hdr hdr; u8 digest[HASH_MAX_DIGESTSIZE]; } __packed;
  67. -Wflex-array-member-not-at-end Case 3: Implicit unions between FAMs and fixed-size arrays

    of the same element type. struct ima_digest_data { /* flexible struct */ + /* New members must be added within the __struct_group() macro below. */ + __struct_group(ima_digest_data_hdr, hdr, __packed, u8 algo; u8 length; ... + ); u8 digest[]; } __packed; /* implicit union: FAM & fixed-size array*/ struct ima_max_digest_data { - struct ima_digest_data hdr; + struct ima_digest_data_hdr hdr; u8 digest[HASH_MAX_DIGESTSIZE]; } __packed;
  68. -Wflex-array-member-not-at-end Case 3: Implicit unions between FAMs and fixed-size arrays

    of the same element type. – However, FAM digest is accessed at run-time. struct ima_digest_data { /* flexible struct */ + /* New members must be added within the __struct_group() macro below. */ + __struct_group(ima_digest_data_hdr, hdr, __packed, u8 algo; u8 length; ... + ); u8 digest[]; } __packed; /* implicit union: FAM & fixed-size array*/ struct ima_max_digest_data { - struct ima_digest_data hdr; + struct ima_digest_data_hdr hdr; u8 digest[HASH_MAX_DIGESTSIZE]; } __packed;
  69. -Wflex-array-member-not-at-end Case 3: Implicit unions between FAMs and fixed-size arrays

    of the same element type. – However, FAM digest is accessed at run-time. struct ima_digest_data { /* flexible struct */ + /* New members must be added within the __struct_group() macro below. */ + __struct_group(ima_digest_data_hdr, hdr, __packed, u8 algo; u8 length; ... + ); u8 digest[]; } __packed; /* implicit union: FAM & fixed-size array*/ struct ima_max_digest_data { - struct ima_digest_data hdr; + struct ima_digest_data_hdr hdr; u8 digest[HASH_MAX_DIGESTSIZE]; } __packed;
  70. -Wflex-array-member-not-at-end Case 3: Implicit unions between FAMs and fixed-size arrays

    of the same element type. – However, FAM digest is accessed at run-time. /* implicit union: FAM & fixed-size array*/ struct ima_max_digest_data { - struct ima_digest_data hdr; + struct ima_digest_data_hdr hdr; u8 digest[HASH_MAX_DIGESTSIZE]; } __packed; struct ima_max_digest_data hash; ... /* read data from the FAM digest */ memcpy(digest_hash, hash.hdr.digest, digest_hash_len);
  71. -Wflex-array-member-not-at-end Case 3: Implicit unions between FAMs and fixed-size arrays

    of the same element type. – However, FAM digest is accessed at run-time. /* implicit union: FAM & fixed-size array*/ struct ima_max_digest_data { - struct ima_digest_data hdr; + struct ima_digest_data_hdr hdr; u8 digest[HASH_MAX_DIGESTSIZE]; } __packed; struct ima_max_digest_data hash; ... /* read data from the FAM digest */ memcpy(digest_hash, hash.hdr.digest, digest_hash_len);
  72. -Wflex-array-member-not-at-end Case 3: Implicit unions between FAMs and fixed-size arrays

    of the same element type. – However, FAM digest is accessed at run-time. /* implicit union: FAM & fixed-size array*/ struct ima_max_digest_data { - struct ima_digest_data hdr; + struct ima_digest_data_hdr hdr; u8 digest[HASH_MAX_DIGESTSIZE]; } __packed; struct ima_max_digest_data hash; ... /* read data from the FAM digest */ memcpy(digest_hash, hash.hdr.digest, digest_hash_len);
  73. -Wflex-array-member-not-at-end Case 3: Implicit unions between FAMs and fixed-size arrays

    of the same element type. – However, FAM digest is accessed at run-time. /* implicit union: FAM & fixed-size array*/ struct ima_max_digest_data { - struct ima_digest_data hdr; + struct ima_digest_data_hdr hdr; u8 digest[HASH_MAX_DIGESTSIZE]; } __packed; struct ima_max_digest_data hash; ... /* read data from the FAM digest */ memcpy(digest_hash, hash.hdr.digest, digest_hash_len);
  74. -Wflex-array-member-not-at-end Case 3: Implicit unions between FAMs and fixed-size arrays

    of the same element type. – Use container_of() to get a pointer to the flex struct. – Access FAM through that pointer.
  75. -Wflex-array-member-not-at-end Case 3: Implicit unions between FAMs and fixed-size arrays

    of the same element type. – Use container_of() to get a pointer to the flex struct. – Access FAM through that pointer. struct ima_max_digest_data hash; /* struct with implicit union */ + struct ima_digest_data *hash_hdr = container_of(&hash.hdr, + struct ima_digest_data, hdr); ... hash_hdr is now a pointer to flex struct ima_digest_data /* read data from the FAM digest */ - memcpy(digest_hash, hash.hdr.digest, digest_hash_len); + memcpy(digest_hash, hash_hdr->digest, digest_hash_len);
  76. -Wflex-array-member-not-at-end Case 3: Implicit unions between FAMs and fixed-size arrays

    of the same element type. – Use container_of() to get a pointer to the flex struct. – Access FAM through that pointer. struct ima_max_digest_data hash; /* struct with implicit union */ + struct ima_digest_data *hash_hdr = container_of(&hash.hdr, + struct ima_digest_data, hdr); ... hash_hdr is now a pointer to flex struct ima_digest_data /* read data from the FAM digest */ - memcpy(digest_hash, hash.hdr.digest, digest_hash_len); + memcpy(digest_hash, hash_hdr->digest, digest_hash_len);
  77. -Wflex-array-member-not-at-end Case 3: Implicit unions between FAMs and fixed-size arrays

    of the same element type. – Use container_of() to get a pointer to the flex struct. – Access FAM through that pointer. struct ima_max_digest_data hash; /* struct with implicit union */ + struct ima_digest_data *hash_hdr = container_of(&hash.hdr, + struct ima_digest_data, hdr); ... hash_hdr is now a pointer to flex struct ima_digest_data /* read data from the FAM digest */ - memcpy(digest_hash, hash.hdr.digest, digest_hash_len); + memcpy(digest_hash, hash_hdr->digest, digest_hash_len);
  78. -Wflex-array-member-not-at-end Case 3: Implicit unions between FAMs and fixed-size arrays

    of the same element type. – Use container_of() to get a pointer to the flex struct. – Access FAM through that pointer. struct ima_max_digest_data hash; /* struct with implicit union */ + struct ima_digest_data *hash_hdr = container_of(&hash.hdr, + struct ima_digest_data, hdr); ... hash_hdr is now a pointer to flex struct ima_digest_data /* read data from the FAM digest */ - memcpy(digest_hash, hash.hdr.digest, digest_hash_len); + memcpy(digest_hash, hash_hdr->digest, digest_hash_len);
  79. -Wflex-array-member-not-at-end Case 3: Implicit unions between FAMs and fixed-size arrays

    of the same element type. struct ima_max_digest_data hash; /* struct with implicit union */ + struct ima_digest_data *hash_hdr = container_of(&hash.hdr, + struct ima_digest_data, hdr); struct ima_digest_data { /* flexible struct */ + /* New members must be added within the __struct_group() macro below. */ + __struct_group(ima_digest_data_hdr, hdr, __packed, u8 algo; u8 length; ... + ); u8 digest[]; } __packed; /* implicit union: FAM & fixed-size array*/ struct ima_max_digest_data { - struct ima_digest_data hdr; + struct ima_digest_data_hdr hdr; u8 digest[HASH_MAX_DIGESTSIZE]; } __packed;
  80. -Wflex-array-member-not-at-end Case 3: Implicit unions between FAMs and fixed-size arrays

    of the same element type. struct ima_max_digest_data hash; /* struct with implicit union */ + struct ima_digest_data *hash_hdr = container_of(&hash.hdr, + struct ima_digest_data, hdr); struct ima_digest_data { /* flexible struct */ + /* New members must be added within the __struct_group() macro below. */ + __struct_group(ima_digest_data_hdr, hdr, __packed, u8 algo; u8 length; ... + ); u8 digest[]; } __packed; /* implicit union: FAM & fixed-size array*/ struct ima_max_digest_data { - struct ima_digest_data hdr; + struct ima_digest_data_hdr hdr; u8 digest[HASH_MAX_DIGESTSIZE]; } __packed;
  81. -Wflex-array-member-not-at-end Case 3: Implicit unions between FAMs and fixed-size arrays

    of the same element type. struct ima_max_digest_data hash; /* struct with implicit union */ + struct ima_digest_data *hash_hdr = container_of(&hash.hdr, + struct ima_digest_data, hdr); struct ima_digest_data { /* flexible struct */ + /* New members must be added within the __struct_group() macro below. */ + __struct_group(ima_digest_data_hdr, hdr, __packed, u8 algo; u8 length; ... + ); u8 digest[]; } __packed; /* implicit union: FAM & fixed-size array*/ struct ima_max_digest_data { - struct ima_digest_data hdr; + struct ima_digest_data_hdr hdr; u8 digest[HASH_MAX_DIGESTSIZE]; } __packed;
  82. -Wflex-array-member-not-at-end Case 3: Implicit unions between FAMs and fixed-size arrays

    of the same element type. struct ima_max_digest_data hash; /* struct with implicit union */ + struct ima_digest_data *hash_hdr = container_of(&hash.hdr, + struct ima_digest_data, hdr); struct ima_digest_data { /* flexible struct */ + /* New members must be added within the __struct_group() macro below. */ + __struct_group(ima_digest_data_hdr, hdr, __packed, u8 algo; u8 length; ... + ); u8 digest[]; } __packed; /* implicit union: FAM & fixed-size array*/ struct ima_max_digest_data { - struct ima_digest_data hdr; + struct ima_digest_data_hdr hdr; u8 digest[HASH_MAX_DIGESTSIZE]; } __packed;
  83. -Wflex-array-member-not-at-end Case 3: Implicit unions between FAMs and fixed-size arrays

    of the same element type. struct ima_max_digest_data hash; /* struct with implicit union */ + struct ima_digest_data *hash_hdr = container_of(&hash.hdr, + struct ima_digest_data, hdr); struct ima_digest_data { /* flexible struct */ + /* New members must be added within the __struct_group() macro below. */ + __struct_group(ima_digest_data_hdr, hdr, __packed, u8 algo; u8 length; ... + ); u8 digest[]; } __packed; /* implicit union: FAM & fixed-size array*/ struct ima_max_digest_data { - struct ima_digest_data hdr; + struct ima_digest_data_hdr hdr; u8 digest[HASH_MAX_DIGESTSIZE]; } __packed;
  84. -Wflex-array-member-not-at-end Case 3: Implicit unions between FAMs and fixed-size arrays

    of the same element type. struct ima_max_digest_data hash; /* struct with implicit union */ + struct ima_digest_data *hash_hdr = container_of(&hash.hdr, + struct ima_digest_data, hdr); struct ima_digest_data { /* flexible struct */ + /* New members must be added within the __struct_group() macro below. */ + __struct_group(ima_digest_data_hdr, hdr, __packed, u8 algo; u8 length; ... + ); u8 digest[]; } __packed; /* implicit union: FAM & fixed-size array*/ struct ima_max_digest_data { - struct ima_digest_data hdr; + struct ima_digest_data_hdr hdr; u8 digest[HASH_MAX_DIGESTSIZE]; } __packed;
  85. -Wflex-array-member-not-at-end Case 3: Implicit unions between FAMs and fixed-size arrays

    of the same element type. struct ima_max_digest_data hash; /* struct with implicit union */ + struct ima_digest_data *hash_hdr = container_of(&hash.hdr, + struct ima_digest_data, hdr); struct ima_digest_data { /* flexible struct */ + /* New members must be added within the __struct_group() macro below. */ + __struct_group(ima_digest_data_hdr, hdr, __packed, u8 algo; u8 length; ... + ); u8 digest[]; } __packed; /* implicit union: FAM & fixed-size array*/ struct ima_max_digest_data { - struct ima_digest_data hdr; + struct ima_digest_data_hdr hdr; u8 digest[HASH_MAX_DIGESTSIZE]; } __packed;
  86. -Wflex-array-member-not-at-end Case 3: Implicit unions between FAMs and fixed-size arrays

    of the same element type. struct ima_max_digest_data hash; /* struct with implicit union */ + struct ima_digest_data *hash_hdr = container_of(&hash.hdr, + struct ima_digest_data, hdr); struct ima_digest_data { /* flexible struct */ + /* New members must be added within the __struct_group() macro below. */ + __struct_group(ima_digest_data_hdr, hdr, __packed, u8 algo; u8 length; ... + ); u8 digest[]; } __packed; /* implicit union: FAM & fixed-size array*/ struct ima_max_digest_data { - struct ima_digest_data hdr; + struct ima_digest_data_hdr hdr; u8 digest[HASH_MAX_DIGESTSIZE]; } __packed;
  87. -Wflex-array-member-not-at-end Case 3: Implicit unions between FAMs and fixed-size arrays

    of the same element type. – Use container_of() to get a pointer to the flex struct. – Access FAM through that pointer. – 38aa3f5ac6d2 (“integrity: Avoid -Wflex-array-member...”) struct ima_max_digest_data hash; /* struct with implicit union */ + struct ima_digest_data *hash_hdr = container_of(&hash.hdr, + struct ima_digest_data, hdr); ... hash_hdr is now a pointer to flex struct ima_digest_data /* read data from the FAM digest */ - memcpy(digest_hash, hash.hdr.digest, digest_hash_len); + memcpy(digest_hash, hash_hdr->digest, digest_hash_len);
  88. -Wflex-array-member-not-at-end Case 3: Implicit unions between FAMs and fixed-size arrays

    of the same element type. – Use container_of() to get a pointer to the flex struct. – Access FAM through that pointer. – 38aa3f5ac6d2 (“integrity: Avoid -Wflex-array-member...”) struct ima_max_digest_data hash; /* struct with implicit union */ + struct ima_digest_data *hash_hdr = container_of(&hash.hdr, + struct ima_digest_data, hdr); ... hash_hdr is now a pointer to flex struct ima_digest_data /* read data from the FAM digest */ - memcpy(digest_hash, hash.hdr.digest, digest_hash_len); + memcpy(digest_hash, hash_hdr->digest, digest_hash_len);
  89. -Wflex-array-member-not-at-end Case 4: Implicit unions between FAMs and fixed-size arrays

    of the same element type – on stack. struct flex_struct { ... size_t count; struct foo flex_array[] __counted_by(count); }; int some_function(...) /* on-stack -Wfamnae warning */ { struct { struct flex_struct flex; struct foo fixed_array[10]; } obj = ... ... }
  90. -Wflex-array-member-not-at-end Case 4: Implicit unions between FAMs and fixed-size arrays

    of the same element type – on stack. struct flex_struct { ... size_t count; struct foo flex_array[] __counted_by(count); }; int some_function(...) /* on-stack -Wfamnae warning */ { struct { struct flex_struct flex; struct foo fixed_array[10]; } obj = ... ... }
  91. -Wflex-array-member-not-at-end Case 4: Implicit unions between FAMs and fixed-size arrays

    of the same element type – on stack. struct fun_admin_bind_req { struct fun_admin_req_common common; struct fun_admin_bind_entry entry[]; }; int fun_bind(...) /* on-stack -Wfamnae warning */ { struct { struct fun_admin_bind_req req; struct fun_admin_bind_entry entry[2]; } cmd = ... ... }
  92. -Wflex-array-member-not-at-end Case 4: Implicit unions between FAMs and fixed-size arrays

    of the same element type – on stack. struct fun_admin_bind_req { struct fun_admin_req_common common; struct fun_admin_bind_entry entry[]; }; int fun_bind(...) /* on-stack -Wfamnae warning */ { struct { struct fun_admin_bind_req req; struct fun_admin_bind_entry entry[2]; } cmd = ... ... }
  93. -Wflex-array-member-not-at-end Case 4: Implicit unions between FAMs and fixed-size arrays

    of the same element type – on stack. struct fun_admin_bind_req { struct fun_admin_req_common common; struct fun_admin_bind_entry entry[]; /* flex-array member */ }; int fun_bind(...) /* on-stack -Wfamnae warning */ { struct { struct fun_admin_bind_req req; struct fun_admin_bind_entry entry[2]; /* fixed-size array */ } cmd = ... ... }
  94. Case 4: Implicit unions between FAMs and fixed-size arrays of

    the same element type – on stack. -Wflex-array-member-not-at-end - struct { - struct fun_admin_bind_req req; - struct fun_admin_bind_entry entry[2]; - } cmd = { - .req.common = FUN_ADMIN_REQ_COMMON_INIT2(FUN_ADMIN_OP_BIND, - sizeof(cmd)), - .entry[0] = FUN_ADMIN_BIND_ENTRY_INIT(type0, id0), - .entry[1] = FUN_ADMIN_BIND_ENTRY_INIT(type1, id1), - }; + DEFINE_RAW_FLEX(struct fun_admin_bind_req, cmd, entry, 2); + + cmd->common = FUN_ADMIN_REQ_COMMON_INIT2(FUN_ADMIN_OP_BIND, + __struct_size(cmd)); + cmd->entry[0] = FUN_ADMIN_BIND_ENTRY_INIT(type0, id0); + cmd->entry[1] = FUN_ADMIN_BIND_ENTRY_INIT(type1, id1);
  95. Case 4: Implicit unions between FAMs and fixed-size arrays of

    the same element type – on stack. -Wflex-array-member-not-at-end - struct { - struct fun_admin_bind_req req; - struct fun_admin_bind_entry entry[2]; - } cmd = { - .req.common = FUN_ADMIN_REQ_COMMON_INIT2(FUN_ADMIN_OP_BIND, - sizeof(cmd)), - .entry[0] = FUN_ADMIN_BIND_ENTRY_INIT(type0, id0), - .entry[1] = FUN_ADMIN_BIND_ENTRY_INIT(type1, id1), - }; + DEFINE_RAW_FLEX(struct fun_admin_bind_req, cmd, entry, 2); + + cmd->common = FUN_ADMIN_REQ_COMMON_INIT2(FUN_ADMIN_OP_BIND, + __struct_size(cmd)); + cmd->entry[0] = FUN_ADMIN_BIND_ENTRY_INIT(type0, id0); + cmd->entry[1] = FUN_ADMIN_BIND_ENTRY_INIT(type1, id1);
  96. Case 4: Implicit unions between FAMs and fixed-size arrays of

    the same element type – on stack. -Wflex-array-member-not-at-end - struct { - struct fun_admin_bind_req req; - struct fun_admin_bind_entry entry[2]; - } cmd = { - .req.common = FUN_ADMIN_REQ_COMMON_INIT2(FUN_ADMIN_OP_BIND, - sizeof(cmd)), - .entry[0] = FUN_ADMIN_BIND_ENTRY_INIT(type0, id0), - .entry[1] = FUN_ADMIN_BIND_ENTRY_INIT(type1, id1), - }; + DEFINE_RAW_FLEX(struct fun_admin_bind_req, cmd, entry, 2); + + cmd->common = FUN_ADMIN_REQ_COMMON_INIT2(FUN_ADMIN_OP_BIND, + __struct_size(cmd)); + cmd->entry[0] = FUN_ADMIN_BIND_ENTRY_INIT(type0, id0); + cmd->entry[1] = FUN_ADMIN_BIND_ENTRY_INIT(type1, id1);
  97. Case 4: Implicit unions between FAMs and fixed-size arrays of

    the same element type – on stack. -Wflex-array-member-not-at-end - struct { - struct fun_admin_bind_req req; - struct fun_admin_bind_entry entry[2]; - } cmd = { - .req.common = FUN_ADMIN_REQ_COMMON_INIT2(FUN_ADMIN_OP_BIND, - sizeof(cmd)), - .entry[0] = FUN_ADMIN_BIND_ENTRY_INIT(type0, id0), - .entry[1] = FUN_ADMIN_BIND_ENTRY_INIT(type1, id1), - }; + DEFINE_RAW_FLEX(struct fun_admin_bind_req, cmd, entry, 2); + + cmd->common = FUN_ADMIN_REQ_COMMON_INIT2(FUN_ADMIN_OP_BIND, + __struct_size(cmd)); + cmd->entry[0] = FUN_ADMIN_BIND_ENTRY_INIT(type0, id0); + cmd->entry[1] = FUN_ADMIN_BIND_ENTRY_INIT(type1, id1);
  98. Case 4: Implicit unions between FAMs and fixed-size arrays of

    the same element type – on stack. -Wflex-array-member-not-at-end - struct { - struct fun_admin_bind_req req; - struct fun_admin_bind_entry entry[2]; - } cmd = { - .req.common = FUN_ADMIN_REQ_COMMON_INIT2(FUN_ADMIN_OP_BIND, - sizeof(cmd)), - .entry[0] = FUN_ADMIN_BIND_ENTRY_INIT(type0, id0), - .entry[1] = FUN_ADMIN_BIND_ENTRY_INIT(type1, id1), - }; + DEFINE_RAW_FLEX(struct fun_admin_bind_req, cmd, entry, 2); + + cmd->common = FUN_ADMIN_REQ_COMMON_INIT2(FUN_ADMIN_OP_BIND, + __struct_size(cmd)); + cmd->entry[0] = FUN_ADMIN_BIND_ENTRY_INIT(type0, id0); + cmd->entry[1] = FUN_ADMIN_BIND_ENTRY_INIT(type1, id1);
  99. Case 4: Implicit unions between FAMs and fixed-size arrays of

    the same element type – on stack. -Wflex-array-member-not-at-end - struct { - struct fun_admin_bind_req req; - struct fun_admin_bind_entry entry[2]; - } cmd = { - .req.common = FUN_ADMIN_REQ_COMMON_INIT2(FUN_ADMIN_OP_BIND, - sizeof(cmd)), - .entry[0] = FUN_ADMIN_BIND_ENTRY_INIT(type0, id0), - .entry[1] = FUN_ADMIN_BIND_ENTRY_INIT(type1, id1), - }; + DEFINE_RAW_FLEX(struct fun_admin_bind_req, cmd, entry, 2); + + cmd->common = FUN_ADMIN_REQ_COMMON_INIT2(FUN_ADMIN_OP_BIND, + __struct_size(cmd)); + cmd->entry[0] = FUN_ADMIN_BIND_ENTRY_INIT(type0, id0); + cmd->entry[1] = FUN_ADMIN_BIND_ENTRY_INIT(type1, id1);
  100. Case 4: Implicit unions between FAMs and fixed-size arrays of

    the same element type – on stack. -Wflex-array-member-not-at-end - struct { - struct fun_admin_bind_req req; - struct fun_admin_bind_entry entry[2]; - } cmd = { - .req.common = FUN_ADMIN_REQ_COMMON_INIT2(FUN_ADMIN_OP_BIND, - sizeof(cmd)), - .entry[0] = FUN_ADMIN_BIND_ENTRY_INIT(type0, id0), - .entry[1] = FUN_ADMIN_BIND_ENTRY_INIT(type1, id1), - }; + DEFINE_RAW_FLEX(struct fun_admin_bind_req, cmd, entry, 2); + + cmd->common = FUN_ADMIN_REQ_COMMON_INIT2(FUN_ADMIN_OP_BIND, + __struct_size(cmd)); + cmd->entry[0] = FUN_ADMIN_BIND_ENTRY_INIT(type0, id0); + cmd->entry[1] = FUN_ADMIN_BIND_ENTRY_INIT(type1, id1);
  101. -Wflex-array-member-not-at-end Case 4: Implicit unions between FAMs and fixed-size arrays

    of the same element type – on stack. – We use DECLARE_FLEX() and DECLARE_RAW_FLEX() helpers. – Some examples: • 6c85a13b133f (“platform/chrome: cros_ec_proto:…”) • 4d69c58ef2e4 (“fsnotify: Avoid -Wflex-array-mem…”) • 215c4704208b (“Bluetooth: L2CAP: Avoid -Wflex-...”)
  102. Conclusions A simple three-step solution for the complex case: –

    Use struct_group_tagged() to create a new tagged struct. • This groups together all members in the flex struct except the FAM.
  103. Conclusions A simple three-step solution for the complex case: –

    Use struct_group_tagged() to create a new tagged struct. • This groups together all members in the flex struct except the FAM. – Change the type of the conflicting object to the newly created tagged struct.
  104. Conclusions A simple three-step solution for the complex case: –

    Use struct_group_tagged() to create a new tagged struct. • This groups together all members in the flex struct except the FAM. – Change the type of the conflicting object to the newly created tagged struct. – Use container_of() to retrieve a pointer to the flex struct when needed. • Access the FAM via this pointer if necessary.
  105. Conclusions For implicit unions on the stack: – Use DECLARE_FLEX()

    when the FAM is annotated with __counted_by(). – We can use DECLARE_RAW_FLEX() in any other case.
  106. Conclusions • Clear strategy to enable -Wflex-array-member-not-at-end in mainline, soon.

    • A couple (three) dozen patches are already in mainline. • Down to ~300 (from ~650) unique warnings. • ~30% of total warnings addressed so far.