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

batman-adv: Add flex array to struct batadv_tvlv_tt_data

The "struct batadv_tvlv_tt_data" uses a dynamically sized set of
trailing elements. Specifically, it uses an array of structures of type
"batadv_tvlv_tt_vlan_data". So, use the preferred way in the kernel
declaring a flexible array [1].

At the same time, prepare for the coming implementation by GCC and Clang
of the __counted_by attribute. Flexible array members annotated with
__counted_by can have their accesses bounds-checked at run-time via
CONFIG_UBSAN_BOUNDS (for array indexing) and CONFIG_FORTIFY_SOURCE (for
strcpy/memcpy-family functions). In this case, it is important to note
that the attribute used is specifically __counted_by_be since variable
"num_vlan" is of type __be16.

The following change to the "batadv_tt_tvlv_ogm_handler_v1" function:

- tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(tt_data + 1);
- tt_change = (struct batadv_tvlv_tt_change *)(tt_vlan + num_vlan);

+ tt_change = (struct batadv_tvlv_tt_change *)((void *)tt_data
+ + flex_size);

is intended to prevent the compiler from generating an "out-of-bounds"
notification due to the __counted_by attribute. The compiler can do a
pointer calculation using the vlan_data flexible array memory, or in
other words, this may be calculated as an array offset, since it is the
same as:

&tt_data->vlan_data[num_vlan]

Therefore, we go past the end of the array. In other "multiple trailing
flexible array" situations, this has been solved by addressing from the
base pointer, since the compiler either knows the full allocation size
or it knows nothing about it (this case, since it came from a "void *"
function argument).

The order in which the structure batadv_tvlv_tt_data and the structure
batadv_tvlv_tt_vlan_data are defined must be swap to avoid an incomplete
type error.

Also, avoid the open-coded arithmetic in memory allocator functions [2]
using the "struct_size" macro and use the "flex_array_size" helper to
clarify some calculations, when possible.

Moreover, the new structure member also allow us to avoid the open-coded
arithmetic on pointers in some situations. Take advantage of this.

This code was detected with the help of Coccinelle, and audited and
modified manually.

Link: https://www.kernel.org/doc/html/next/process/deprecated.html#zero-length-and-one-element-arrays [1]
Link: https://www.kernel.org/doc/html/next/process/deprecated.html#open-coded-arithmetic-in-allocator-arguments [2]
Reviewed-by: Kees Cook <keescook@chromium.org>
Signed-off-by: Erick Archer <erick.archer@outlook.com>
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Signed-off-by: Simon Wunderlich <sw@simonwunderlich.de>

authored by

Erick Archer and committed by
Simon Wunderlich
4436df47 0f4e6f94

+36 -42
+16 -13
include/uapi/linux/batadv_packet.h
··· 9 9 10 10 #include <asm/byteorder.h> 11 11 #include <linux/if_ether.h> 12 + #include <linux/stddef.h> 12 13 #include <linux/types.h> 13 14 14 15 /** ··· 594 593 }; 595 594 596 595 /** 597 - * struct batadv_tvlv_tt_data - tt data propagated through the tt tvlv container 598 - * @flags: translation table flags (see batadv_tt_data_flags) 599 - * @ttvn: translation table version number 600 - * @num_vlan: number of announced VLANs. In the TVLV this struct is followed by 601 - * one batadv_tvlv_tt_vlan_data object per announced vlan 602 - */ 603 - struct batadv_tvlv_tt_data { 604 - __u8 flags; 605 - __u8 ttvn; 606 - __be16 num_vlan; 607 - }; 608 - 609 - /** 610 596 * struct batadv_tvlv_tt_vlan_data - vlan specific tt data propagated through 611 597 * the tt tvlv container 612 598 * @crc: crc32 checksum of the entries belonging to this vlan ··· 604 616 __be32 crc; 605 617 __be16 vid; 606 618 __u16 reserved; 619 + }; 620 + 621 + /** 622 + * struct batadv_tvlv_tt_data - tt data propagated through the tt tvlv container 623 + * @flags: translation table flags (see batadv_tt_data_flags) 624 + * @ttvn: translation table version number 625 + * @num_vlan: number of announced VLANs. In the TVLV this struct is followed by 626 + * one batadv_tvlv_tt_vlan_data object per announced vlan 627 + * @vlan_data: array of batadv_tvlv_tt_vlan_data objects 628 + */ 629 + struct batadv_tvlv_tt_data { 630 + __u8 flags; 631 + __u8 ttvn; 632 + __be16 num_vlan; 633 + struct batadv_tvlv_tt_vlan_data vlan_data[] __counted_by_be(num_vlan); 607 634 }; 608 635 609 636 /**
+20 -29
net/batman-adv/translation-table.c
··· 28 28 #include <linux/net.h> 29 29 #include <linux/netdevice.h> 30 30 #include <linux/netlink.h> 31 + #include <linux/overflow.h> 31 32 #include <linux/rculist.h> 32 33 #include <linux/rcupdate.h> 33 34 #include <linux/skbuff.h> ··· 857 856 num_entries += atomic_read(&vlan->tt.num_entries); 858 857 } 859 858 860 - change_offset = sizeof(**tt_data); 861 - change_offset += num_vlan * sizeof(*tt_vlan); 859 + change_offset = struct_size(*tt_data, vlan_data, num_vlan); 862 860 863 861 /* if tt_len is negative, allocate the space needed by the full table */ 864 862 if (*tt_len < 0) ··· 876 876 (*tt_data)->ttvn = atomic_read(&orig_node->last_ttvn); 877 877 (*tt_data)->num_vlan = htons(num_vlan); 878 878 879 - tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(*tt_data + 1); 879 + tt_vlan = (*tt_data)->vlan_data; 880 880 hlist_for_each_entry(vlan, &orig_node->vlan_list, list) { 881 881 tt_vlan->vid = htons(vlan->vid); 882 882 tt_vlan->crc = htonl(vlan->tt.crc); ··· 936 936 total_entries += vlan_entries; 937 937 } 938 938 939 - change_offset = sizeof(**tt_data); 940 - change_offset += num_vlan * sizeof(*tt_vlan); 939 + change_offset = struct_size(*tt_data, vlan_data, num_vlan); 941 940 942 941 /* if tt_len is negative, allocate the space needed by the full table */ 943 942 if (*tt_len < 0) ··· 955 956 (*tt_data)->ttvn = atomic_read(&bat_priv->tt.vn); 956 957 (*tt_data)->num_vlan = htons(num_vlan); 957 958 958 - tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(*tt_data + 1); 959 + tt_vlan = (*tt_data)->vlan_data; 959 960 hlist_for_each_entry(vlan, &bat_priv->softif_vlan_list, list) { 960 961 vlan_entries = atomic_read(&vlan->tt.num_entries); 961 962 if (vlan_entries < 1) ··· 2915 2916 { 2916 2917 struct batadv_tvlv_tt_data *tvlv_tt_data = NULL; 2917 2918 struct batadv_tt_req_node *tt_req_node = NULL; 2918 - struct batadv_tvlv_tt_vlan_data *tt_vlan_req; 2919 2919 struct batadv_hard_iface *primary_if; 2920 2920 bool ret = false; 2921 2921 int i, size; ··· 2930 2932 if (!tt_req_node) 2931 2933 goto out; 2932 2934 2933 - size = sizeof(*tvlv_tt_data) + sizeof(*tt_vlan_req) * num_vlan; 2935 + size = struct_size(tvlv_tt_data, vlan_data, num_vlan); 2934 2936 tvlv_tt_data = kzalloc(size, GFP_ATOMIC); 2935 2937 if (!tvlv_tt_data) 2936 2938 goto out; ··· 2942 2944 /* send all the CRCs within the request. This is needed by intermediate 2943 2945 * nodes to ensure they have the correct table before replying 2944 2946 */ 2945 - tt_vlan_req = (struct batadv_tvlv_tt_vlan_data *)(tvlv_tt_data + 1); 2946 2947 for (i = 0; i < num_vlan; i++) { 2947 - tt_vlan_req->vid = tt_vlan->vid; 2948 - tt_vlan_req->crc = tt_vlan->crc; 2948 + tvlv_tt_data->vlan_data[i].vid = tt_vlan->vid; 2949 + tvlv_tt_data->vlan_data[i].crc = tt_vlan->crc; 2949 2950 2950 - tt_vlan_req++; 2951 2951 tt_vlan++; 2952 2952 } 2953 2953 ··· 2997 3001 struct batadv_orig_node *res_dst_orig_node = NULL; 2998 3002 struct batadv_tvlv_tt_change *tt_change; 2999 3003 struct batadv_tvlv_tt_data *tvlv_tt_data = NULL; 3000 - struct batadv_tvlv_tt_vlan_data *tt_vlan; 3001 3004 bool ret = false, full_table; 3002 3005 u8 orig_ttvn, req_ttvn; 3003 3006 u16 tvlv_len; ··· 3019 3024 orig_ttvn = (u8)atomic_read(&req_dst_orig_node->last_ttvn); 3020 3025 req_ttvn = tt_data->ttvn; 3021 3026 3022 - tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(tt_data + 1); 3023 3027 /* this node doesn't have the requested data */ 3024 3028 if (orig_ttvn != req_ttvn || 3025 - !batadv_tt_global_check_crc(req_dst_orig_node, tt_vlan, 3029 + !batadv_tt_global_check_crc(req_dst_orig_node, tt_data->vlan_data, 3026 3030 ntohs(tt_data->num_vlan))) 3027 3031 goto out; 3028 3032 ··· 3364 3370 struct batadv_orig_node *orig_node = NULL; 3365 3371 struct batadv_tvlv_tt_change *tt_change; 3366 3372 u8 *tvlv_ptr = (u8 *)tt_data; 3367 - u16 change_offset; 3368 3373 3369 3374 batadv_dbg(BATADV_DBG_TT, bat_priv, 3370 3375 "Received TT_RESPONSE from %pM for ttvn %d t_size: %d [%c]\n", ··· 3376 3383 3377 3384 spin_lock_bh(&orig_node->tt_lock); 3378 3385 3379 - change_offset = sizeof(struct batadv_tvlv_tt_vlan_data); 3380 - change_offset *= ntohs(tt_data->num_vlan); 3381 - change_offset += sizeof(*tt_data); 3382 - tvlv_ptr += change_offset; 3386 + tvlv_ptr += struct_size(tt_data, vlan_data, ntohs(tt_data->num_vlan)); 3383 3387 3384 3388 tt_change = (struct batadv_tvlv_tt_change *)tvlv_ptr; 3385 3389 if (tt_data->flags & BATADV_TT_FULL_TABLE) { ··· 3975 3985 u8 flags, void *tvlv_value, 3976 3986 u16 tvlv_value_len) 3977 3987 { 3978 - struct batadv_tvlv_tt_vlan_data *tt_vlan; 3979 3988 struct batadv_tvlv_tt_change *tt_change; 3980 3989 struct batadv_tvlv_tt_data *tt_data; 3981 3990 u16 num_entries, num_vlan; 3991 + size_t flex_size; 3982 3992 3983 3993 if (tvlv_value_len < sizeof(*tt_data)) 3984 3994 return; ··· 3988 3998 3989 3999 num_vlan = ntohs(tt_data->num_vlan); 3990 4000 3991 - if (tvlv_value_len < sizeof(*tt_vlan) * num_vlan) 4001 + flex_size = flex_array_size(tt_data, vlan_data, num_vlan); 4002 + if (tvlv_value_len < flex_size) 3992 4003 return; 3993 4004 3994 - tt_vlan = (struct batadv_tvlv_tt_vlan_data *)(tt_data + 1); 3995 - tt_change = (struct batadv_tvlv_tt_change *)(tt_vlan + num_vlan); 3996 - tvlv_value_len -= sizeof(*tt_vlan) * num_vlan; 4005 + tt_change = (struct batadv_tvlv_tt_change *)((void *)tt_data 4006 + + flex_size); 4007 + tvlv_value_len -= flex_size; 3997 4008 3998 4009 num_entries = batadv_tt_entries(tvlv_value_len); 3999 4010 4000 - batadv_tt_update_orig(bat_priv, orig, tt_vlan, num_vlan, tt_change, 4001 - num_entries, tt_data->ttvn); 4011 + batadv_tt_update_orig(bat_priv, orig, tt_data->vlan_data, num_vlan, 4012 + tt_change, num_entries, tt_data->ttvn); 4002 4013 } 4003 4014 4004 4015 /** ··· 4030 4039 tt_data = tvlv_value; 4031 4040 tvlv_value_len -= sizeof(*tt_data); 4032 4041 4033 - tt_vlan_len = sizeof(struct batadv_tvlv_tt_vlan_data); 4034 - tt_vlan_len *= ntohs(tt_data->num_vlan); 4042 + tt_vlan_len = flex_array_size(tt_data, vlan_data, 4043 + ntohs(tt_data->num_vlan)); 4035 4044 4036 4045 if (tvlv_value_len < tt_vlan_len) 4037 4046 return NET_RX_SUCCESS;