[comment]: # (This presentation was made with markdown-slides) [comment]: # (This is a CommonMark compliant comment. It will not be included in the presentation.) [comment]: # (Compile this presentation with the command below) [comment]: # (mdslides presentation.md --include media) [comment]: # (Set the theme:) [comment]: # (The list of themes is at https://revealjs.com/themes/) [comment]: # (The list of code themes is at https://highlightjs.org/) [comment]: # (Pass optional settings to reveal.js:) [comment]: # (Other settings are documented at https://revealjs.com/config/) Linux Security Summit North America | June 23, 2022 ## Meaningful Bounds Checking in the Linux Kernel Kees ("Case") Cook [@kees_cook](https://twitter.com/kees_cook) keescook@chromium.org https://outflux.net/slides/2022/lss-na/
### 25 "buffer overflow" CVEs across 2018, 2019, 2020... [comment]: # (chartjs config https://www.chartjs.org/docs/3.2.0/general/fonts.html)
,array index overflow (7),memcpy overflow (11),bad allocation size (2),iovec confusion (1),type confusion (1),string handling (1) CVEs, 7, 11, 2, 1, 1, 1,
**7 array index overflow flaws ...**
,array index overflow (7),memcpy overflow (11),bad allocation size (2),iovec confusion (1),type confusion (1),string handling (1) CVEs, 7, 11, 2, 1, 1, 1,
Without instrumentation, there's no checking of fixed-sized array indexes: ```cpp[1-14|3,7,11,12] struct sockaddr_alg { ... __u8 salg_name[64]; ... } sa; void something(int index, ...) { struct sockaddr_alg sa = { ... }; ... /* Oops! Out-of-bounds write when index >= 64 */ sa.salg_name[index] = input; ... } ```
All fixed-size array indexing flaws are mitigated with Undefined Behavior Sanitizer (**`-fsanitize=bounds`**) in GCC and Clang with these settings: ```shell CONFIG_UBSAN_BOUNDS=y CONFIG_UBSAN_TRAP=y (or set sysctl "panic_on_warn=1") ``` For example: ```shell UBSAN: array-index-out-of-bounds in crypto/af_alg.c:166:2 index 98 is out of range for type '__u8 [64]' ... Kernel panic - not syncing: panic_on_warn set ... ``` Though the `af_alg` bug replaced a fixed-size array with a flexible array ... https://git.kernel.org/linus/92eb6c3060eb
**11 memcpy() buffer overflow flaws ...**
,array index overflow (7),memcpy overflow (11),bad allocation size (2),iovec confusion (1),type confusion (1),string handling (1) CVEs, 7, 11, 2, 1, 1, 1,
### CVE-2020-24490 https://git.kernel.org/linus/a2ec905d1e16 [BleedingTooth: Linux Bluetooth Zero-Click Remote Code Execution](https://google.github.io/security-research/pocs/linux/bleedingtooth/writeup#badvibes-heap-based-buffer-overflow-cve-2020-24490) [comment]: # "in process_adv_report() at net/bluetooth/hci_event.c:5537:" net/bluetooth/hci_event.c ```cpp struct discovery_state { ... u8 last_adv_data[HCI_MAX_AD_LENGTH]; ... }; ... memcpy(d->last_adv_data, data, len); ```
### CVE-2020-12654 https://git.kernel.org/linus/3a9b153c5591 [comment]: # "in mwifiex_ret_wmm_get_status() at drivers/net/wireless/marvell/mwifiex/wmm.c:992:" drivers/net/wireless/marvell/mwifiex/wmm.c ```cpp struct mwifiex_bssdescriptor { ... struct ieee_types_wmm_parameter wmm_ie; ... }; ... memcpy((u8 *) &priv->curr_bss_params.bss_descriptor.wmm_ie, wmm_param_ie, wmm_param_ie->vend_hdr.len + 2); ```
### CVE-2020-12653 https://git.kernel.org/linus/b70261a288ea [comment]: # "in mwifiex_cmd_append_vsie_tlv() at drivers/net/wireless/marvell/mwifiex/scan.c:2893:" drivers/net/wireless/marvell/mwifiex/scan.c ```cpp struct mwifiex_ie_types_vendor_param_set { ... u8 ie[MWIFIEX_MAX_VSIE_LEN]; ... }; ... memcpy(vs_param_set->ie, priv->vs_ie[id].ie, le16_to_cpu(vs_param_set->header.len)); ```
### CVE-2019-14895 https://git.kernel.org/linus/3d94a4a8373b [comment]: # "in mwifiex_process_country_ie() at drivers/net/wireless/marvell/mwifiex/sta_ioctl.c:252:" drivers/net/wireless/marvell/mwifiex/sta_ioctl.c ```cpp struct mwifiex_802_11d_domain_reg { ... struct ieee80211_country_ie_triplet triplet[MWIFIEX_MAX_TRIPLET_802_11D]; ... }; ... memcpy(domain_info->triplet, &country_ie[2] + IEEE80211_COUNTRY_STRING_LEN, country_ie_len); ```
### CVE-2019-14816 https://git.kernel.org/linus/7caac62ed598 [comment]: # "in mwifiex_update_vs_ie(): at drivers/net/wireless/marvell/mwifiex/ie.c:247" drivers/net/wireless/marvell/mwifiex/ie.c ```cpp struct mwifiex_ie { ... u8 ie_buffer[IEEE_MAX_IE_SIZE]; ... }; memcpy(ie->ie_buffer + le16_to_cpu(ie->ie_length), vs_ie, vs_ie->len + 2); ```
### CVE-2019-14815 https://git.kernel.org/linus/7caac62ed598 [comment]: # "in mwifiex_set_uap_rates() at drivers/net/wireless/marvell/mwifiex/uap_cmd.c:271:" drivers/net/wireless/marvell/mwifiex/uap_cmd.c ```cpp struct mwifiex_uap_bss_param { ... u8 rates[MWIFIEX_SUPPORTED_RATES]; ... }; ... memcpy(bss_cfg->rates, rate_ie + 1, rate_ie->len); ```
### CVE-2019-14814 https://git.kernel.org/linus/7caac62ed598 [comment]: # "in mwifiex_set_wmm_params() at drivers/net/wireless/marvell/mwifiex/uap_cmd.c:402:" drivers/net/wireless/marvell/mwifiex/uap_cmd.c ```cpp struct mwifiex_uap_bss_param { ... struct mwifiex_types_wmm_info wmm_info; ... }; ... memcpy(&bss_cfg->wmm_info, wmm_ie + sizeof(struct ieee_types_header), *(wmm_ie + 1)); ```
### CVE-2019-10126 https://git.kernel.org/linus/69ae4f6aac15 [comment]: # "in mwifiex_uap_parse_tail_ies() at drivers/net/wireless/marvell/mwifiex/ie.c:383:" drivers/net/wireless/marvell/mwifiex/ie.c ```cpp struct mwifiex_ie { ... u8 ie_buffer[IEEE_MAX_IE_SIZE]; ... }; ... memcpy(gen_ie->ie_buffer + ie_len, hdr, token_len); ```
### CVE-2019-9500 https://git.kernel.org/linus/1b5e2423164b [comment]: # "in brcmf_wowl_nd_results() at drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c:3708:" drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c ```cpp struct cfg80211_ssid { u8 ssid[IEEE80211_MAX_SSID_LEN]; ... }; ... memcpy(cfg->wowl.nd->ssid.ssid, netinfo->SSID, netinfo->SSID_len); ```
### no CVE yet? https://git.kernel.org/linus/130f634da1af [comment]: # "in qtnf_event_handle_external_auth() at drivers/net/wireless/quantenna/qtnfmac/event.c:575:" drivers/net/wireless/quantenna/qtnfmac/event.c ```cpp struct cfg80211_ssid { u8 ssid[IEEE80211_MAX_SSID_LEN]; ... }; ... memcpy(auth.ssid.ssid, ev->ssid, len); ```
### no CVE yet? https://git.kernel.org/linus/d10a87a3535c [comment]: # "in wl1251_cmd_scan() at drivers/net/wireless/ti/wl1251/cmd.c:459:" drivers/net/wireless/ti/wl1251/cmd.c ```cpp struct cfg80211_ssid { u8 ssid[IEEE80211_MAX_SSID_LEN]; ... }; ... memcpy(cmd->params.ssid, ssid, ssid_len); ```
Wait a second, I thought **`CONFIG_FORTIFY_SOURCE=y`** solved this?!
Fortified `memcpy()` ```cpp[1-17|3-4|6-10|11-13|14|3-4] __FORTIFY_INLINE void *memcpy(void *dst, const void *src, size_t size) { size_t dst_size = __builtin_object_size(dst, 0); size_t src_size = __builtin_object_size(src, 0); if (__builtin_constant_p(size)) { /* Compile-time */ if (dst_size < size) __write_overflow(); if (src_size < size) __read_overflow2(); } if (dst_size < size || src_size < size) /* Run-time */ fortify_panic(__func__); return __underlying_memcpy(dst, src, size); } ```
`__builtin_object_size(OBJ, `**`MODE`**`)` MODE 0: bytes to end of entire structure MODE 1: bytes to end of structure *member* ```cpp[1-14|2,8,12|3,9,13|5,10,14] struct something { int count; /* 4 bytes */ char name[8]; /* 8 bytes */ int secret; /* 4 bytes */ char blob[]; /* flexible array */ } *instance; /* 16 bytes total */ __builtin_object_size(&instance->count, 0) == 16 __builtin_object_size(instance->name, 0) == 12 __builtin_object_size(instance->blob, 0) == -1 __builtin_object_size(&instance->count, 1) == 4 __builtin_object_size(instance->name, 1) == 8 __builtin_object_size(instance->blob, 1) == -1 ```
BleedingTooth targets members within the same composite structure... ```cpp[1-14|5|10] struct hci_dev { ... struct discovery_state { ... u8 last_adv_data[HCI_MAX_AD_LENGTH]; ... }; ... struct list_head { struct list_head *next; struct list_head *prev; } mgmt_pending; ... }; ``` ```cpp[1-5] static void store_pending_adv_report(struct hci_dev *hdev, ...) { struct discovery_state *d = &hdev->discovery; ... memcpy(d->last_adv_data, data, len); ```
Prior Fortified `memcpy()` with MODE = `0` (limits copy to end of surrounding structure) ```cpp[1-17|3-4] __FORTIFY_INLINE void *memcpy(void *dst, const void *src, size_t size) { size_t dst_size = __builtin_object_size(dst, 0); size_t src_size = __builtin_object_size(src, 0); if (__builtin_constant_p(size)) { /* Compile-time */ if (dst_size < size) __write_overflow(); if (src_size < size) __read_overflow2(); } if (dst_size < size || src_size < size) /* Run-time */ fortify_panic(__func__); return __underlying_memcpy(dst, src, size); } ```
_Strict_ Fortified `memcpy()` with MODE = **`1`** (limits copy to end of surrounding structure **member**) ```cpp[3-4] __FORTIFY_INLINE void *memcpy(void *dst, const void *src, size_t size) { size_t dst_size = __builtin_object_size(dst, 1); size_t src_size = __builtin_object_size(src, 1); if (__builtin_constant_p(size)) { /* Compile-time */ if (dst_size < size) __write_overflow(); if (src_size < size) __read_overflow2(); } if (dst_size < size || src_size < size) /* Run-time */ fortify_panic(__func__); return __underlying_memcpy(dst, src, size); } ```
Counting the `memcpy()` calls on a recent x86_64 allmodconfig build: - 35,294 `memcpy()` calls total: - 22,129 (62.7%) dest buffer size **not** known at compile time - 11,889 (33.7%) dest buffer size **and** copy size **known at compile time** - 1,276 (3.6%) dest buffer size **known** but copy size is **dynamic** Checking the 11 mentioned `memcpy()` flaws above, _all_ could be mitigated by adding dynamic copy size checks (1,276 instances). 🤩 This implies that we may have gained two orders of magnitude additional coverage over unknown flaws (11 were known and the remaining 1,265 more **might** be flaws), but where less than 4% of all `memcpy()` calls may be getting a false positive run-time warning.
But that would be too easy... Those 11,889 cases of "known buffer and copy size" actually needed to be fixed first because of existing **intentional** overflows, which would always trigger at runtime. 😞 Luckily, they are detectable at compile time, so they can all be fixed.
Kernel had many intentional cross-member `memcpy()` overflows ... ```cpp[1-14|4,14|4-6,12,14] struct mwl8k_cmd_set_key { ... __u8 key_material[MAX_ENCR_KEY_LENGTH]; __u8 tkip_tx_mic_key[MIC_KEY_LENGTH]; __u8 tkip_rx_mic_key[MIC_KEY_LENGTH]; ... }; keymlen = MAX_ENCR_KEY_LENGTH + 2 * MIC_KEY_LENGTH; ... memcpy(cmd->key_material, key->key, keymlen); ```
... which could be a simple fix of adding a named struct ... ```cpp[3,7,14] struct mwl8k_cmd_set_key { ... struct { __u8 key_material[MAX_ENCR_KEY_LENGTH]; __u8 tkip_tx_mic_key[MIC_KEY_LENGTH]; __u8 tkip_rx_mic_key[MIC_KEY_LENGTH]; } tkip; ... }; keymlen = MAX_ENCR_KEY_LENGTH + 2 * MIC_KEY_LENGTH; ... memcpy(cmd->tkip, key->key, keymlen); ```
... but now everything must include the name of the named struct ... ```cpp[3,7,12-14] struct mwl8k_cmd_set_key { ... struct { __u8 key_material[MAX_ENCR_KEY_LENGTH]; __u8 tkip_tx_mic_key[MIC_KEY_LENGTH]; __u8 tkip_rx_mic_key[MIC_KEY_LENGTH]; } tkip; ... }; diff ... - do_something_with(cmd->key_material); + do_something_with(cmd->tkip.key_material); ```
... so invent `struct_group()` to provide both: ```cpp[3,7,12-14] struct mwl8k_cmd_set_key { ... struct_group(tkip, __u8 key_material[MAX_ENCR_KEY_LENGTH]; __u8 tkip_tx_mic_key[MIC_KEY_LENGTH]; __u8 tkip_rx_mic_key[MIC_KEY_LENGTH]; ); ... }; /* Accessible either way: */ do_something_with(cmd->key_material); do_something_with(cmd->tkip.key_material); ```
### `struct_group()` Creates a union of both an _anonymous_ struct **and** a _named_ struct with identical members, for the best of both worlds: ```cpp #define struct_group(NAME, MEMBERS...) \ union { \ struct { MEMBERS }; \ struct { MEMBERS } NAME; \ } ``` (There are additional helpers for adding attributes and tags.)
`memcpy()` run-time checking now possible! ```cpp[1-17|3-4|6-10|11-13] __FORTIFY_INLINE void *memcpy(void *dst, const void *src, size_t size) { size_t dst_size = __builtin_object_size(dst, 1); size_t src_size = __builtin_object_size(src, 1); if (__builtin_constant_p(size)) { /* Compile-time */ if (dst_size < size) __write_overflow(); if (src_size < size) __read_overflow2(); } if (dst_size < size || src_size < size) fortify_panic(__func__); /* Run-time */ return __underlying_memcpy(dst, src, size); } ```
So now we're done! Oh, wait, no, let's look at that list again: - 35,294 memcpy() calls total: - **22,129 (62.7%) dest buffer size not known at compile time** - 11,889 (33.7%) dest buffer size and copy size known at compile time - 1,276 (3.6%) dest buffer size known but copy size is dynamic
We need to deal with the flexible array case: ```cpp[3-4,7,9,12] __FORTIFY_INLINE void *memcpy(void *dst, const void *src, size_t size) { size_t dst_size = __builtin_object_size(dst, 1); /* == SIZE_MAX (-1) */ size_t src_size = __builtin_object_size(src, 1); /* == SIZE_MAX (-1) */ if (__builtin_constant_p(size)) { /* Compile-time */ if (dst_size < size) __write_overflow(); if (src_size < size) __read_overflow2(); } if (dst_size < size || src_size < size) fortify_panic(__func__); /* Run-time */ return __underlying_memcpy(dst, src, size); } ```
```cpp[1-9] struct bitmap_image { ... u16 pixels; /* how many items in flex array */ ... u32 pixel_data[]; /* flexible array */ } *image; __builtin_object_size(image->pixel_data, 1) == -1 sizeof(image->pixel_data) == invalid expression ``` So we just need to handle these. Except, wait for it, the kernel was filled with "fake" flexible arrays...
```cpp[5,8,9] struct bitmap_image { ... u16 pixels; /* how many items in flex array */ ... u32 pixel_data[0]; /* GNU extension: 0-element array */ } *image; __builtin_object_size(image->pixel_data, 1) == -1 sizeof(image->pixel_data) == 0 ``` Wait, what? Why aren't these both "0"? Shouldn't the new `memcpy()` refuse to write any bytes into this "fixed size" array?
```cpp[5,8,9] struct bitmap_image { ... u16 pixels; /* how many items in flex array */ ... u32 pixel_data[1]; /* Ancient style: 1-element array */ } *image; __builtin_object_size(image->pixel_data, 1) == -1 sizeof(image->pixel_data) == 4 ``` Oh, or this one? This was the even older way to specify a dynamically sized trailing array.
```cpp[5,8,9] struct bitmap_image { ... u16 pixels; /* how many items in flex array */ ... u32 pixel_data[64]; /* Actual fixed-size array... */ } *image; __builtin_object_size(image->pixel_data, 1) == -1 sizeof(image->pixel_data) == 256 ``` 🤯 Oh no. Trailing arrays _of any size_ are treated as "fake" flexible arrays. Even after replacing all fake flexible arrays with real flexible arrays, the compilers must enforce "no sloppy flexible arrays" ... which wasn't even an option yet, but now `-fstrict-flex-arrays` is coming soon: https://reviews.llvm.org/D126864 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101836
So, how to solve dynamically sized destination overflows? Compiler doesn't know the destination size. But the bounds are stored _somewhere_. For structures with flexible arrays, this is usually very nearby: ```cpp[1-6,3] struct bitmap_image { ... u16 pixels; ... u32 pixel_data[]; } *image; ``` Many call sites already check the bounds, but there are always going to be bugs with open-coded checks. And how many of these places remember to check the storage size of the bounds variable?
There have been proposals to add bounds annotation to the C language for flexible arrays, which would be absolutely amazing, but we don't have it today: ```cpp[3,5] struct bitmap_image { ... u16 pixels; ... u32 pixel_data[.pixels]; /* Compiler could reason about bounds! */ } *image; ``` This exists for other languages (e.g. Ada), so why not C? It provides a way for the developer to express their _intent_ to the compiler, which can then use it for sanity checking.
Instead, we must systematically enforce bounds checking with a new API, and _disallow `memcpy()` destinations that aren't a constant size_. ```cpp[1-12|7|9|10|11] struct bitmap_image { ... u16 pixels; ... u32 pixel_data[]; } *image; size_t count = width * height; /* multiplication overflow? */ image = kzalloc(struct_size(image, pixels, count), GFP_KERNEL); /* NULL? */ image->pixels = count; /* truncation due to u16 vs size_t? */ memcpy(image->pixel_data, screen, sizeof(u32) * count); /* OOB write? */ ```
Toss the whole open-coded deserialization and use a new set of helpers designed to operate on "flexible array structures". ```cpp[1-12] struct bitmap_image { ... u16 pixels; ... u32 pixel_data[]; } *image = NULL; size_t count = width * height; /* multiplication overflow? */ //image = kzalloc(struct_size(image, pixels, count), GFP_KERNEL); //image->pixels = count; //memcpy(image->pixel_data, screen, sizeof(u32) * count); rc = mem_to_flex_dup(&image, pixel_data, pixels, screen, count, GFP_KERNEL); ```
And if the memory has already been allocated, different helpers are available: ```cpp[1-13] struct bitmap_image { ... u16 pixels; ... u32 pixel_data[]; } *image; size_t count = width * height; /* multiplication overflow? */ image = kzalloc(struct_size(image, pixels, count), GFP_KERNEL); image->pixels = count; //memcpy(image->pixel_data, screen, sizeof(u32) * count); mem_to_flex(image, pixel_data, pixels, screen, width * height); ```
How about a real world example? CVE-2021-43267 [Linux TIPC Remote Code Execution](https://www.sentinelone.com/labs/tipc-remote-linux-kernel-heap-overflow-allows-arbitrary-code-execution/) Heap overflow of a very simple flexible array structure: ```cpp #define TIPC_AEAD_ALG_NAME (32) struct tipc_aead_key { char alg_name[TIPC_AEAD_ALG_NAME]; unsigned int keylen; /* in bytes */ char key[]; }; ... memcpy(skey->key, data + TIPC_AEAD_ALG_NAME + sizeof(__be32), skey->keylen); ```
`net/tipc/crypto.c`: ```cpp[1-13|3,7|11,13] struct tipc_aead_key *skey; u8 *data = msg_data(hdr); u16 size = msg_data_sz(hdr); /* (also: msg_data_sz returns u32) */ ... skey = kmalloc(size, GFP_ATOMIC); if (!skey) return -ENOMEM; ... skey->keylen = ntohl(*((__be32 *)(data + TIPC_AEAD_ALG_NAME))); // > size? memcpy(skey->alg_name, data, TIPC_AEAD_ALG_NAME); /* Fixed size */ memcpy(skey->key, data + TIPC_AEAD_ALG_NAME + sizeof(__be32), skey->keylen); ``` (Checking for `data` OOB _read_ not included for brevity...)
`net/tipc/crypto.c`: ```cpp[1-13] struct tipc_aead_key *skey; u8 *data = msg_data(hdr); u16 size = msg_data_sz(hdr); /* (also: msg_data_sz returns u32) */ ... skey = kmalloc(size, GFP_ATOMIC); if (!skey) return -ENOMEM; ... skey->keylen = ntohl(*((__be32 *)(data + TIPC_AEAD_ALG_NAME))); // > size? memcpy(skey->alg_name, data, TIPC_AEAD_ALG_NAME); /* Fixed size */ memcpy(skey->key, data + TIPC_AEAD_ALG_NAME + sizeof(__be32), skey->keylen); ``` (Checking for `data` OOB _read_ not included for brevity...)
Using a new API that checks everything: ```cpp[1-10] struct tipc_aead_key *skey = NULL u8 *data = msg_data(hdr); int rc; ... rc = mem_to_flex_dup(&skey, data, ntohl(*((__be32 *)(data + TIPC_AEAD_ALG_NAME))), GFP_ATOMIC); if (rc) return rc; memcpy(skey->alg_name, data, TIPC_AEAD_ALG_NAME); /* Fixed size */ ``` (Checking for `data` OOB _read_ not included for brevity...)
```cpp[3-6] struct tipc_aead_key { char alg_name[TIPC_AEAD_ALG_NAME]; unsigned int keylen; char key[]; }; ``` Early design of `mem_to_flex_dup()` needed callers to include the flex-array and flex-array count member names as args ... every time. 😠```cpp[1-4|2] rc = mem_to_flex_dup(&skey, data, key, key_len, ntohl(*((__be32 *)(data + TIPC_AEAD_ALG_NAME))), GFP_ATOMIC); ```
```cpp[3-6] struct tipc_aead_key { char alg_name[TIPC_AEAD_ALG_NAME]; bounded_flex_array( unsigned int, keylen, char, key ); }; ``` Instead, created member name aliases with new helper `bounded_flex_array()`, to be used internally. Now member names are dropped entirely from the callers: ```cpp[1-4] rc = mem_to_flex_dup(&skey, data, ntohl(*((__be32 *)(data + TIPC_AEAD_ALG_NAME))), GFP_ATOMIC); ```
`bounded_flex_array()` helper for a FAS (flexible array struct): ```cpp #define DECLARE_FAS_COUNT(TYPE, NAME) \ union { \ TYPE __flex_array_elements_count; \ TYPE NAME; \ } #define DECLARE_FAS_ARRAY(TYPE, NAME) \ union { \ DECLARE_FLEX_ARRAY(TYPE, __flex_array_elements); \ DECLARE_FLEX_ARRAY(TYPE, NAME); \ } #define DECLARE_FLEX_ARRAY(TYPE, NAME) \ struct { \ struct { } __empty_ ## NAME; \ TYPE NAME[]; \ } #define bounded_flex_array(COUNT_TYPE, COUNT_NAME, ARRAY_TYPE, ARRAY_NAME) \ DECLARE_FAS_COUNT(COUNT_TYPE, COUNT_NAME); \ DECLARE_FAS_ARRAY(ARRAY_TYPE, ARRAY_NAME) ```
And this can will also be forward-compatible to the future "bounded flexible array" C language syntax: ```cpp[1,4] #ifdef CC_HAS_BOUNDED_FLEX_ARRAYS #define bounded_flex_array(COUNT_TYPE, COUNT_NAME, ARRAY_TYPE, ARRAY_NAME) \ DECLARE_FAS_COUNT(COUNT_TYPE, COUNT_NAME); \ ARRAY_TYPE ARRAY_NAME[.COUNT_NAME] #else #define bounded_flex_array(COUNT_TYPE, COUNT_NAME, ARRAY_TYPE, ARRAY_NAME) \ DECLARE_FAS_COUNT(COUNT_TYPE, COUNT_NAME); \ DECLARE_FAS_ARRAY(ARRAY_TYPE, ARRAY_NAME) #endif ```
```cpp[1-23|1,22,23|1,2,5,10,14,20-22|3,4|6|7,8|9|6-11|12|13-15|16|17|18|19-22] #define mem_to_flex_dup(_alloc, _src, _count, _gfp) __must_check_errno(({ \ int rc = -EINVAL; \ typeof(*(_alloc)) *p; \ size_t alloc_bytes, copy_bytes; \ do { \ if (_count > type_max(typeof(p->__flex_array_elements_count)) || \ check_mul_overflow(sizeof(*p->__flex_array_elements), _count, \ ©_bytes) || \ check_add_overflow(copy_bytes, sizeof(*p), &alloc_bytes)) { \ rc = -E2BIG; break; \ } \ p = kmalloc(alloc_bytes, _gfp); \ if (!p) { \ rc = -ENOMEM; break; \ } \ memset(p, 0, alloc_bytes - copy_bytes); \ __builtin_memcpy(p->__flex_array_elements, _src, copy_bytes); \ p->__flex_array_elements_count = _count; \ *_alloc = p; \ rc = 0; \ } while (0); \ rc; \ })) ```
## Released kernels - v5.16: _released January 9th_ - `struct_group()` (and related helpers) landed - almost all flexible array conversions done - v5.17: _released March 20th_ - all `struct_group()` conversions landed - almost all `-Warray-bounds` warnings fixed - v5.18: _released May 22nd_ - compile-time `memcpy()` enforcement landed - `-Warray-bounds` warning enabled globally
## Scheduled kernels - v5.19: _July?_ - `unsafe_memcpy()` landed - 200 more `-Warray-bounds` warnings to fix from GCC 12 - v5.20: _October?_ - `mem_to_flex_dup()` and related helpers landing - run-time `memcpy()` warnings landing
## Questions and Feedback? **Thank you for your attention!** Kees ("Case") Cook [@kees_cook](https://twitter.com/kees_cook) keescook@chromium.org https://outflux.net/slides/2022/lss-na/