sfxge(4): check buffer size for hash flags

The efx_rx_scale_hash_flags_get interface is unsafe, as it does not
have an argument for the size of the output buffer used to return
the flags. While the only caller currently supplies a sufficiently
large buffer, this should be checked at runtime to avoid writing
past the end of the buffer.

Submitted by:   Ivan Malov <ivan.malov at oktetlabs.ru>
Sponsored by:   Solarflare Communications, Inc.
Differential Revision:  https://reviews.freebsd.org/D18267
This commit is contained in:
Andrew Rybchenko 2018-11-30 07:07:09 +00:00
parent bd1be89ebe
commit 94a7dab5bf
3 changed files with 81 additions and 45 deletions

View File

@ -2396,7 +2396,8 @@ extern __checkReturn efx_rc_t
efx_rx_scale_hash_flags_get(
__in efx_nic_t *enp,
__in efx_rx_hash_alg_t hash_alg,
__inout_ecount(EFX_RX_HASH_NFLAGS) unsigned int *flagsp,
__out_ecount_part(max_nflags, *nflagsp) unsigned int *flagsp,
__in unsigned int max_nflags,
__out unsigned int *nflagsp);
extern __checkReturn efx_rc_t

View File

@ -61,6 +61,7 @@
#define __out_opt
#define __out_ecount(_n)
#define __out_ecount_opt(_n)
#define __out_ecount_part(_n, _l)
#define __out_bcount(_n)
#define __out_bcount_opt(_n)
#define __out_bcount_part(_n, _l)

View File

@ -327,13 +327,12 @@ fail1:
efx_rx_scale_hash_flags_get(
__in efx_nic_t *enp,
__in efx_rx_hash_alg_t hash_alg,
__inout_ecount(EFX_RX_HASH_NFLAGS) unsigned int *flagsp,
__out_ecount_part(max_nflags, *nflagsp) unsigned int *flagsp,
__in unsigned int max_nflags,
__out unsigned int *nflagsp)
{
efx_nic_cfg_t *encp = &enp->en_nic_cfg;
boolean_t l4;
boolean_t additional_modes;
unsigned int *entryp = flagsp;
unsigned int nflags = 0;
efx_rc_t rc;
if (flagsp == NULL || nflagsp == NULL) {
@ -342,56 +341,90 @@ efx_rx_scale_hash_flags_get(
}
if ((encp->enc_rx_scale_hash_alg_mask & (1U << hash_alg)) == 0) {
*nflagsp = 0;
return 0;
nflags = 0;
goto done;
}
l4 = encp->enc_rx_scale_l4_hash_supported;
additional_modes = encp->enc_rx_scale_additional_modes_supported;
#define LIST_FLAGS(_entryp, _class, _l4_hashing, _additional_modes) \
do { \
if (_l4_hashing) { \
*(_entryp++) = EFX_RX_HASH(_class, 4TUPLE); \
\
if (_additional_modes) { \
*(_entryp++) = \
EFX_RX_HASH(_class, 2TUPLE_DST); \
*(_entryp++) = \
EFX_RX_HASH(_class, 2TUPLE_SRC); \
} \
} \
\
*(_entryp++) = EFX_RX_HASH(_class, 2TUPLE); \
\
if (_additional_modes) { \
*(_entryp++) = EFX_RX_HASH(_class, 1TUPLE_DST); \
*(_entryp++) = EFX_RX_HASH(_class, 1TUPLE_SRC); \
} \
\
*(_entryp++) = EFX_RX_HASH(_class, DISABLE); \
\
_NOTE(CONSTANTCONDITION) \
/* Helper to add flags word to flags array without buffer overflow */
#define INSERT_FLAGS(_flags) \
do { \
if (nflags >= max_nflags) { \
rc = E2BIG; \
goto fail2; \
} \
*(flagsp + nflags) = (_flags); \
nflags++; \
\
_NOTE(CONSTANTCONDITION) \
} while (B_FALSE)
LIST_FLAGS(entryp, IPV4_TCP, l4, additional_modes);
LIST_FLAGS(entryp, IPV6_TCP, l4, additional_modes);
if (additional_modes) {
LIST_FLAGS(entryp, IPV4_UDP, l4, additional_modes);
LIST_FLAGS(entryp, IPV6_UDP, l4, additional_modes);
if (encp->enc_rx_scale_l4_hash_supported != B_FALSE) {
INSERT_FLAGS(EFX_RX_HASH(IPV4_TCP, 4TUPLE));
INSERT_FLAGS(EFX_RX_HASH(IPV6_TCP, 4TUPLE));
}
LIST_FLAGS(entryp, IPV4, B_FALSE, additional_modes);
LIST_FLAGS(entryp, IPV6, B_FALSE, additional_modes);
if ((encp->enc_rx_scale_l4_hash_supported != B_FALSE) &&
(encp->enc_rx_scale_additional_modes_supported != B_FALSE)) {
INSERT_FLAGS(EFX_RX_HASH(IPV4_TCP, 2TUPLE_DST));
INSERT_FLAGS(EFX_RX_HASH(IPV4_TCP, 2TUPLE_SRC));
#undef LIST_FLAGS
INSERT_FLAGS(EFX_RX_HASH(IPV6_TCP, 2TUPLE_DST));
INSERT_FLAGS(EFX_RX_HASH(IPV6_TCP, 2TUPLE_SRC));
*nflagsp = (unsigned int)(entryp - flagsp);
EFSYS_ASSERT3U(*nflagsp, <=, EFX_RX_HASH_NFLAGS);
INSERT_FLAGS(EFX_RX_HASH(IPV4_UDP, 4TUPLE));
INSERT_FLAGS(EFX_RX_HASH(IPV4_UDP, 2TUPLE_DST));
INSERT_FLAGS(EFX_RX_HASH(IPV4_UDP, 2TUPLE_SRC));
INSERT_FLAGS(EFX_RX_HASH(IPV6_UDP, 4TUPLE));
INSERT_FLAGS(EFX_RX_HASH(IPV6_UDP, 2TUPLE_DST));
INSERT_FLAGS(EFX_RX_HASH(IPV6_UDP, 2TUPLE_SRC));
}
INSERT_FLAGS(EFX_RX_HASH(IPV4_TCP, 2TUPLE));
INSERT_FLAGS(EFX_RX_HASH(IPV6_TCP, 2TUPLE));
INSERT_FLAGS(EFX_RX_HASH(IPV4, 2TUPLE));
INSERT_FLAGS(EFX_RX_HASH(IPV6, 2TUPLE));
if (encp->enc_rx_scale_additional_modes_supported != B_FALSE) {
INSERT_FLAGS(EFX_RX_HASH(IPV4_TCP, 1TUPLE_DST));
INSERT_FLAGS(EFX_RX_HASH(IPV4_TCP, 1TUPLE_SRC));
INSERT_FLAGS(EFX_RX_HASH(IPV6_TCP, 1TUPLE_DST));
INSERT_FLAGS(EFX_RX_HASH(IPV6_TCP, 1TUPLE_SRC));
INSERT_FLAGS(EFX_RX_HASH(IPV4_UDP, 2TUPLE));
INSERT_FLAGS(EFX_RX_HASH(IPV4_UDP, 1TUPLE_DST));
INSERT_FLAGS(EFX_RX_HASH(IPV4_UDP, 1TUPLE_SRC));
INSERT_FLAGS(EFX_RX_HASH(IPV6_UDP, 2TUPLE));
INSERT_FLAGS(EFX_RX_HASH(IPV6_UDP, 1TUPLE_DST));
INSERT_FLAGS(EFX_RX_HASH(IPV6_UDP, 1TUPLE_SRC));
INSERT_FLAGS(EFX_RX_HASH(IPV4, 1TUPLE_DST));
INSERT_FLAGS(EFX_RX_HASH(IPV4, 1TUPLE_SRC));
INSERT_FLAGS(EFX_RX_HASH(IPV6, 1TUPLE_DST));
INSERT_FLAGS(EFX_RX_HASH(IPV6, 1TUPLE_SRC));
}
INSERT_FLAGS(EFX_RX_HASH(IPV4_TCP, DISABLE));
INSERT_FLAGS(EFX_RX_HASH(IPV6_TCP, DISABLE));
INSERT_FLAGS(EFX_RX_HASH(IPV4_UDP, DISABLE));
INSERT_FLAGS(EFX_RX_HASH(IPV6_UDP, DISABLE));
INSERT_FLAGS(EFX_RX_HASH(IPV4, DISABLE));
INSERT_FLAGS(EFX_RX_HASH(IPV6, DISABLE));
#undef INSERT_FLAGS
done:
*nflagsp = nflags;
return (0);
fail2:
EFSYS_PROBE(fail2);
fail1:
EFSYS_PROBE1(fail1, efx_rc_t, rc);
@ -577,7 +610,8 @@ efx_rx_scale_mode_set(
/*
* Get the list of supported hash flags and sanitise the input.
*/
rc = efx_rx_scale_hash_flags_get(enp, alg, type_flags, &type_nflags);
rc = efx_rx_scale_hash_flags_get(enp, alg, type_flags,
EFX_ARRAY_SIZE(type_flags), &type_nflags);
if (rc != 0)
goto fail2;