mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-23 01:10:04 +00:00
![Thadeu Lima de Souza Cascardo](/assets/img/avatar_default.png)
When evaluating byteorder expressions with size 2, a union with 32-bit and
16-bit members is used. Since the 16-bit members are aligned to 32-bit,
the array accesses will be out-of-bounds.
It may lead to a stack-out-of-bounds access like the one below:
[ 23.095215] ==================================================================
[ 23.095625] BUG: KASAN: stack-out-of-bounds in nft_byteorder_eval+0x13c/0x320
[ 23.096020] Read of size 2 at addr ffffc90000007948 by task ping/115
[ 23.096358]
[ 23.096456] CPU: 0 PID: 115 Comm: ping Not tainted 6.4.0+ #413
[ 23.096770] Call Trace:
[ 23.096910] <IRQ>
[ 23.097030] dump_stack_lvl+0x60/0xc0
[ 23.097218] print_report+0xcf/0x630
[ 23.097388] ? nft_byteorder_eval+0x13c/0x320
[ 23.097577] ? kasan_addr_to_slab+0xd/0xc0
[ 23.097760] ? nft_byteorder_eval+0x13c/0x320
[ 23.097949] kasan_report+0xc9/0x110
[ 23.098106] ? nft_byteorder_eval+0x13c/0x320
[ 23.098298] __asan_load2+0x83/0xd0
[ 23.098453] nft_byteorder_eval+0x13c/0x320
[ 23.098659] nft_do_chain+0x1c8/0xc50
[ 23.098852] ? __pfx_nft_do_chain+0x10/0x10
[ 23.099078] ? __kasan_check_read+0x11/0x20
[ 23.099295] ? __pfx___lock_acquire+0x10/0x10
[ 23.099535] ? __pfx___lock_acquire+0x10/0x10
[ 23.099745] ? __kasan_check_read+0x11/0x20
[ 23.099929] nft_do_chain_ipv4+0xfe/0x140
[ 23.100105] ? __pfx_nft_do_chain_ipv4+0x10/0x10
[ 23.100327] ? lock_release+0x204/0x400
[ 23.100515] ? nf_hook.constprop.0+0x340/0x550
[ 23.100779] nf_hook_slow+0x6c/0x100
[ 23.100977] ? __pfx_nft_do_chain_ipv4+0x10/0x10
[ 23.101223] nf_hook.constprop.0+0x334/0x550
[ 23.101443] ? __pfx_ip_local_deliver_finish+0x10/0x10
[ 23.101677] ? __pfx_nf_hook.constprop.0+0x10/0x10
[ 23.101882] ? __pfx_ip_rcv_finish+0x10/0x10
[ 23.102071] ? __pfx_ip_local_deliver_finish+0x10/0x10
[ 23.102291] ? rcu_read_lock_held+0x4b/0x70
[ 23.102481] ip_local_deliver+0xbb/0x110
[ 23.102665] ? __pfx_ip_rcv+0x10/0x10
[ 23.102839] ip_rcv+0x199/0x2a0
[ 23.102980] ? __pfx_ip_rcv+0x10/0x10
[ 23.103140] __netif_receive_skb_one_core+0x13e/0x150
[ 23.103362] ? __pfx___netif_receive_skb_one_core+0x10/0x10
[ 23.103647] ? mark_held_locks+0x48/0xa0
[ 23.103819] ? process_backlog+0x36c/0x380
[ 23.103999] __netif_receive_skb+0x23/0xc0
[ 23.104179] process_backlog+0x91/0x380
[ 23.104350] __napi_poll.constprop.0+0x66/0x360
[ 23.104589] ? net_rx_action+0x1cb/0x610
[ 23.104811] net_rx_action+0x33e/0x610
[ 23.105024] ? _raw_spin_unlock+0x23/0x50
[ 23.105257] ? __pfx_net_rx_action+0x10/0x10
[ 23.105485] ? mark_held_locks+0x48/0xa0
[ 23.105741] __do_softirq+0xfa/0x5ab
[ 23.105956] ? __dev_queue_xmit+0x765/0x1c00
[ 23.106193] do_softirq.part.0+0x49/0xc0
[ 23.106423] </IRQ>
[ 23.106547] <TASK>
[ 23.106670] __local_bh_enable_ip+0xf5/0x120
[ 23.106903] __dev_queue_xmit+0x789/0x1c00
[ 23.107131] ? __pfx___dev_queue_xmit+0x10/0x10
[ 23.107381] ? find_held_lock+0x8e/0xb0
[ 23.107585] ? lock_release+0x204/0x400
[ 23.107798] ? neigh_resolve_output+0x185/0x350
[ 23.108049] ? mark_held_locks+0x48/0xa0
[ 23.108265] ? neigh_resolve_output+0x185/0x350
[ 23.108514] neigh_resolve_output+0x246/0x350
[ 23.108753] ? neigh_resolve_output+0x246/0x350
[ 23.109003] ip_finish_output2+0x3c3/0x10b0
[ 23.109250] ? __pfx_ip_finish_output2+0x10/0x10
[ 23.109510] ? __pfx_nf_hook+0x10/0x10
[ 23.109732] __ip_finish_output+0x217/0x390
[ 23.109978] ip_finish_output+0x2f/0x130
[ 23.110207] ip_output+0xc9/0x170
[ 23.110404] ip_push_pending_frames+0x1a0/0x240
[ 23.110652] raw_sendmsg+0x102e/0x19e0
[ 23.110871] ? __pfx_raw_sendmsg+0x10/0x10
[ 23.111093] ? lock_release+0x204/0x400
[ 23.111304] ? __mod_lruvec_page_state+0x148/0x330
[ 23.111567] ? find_held_lock+0x8e/0xb0
[ 23.111777] ? find_held_lock+0x8e/0xb0
[ 23.111993] ? __rcu_read_unlock+0x7c/0x2f0
[ 23.112225] ? aa_sk_perm+0x18a/0x550
[ 23.112431] ? filemap_map_pages+0x4f1/0x900
[ 23.112665] ? __pfx_aa_sk_perm+0x10/0x10
[ 23.112880] ? find_held_lock+0x8e/0xb0
[ 23.113098] inet_sendmsg+0xa0/0xb0
[ 23.113297] ? inet_sendmsg+0xa0/0xb0
[ 23.113500] ? __pfx_inet_sendmsg+0x10/0x10
[ 23.113727] sock_sendmsg+0xf4/0x100
[ 23.113924] ? move_addr_to_kernel.part.0+0x4f/0xa0
[ 23.114190] __sys_sendto+0x1d4/0x290
[ 23.114391] ? __pfx___sys_sendto+0x10/0x10
[ 23.114621] ? __pfx_mark_lock.part.0+0x10/0x10
[ 23.114869] ? lock_release+0x204/0x400
[ 23.115076] ? find_held_lock+0x8e/0xb0
[ 23.115287] ? rcu_is_watching+0x23/0x60
[ 23.115503] ? __rseq_handle_notify_resume+0x6e2/0x860
[ 23.115778] ? __kasan_check_write+0x14/0x30
[ 23.116008] ? blkcg_maybe_throttle_current+0x8d/0x770
[ 23.116285] ? mark_held_locks+0x28/0xa0
[ 23.116503] ? do_syscall_64+0x37/0x90
[ 23.116713] __x64_sys_sendto+0x7f/0xb0
[ 23.116924] do_syscall_64+0x59/0x90
[ 23.117123] ? irqentry_exit_to_user_mode+0x25/0x30
[ 23.117387] ? irqentry_exit+0x77/0xb0
[ 23.117593] ? exc_page_fault+0x92/0x140
[ 23.117806] entry_SYSCALL_64_after_hwframe+0x6e/0xd8
[ 23.118081] RIP: 0033:0x7f744aee2bba
[ 23.118282] Code: d8 64 89 02 48 c7 c0 ff ff ff ff eb b8 0f 1f 00 f3 0f 1e fa 41 89 ca 64 8b 04 25 18 00 00 00 85 c0 75 15 b8 2c 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 7e c3 0f 1f 44 00 00 41 54 48 83 ec 30 44 89
[ 23.119237] RSP: 002b:00007ffd04a7c9f8 EFLAGS: 00000246 ORIG_RAX: 000000000000002c
[ 23.119644] RAX: ffffffffffffffda RBX: 00007ffd04a7e0a0 RCX: 00007f744aee2bba
[ 23.120023] RDX: 0000000000000040 RSI: 000056488e9e6300 RDI: 0000000000000003
[ 23.120413] RBP: 000056488e9e6300 R08: 00007ffd04a80320 R09: 0000000000000010
[ 23.120809] R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000040
[ 23.121219] R13: 00007ffd04a7dc38 R14: 00007ffd04a7ca00 R15: 00007ffd04a7e0a0
[ 23.121617] </TASK>
[ 23.121749]
[ 23.121845] The buggy address belongs to the virtual mapping at
[ 23.121845] [ffffc90000000000, ffffc90000009000) created by:
[ 23.121845] irq_init_percpu_irqstack+0x1cf/0x270
[ 23.122707]
[ 23.122803] The buggy address belongs to the physical page:
[ 23.123104] page:0000000072ac19f0 refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x24a09
[ 23.123609] flags: 0xfffffc0001000(reserved|node=0|zone=1|lastcpupid=0x1fffff)
[ 23.123998] page_type: 0xffffffff()
[ 23.124194] raw: 000fffffc0001000 ffffea0000928248 ffffea0000928248 0000000000000000
[ 23.124610] raw: 0000000000000000 0000000000000000 00000001ffffffff 0000000000000000
[ 23.125023] page dumped because: kasan: bad access detected
[ 23.125326]
[ 23.125421] Memory state around the buggy address:
[ 23.125682] ffffc90000007800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 23.126072] ffffc90000007880: 00 00 00 00 00 f1 f1 f1 f1 f1 f1 00 00 f2 f2 00
[ 23.126455] >ffffc90000007900: 00 00 00 00 00 00 00 00 00 f2 f2 f2 f2 00 00 00
[ 23.126840] ^
[ 23.127138] ffffc90000007980: 00 00 00 00 00 00 00 00 00 00 00 00 00 f3 f3 f3
[ 23.127522] ffffc90000007a00: f3 00 00 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1
[ 23.127906] ==================================================================
[ 23.128324] Disabling lock debugging due to kernel taint
Using simple s16 pointers for the 16-bit accesses fixes the problem. For
the 32-bit accesses, src and dst can be used directly.
Fixes: 96518518cc
("netfilter: add nftables")
Cc: stable@vger.kernel.org
Reported-by: Tanguy DUBROCA (@SidewayRE) from @Synacktiv working with ZDI
Signed-off-by: Thadeu Lima de Souza Cascardo <cascardo@canonical.com>
Reviewed-by: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
197 lines
4.7 KiB
C
197 lines
4.7 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net>
|
|
*
|
|
* Development of this code funded by Astaro AG (http://www.astaro.com/)
|
|
*/
|
|
|
|
#include <asm/unaligned.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/init.h>
|
|
#include <linux/module.h>
|
|
#include <linux/netlink.h>
|
|
#include <linux/netfilter.h>
|
|
#include <linux/netfilter/nf_tables.h>
|
|
#include <net/netfilter/nf_tables_core.h>
|
|
#include <net/netfilter/nf_tables.h>
|
|
|
|
struct nft_byteorder {
|
|
u8 sreg;
|
|
u8 dreg;
|
|
enum nft_byteorder_ops op:8;
|
|
u8 len;
|
|
u8 size;
|
|
};
|
|
|
|
void nft_byteorder_eval(const struct nft_expr *expr,
|
|
struct nft_regs *regs,
|
|
const struct nft_pktinfo *pkt)
|
|
{
|
|
const struct nft_byteorder *priv = nft_expr_priv(expr);
|
|
u32 *src = ®s->data[priv->sreg];
|
|
u32 *dst = ®s->data[priv->dreg];
|
|
u16 *s16, *d16;
|
|
unsigned int i;
|
|
|
|
s16 = (void *)src;
|
|
d16 = (void *)dst;
|
|
|
|
switch (priv->size) {
|
|
case 8: {
|
|
u64 src64;
|
|
|
|
switch (priv->op) {
|
|
case NFT_BYTEORDER_NTOH:
|
|
for (i = 0; i < priv->len / 8; i++) {
|
|
src64 = nft_reg_load64(&src[i]);
|
|
nft_reg_store64(&dst[i],
|
|
be64_to_cpu((__force __be64)src64));
|
|
}
|
|
break;
|
|
case NFT_BYTEORDER_HTON:
|
|
for (i = 0; i < priv->len / 8; i++) {
|
|
src64 = (__force __u64)
|
|
cpu_to_be64(nft_reg_load64(&src[i]));
|
|
nft_reg_store64(&dst[i], src64);
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case 4:
|
|
switch (priv->op) {
|
|
case NFT_BYTEORDER_NTOH:
|
|
for (i = 0; i < priv->len / 4; i++)
|
|
dst[i] = ntohl((__force __be32)src[i]);
|
|
break;
|
|
case NFT_BYTEORDER_HTON:
|
|
for (i = 0; i < priv->len / 4; i++)
|
|
dst[i] = (__force __u32)htonl(src[i]);
|
|
break;
|
|
}
|
|
break;
|
|
case 2:
|
|
switch (priv->op) {
|
|
case NFT_BYTEORDER_NTOH:
|
|
for (i = 0; i < priv->len / 2; i++)
|
|
d16[i] = ntohs((__force __be16)s16[i]);
|
|
break;
|
|
case NFT_BYTEORDER_HTON:
|
|
for (i = 0; i < priv->len / 2; i++)
|
|
d16[i] = (__force __u16)htons(s16[i]);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static const struct nla_policy nft_byteorder_policy[NFTA_BYTEORDER_MAX + 1] = {
|
|
[NFTA_BYTEORDER_SREG] = { .type = NLA_U32 },
|
|
[NFTA_BYTEORDER_DREG] = { .type = NLA_U32 },
|
|
[NFTA_BYTEORDER_OP] = NLA_POLICY_MAX(NLA_BE32, 255),
|
|
[NFTA_BYTEORDER_LEN] = NLA_POLICY_MAX(NLA_BE32, 255),
|
|
[NFTA_BYTEORDER_SIZE] = NLA_POLICY_MAX(NLA_BE32, 255),
|
|
};
|
|
|
|
static int nft_byteorder_init(const struct nft_ctx *ctx,
|
|
const struct nft_expr *expr,
|
|
const struct nlattr * const tb[])
|
|
{
|
|
struct nft_byteorder *priv = nft_expr_priv(expr);
|
|
u32 size, len;
|
|
int err;
|
|
|
|
if (tb[NFTA_BYTEORDER_SREG] == NULL ||
|
|
tb[NFTA_BYTEORDER_DREG] == NULL ||
|
|
tb[NFTA_BYTEORDER_LEN] == NULL ||
|
|
tb[NFTA_BYTEORDER_SIZE] == NULL ||
|
|
tb[NFTA_BYTEORDER_OP] == NULL)
|
|
return -EINVAL;
|
|
|
|
priv->op = ntohl(nla_get_be32(tb[NFTA_BYTEORDER_OP]));
|
|
switch (priv->op) {
|
|
case NFT_BYTEORDER_NTOH:
|
|
case NFT_BYTEORDER_HTON:
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
err = nft_parse_u32_check(tb[NFTA_BYTEORDER_SIZE], U8_MAX, &size);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
priv->size = size;
|
|
|
|
switch (priv->size) {
|
|
case 2:
|
|
case 4:
|
|
case 8:
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
err = nft_parse_u32_check(tb[NFTA_BYTEORDER_LEN], U8_MAX, &len);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
priv->len = len;
|
|
|
|
err = nft_parse_register_load(tb[NFTA_BYTEORDER_SREG], &priv->sreg,
|
|
priv->len);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
return nft_parse_register_store(ctx, tb[NFTA_BYTEORDER_DREG],
|
|
&priv->dreg, NULL, NFT_DATA_VALUE,
|
|
priv->len);
|
|
}
|
|
|
|
static int nft_byteorder_dump(struct sk_buff *skb,
|
|
const struct nft_expr *expr, bool reset)
|
|
{
|
|
const struct nft_byteorder *priv = nft_expr_priv(expr);
|
|
|
|
if (nft_dump_register(skb, NFTA_BYTEORDER_SREG, priv->sreg))
|
|
goto nla_put_failure;
|
|
if (nft_dump_register(skb, NFTA_BYTEORDER_DREG, priv->dreg))
|
|
goto nla_put_failure;
|
|
if (nla_put_be32(skb, NFTA_BYTEORDER_OP, htonl(priv->op)))
|
|
goto nla_put_failure;
|
|
if (nla_put_be32(skb, NFTA_BYTEORDER_LEN, htonl(priv->len)))
|
|
goto nla_put_failure;
|
|
if (nla_put_be32(skb, NFTA_BYTEORDER_SIZE, htonl(priv->size)))
|
|
goto nla_put_failure;
|
|
return 0;
|
|
|
|
nla_put_failure:
|
|
return -1;
|
|
}
|
|
|
|
static bool nft_byteorder_reduce(struct nft_regs_track *track,
|
|
const struct nft_expr *expr)
|
|
{
|
|
struct nft_byteorder *priv = nft_expr_priv(expr);
|
|
|
|
nft_reg_track_cancel(track, priv->dreg, priv->len);
|
|
|
|
return false;
|
|
}
|
|
|
|
static const struct nft_expr_ops nft_byteorder_ops = {
|
|
.type = &nft_byteorder_type,
|
|
.size = NFT_EXPR_SIZE(sizeof(struct nft_byteorder)),
|
|
.eval = nft_byteorder_eval,
|
|
.init = nft_byteorder_init,
|
|
.dump = nft_byteorder_dump,
|
|
.reduce = nft_byteorder_reduce,
|
|
};
|
|
|
|
struct nft_expr_type nft_byteorder_type __read_mostly = {
|
|
.name = "byteorder",
|
|
.ops = &nft_byteorder_ops,
|
|
.policy = nft_byteorder_policy,
|
|
.maxattr = NFTA_BYTEORDER_MAX,
|
|
.owner = THIS_MODULE,
|
|
};
|