mirror of
https://github.com/torvalds/linux.git
synced 2024-12-01 08:31:37 +00:00
807067bf01
syzkaller reported UAF in kcm_release(). [0]
The scenario is
1. Thread A builds a skb with MSG_MORE and sets kcm->seq_skb.
2. Thread A resumes building skb from kcm->seq_skb but is blocked
by sk_stream_wait_memory()
3. Thread B calls sendmsg() concurrently, finishes building kcm->seq_skb
and puts the skb to the write queue
4. Thread A faces an error and finally frees skb that is already in the
write queue
5. kcm_release() does double-free the skb in the write queue
When a thread is building a MSG_MORE skb, another thread must not touch it.
Let's add a per-sk mutex and serialise kcm_sendmsg().
[0]:
BUG: KASAN: slab-use-after-free in __skb_unlink include/linux/skbuff.h:2366 [inline]
BUG: KASAN: slab-use-after-free in __skb_dequeue include/linux/skbuff.h:2385 [inline]
BUG: KASAN: slab-use-after-free in __skb_queue_purge_reason include/linux/skbuff.h:3175 [inline]
BUG: KASAN: slab-use-after-free in __skb_queue_purge include/linux/skbuff.h:3181 [inline]
BUG: KASAN: slab-use-after-free in kcm_release+0x170/0x4c8 net/kcm/kcmsock.c:1691
Read of size 8 at addr ffff0000ced0fc80 by task syz-executor329/6167
CPU: 1 PID: 6167 Comm: syz-executor329 Tainted: G B 6.8.0-rc5-syzkaller-g9abbc24128bc #0
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/25/2024
Call trace:
dump_backtrace+0x1b8/0x1e4 arch/arm64/kernel/stacktrace.c:291
show_stack+0x2c/0x3c arch/arm64/kernel/stacktrace.c:298
__dump_stack lib/dump_stack.c:88 [inline]
dump_stack_lvl+0xd0/0x124 lib/dump_stack.c:106
print_address_description mm/kasan/report.c:377 [inline]
print_report+0x178/0x518 mm/kasan/report.c:488
kasan_report+0xd8/0x138 mm/kasan/report.c:601
__asan_report_load8_noabort+0x20/0x2c mm/kasan/report_generic.c:381
__skb_unlink include/linux/skbuff.h:2366 [inline]
__skb_dequeue include/linux/skbuff.h:2385 [inline]
__skb_queue_purge_reason include/linux/skbuff.h:3175 [inline]
__skb_queue_purge include/linux/skbuff.h:3181 [inline]
kcm_release+0x170/0x4c8 net/kcm/kcmsock.c:1691
__sock_release net/socket.c:659 [inline]
sock_close+0xa4/0x1e8 net/socket.c:1421
__fput+0x30c/0x738 fs/file_table.c:376
____fput+0x20/0x30 fs/file_table.c:404
task_work_run+0x230/0x2e0 kernel/task_work.c:180
exit_task_work include/linux/task_work.h:38 [inline]
do_exit+0x618/0x1f64 kernel/exit.c:871
do_group_exit+0x194/0x22c kernel/exit.c:1020
get_signal+0x1500/0x15ec kernel/signal.c:2893
do_signal+0x23c/0x3b44 arch/arm64/kernel/signal.c:1249
do_notify_resume+0x74/0x1f4 arch/arm64/kernel/entry-common.c:148
exit_to_user_mode_prepare arch/arm64/kernel/entry-common.c:169 [inline]
exit_to_user_mode arch/arm64/kernel/entry-common.c:178 [inline]
el0_svc+0xac/0x168 arch/arm64/kernel/entry-common.c:713
el0t_64_sync_handler+0x84/0xfc arch/arm64/kernel/entry-common.c:730
el0t_64_sync+0x190/0x194 arch/arm64/kernel/entry.S:598
Allocated by task 6166:
kasan_save_stack mm/kasan/common.c:47 [inline]
kasan_save_track+0x40/0x78 mm/kasan/common.c:68
kasan_save_alloc_info+0x70/0x84 mm/kasan/generic.c:626
unpoison_slab_object mm/kasan/common.c:314 [inline]
__kasan_slab_alloc+0x74/0x8c mm/kasan/common.c:340
kasan_slab_alloc include/linux/kasan.h:201 [inline]
slab_post_alloc_hook mm/slub.c:3813 [inline]
slab_alloc_node mm/slub.c:3860 [inline]
kmem_cache_alloc_node+0x204/0x4c0 mm/slub.c:3903
__alloc_skb+0x19c/0x3d8 net/core/skbuff.c:641
alloc_skb include/linux/skbuff.h:1296 [inline]
kcm_sendmsg+0x1d3c/0x2124 net/kcm/kcmsock.c:783
sock_sendmsg_nosec net/socket.c:730 [inline]
__sock_sendmsg net/socket.c:745 [inline]
sock_sendmsg+0x220/0x2c0 net/socket.c:768
splice_to_socket+0x7cc/0xd58 fs/splice.c:889
do_splice_from fs/splice.c:941 [inline]
direct_splice_actor+0xec/0x1d8 fs/splice.c:1164
splice_direct_to_actor+0x438/0xa0c fs/splice.c:1108
do_splice_direct_actor fs/splice.c:1207 [inline]
do_splice_direct+0x1e4/0x304 fs/splice.c:1233
do_sendfile+0x460/0xb3c fs/read_write.c:1295
__do_sys_sendfile64 fs/read_write.c:1362 [inline]
__se_sys_sendfile64 fs/read_write.c:1348 [inline]
__arm64_sys_sendfile64+0x160/0x3b4 fs/read_write.c:1348
__invoke_syscall arch/arm64/kernel/syscall.c:37 [inline]
invoke_syscall+0x98/0x2b8 arch/arm64/kernel/syscall.c:51
el0_svc_common+0x130/0x23c arch/arm64/kernel/syscall.c:136
do_el0_svc+0x48/0x58 arch/arm64/kernel/syscall.c:155
el0_svc+0x54/0x168 arch/arm64/kernel/entry-common.c:712
el0t_64_sync_handler+0x84/0xfc arch/arm64/kernel/entry-common.c:730
el0t_64_sync+0x190/0x194 arch/arm64/kernel/entry.S:598
Freed by task 6167:
kasan_save_stack mm/kasan/common.c:47 [inline]
kasan_save_track+0x40/0x78 mm/kasan/common.c:68
kasan_save_free_info+0x5c/0x74 mm/kasan/generic.c:640
poison_slab_object+0x124/0x18c mm/kasan/common.c:241
__kasan_slab_free+0x3c/0x78 mm/kasan/common.c:257
kasan_slab_free include/linux/kasan.h:184 [inline]
slab_free_hook mm/slub.c:2121 [inline]
slab_free mm/slub.c:4299 [inline]
kmem_cache_free+0x15c/0x3d4 mm/slub.c:4363
kfree_skbmem+0x10c/0x19c
__kfree_skb net/core/skbuff.c:1109 [inline]
kfree_skb_reason+0x240/0x6f4 net/core/skbuff.c:1144
kfree_skb include/linux/skbuff.h:1244 [inline]
kcm_release+0x104/0x4c8 net/kcm/kcmsock.c:1685
__sock_release net/socket.c:659 [inline]
sock_close+0xa4/0x1e8 net/socket.c:1421
__fput+0x30c/0x738 fs/file_table.c:376
____fput+0x20/0x30 fs/file_table.c:404
task_work_run+0x230/0x2e0 kernel/task_work.c:180
exit_task_work include/linux/task_work.h:38 [inline]
do_exit+0x618/0x1f64 kernel/exit.c:871
do_group_exit+0x194/0x22c kernel/exit.c:1020
get_signal+0x1500/0x15ec kernel/signal.c:2893
do_signal+0x23c/0x3b44 arch/arm64/kernel/signal.c:1249
do_notify_resume+0x74/0x1f4 arch/arm64/kernel/entry-common.c:148
exit_to_user_mode_prepare arch/arm64/kernel/entry-common.c:169 [inline]
exit_to_user_mode arch/arm64/kernel/entry-common.c:178 [inline]
el0_svc+0xac/0x168 arch/arm64/kernel/entry-common.c:713
el0t_64_sync_handler+0x84/0xfc arch/arm64/kernel/entry-common.c:730
el0t_64_sync+0x190/0x194 arch/arm64/kernel/entry.S:598
The buggy address belongs to the object at ffff0000ced0fc80
which belongs to the cache skbuff_head_cache of size 240
The buggy address is located 0 bytes inside of
freed 240-byte region [ffff0000ced0fc80, ffff0000ced0fd70)
The buggy address belongs to the physical page:
page:00000000d35f4ae4 refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x10ed0f
flags: 0x5ffc00000000800(slab|node=0|zone=2|lastcpupid=0x7ff)
page_type: 0xffffffff()
raw: 05ffc00000000800 ffff0000c1cbf640 fffffdffc3423100 dead000000000004
raw: 0000000000000000 00000000000c000c 00000001ffffffff 0000000000000000
page dumped because: kasan: bad access detected
Memory state around the buggy address:
ffff0000ced0fb80: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
ffff0000ced0fc00: fb fb fb fb fb fb fc fc fc fc fc fc fc fc fc fc
>ffff0000ced0fc80: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
^
ffff0000ced0fd00: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fc fc
ffff0000ced0fd80: fc fc fc fc fc fc fc fc fa fb fb fb fb fb fb fb
Fixes: ab7ac4eb98
("kcm: Kernel Connection Multiplexor module")
Reported-by: syzbot+b72d86aa5df17ce74c60@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=b72d86aa5df17ce74c60
Tested-by: syzbot+b72d86aa5df17ce74c60@syzkaller.appspotmail.com
Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Link: https://patch.msgid.link/20240815220437.69511-1-kuniyu@amazon.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
200 lines
4.8 KiB
C
200 lines
4.8 KiB
C
/* SPDX-License-Identifier: GPL-2.0-only */
|
|
/*
|
|
* Kernel Connection Multiplexor
|
|
*
|
|
* Copyright (c) 2016 Tom Herbert <tom@herbertland.com>
|
|
*/
|
|
|
|
#ifndef __NET_KCM_H_
|
|
#define __NET_KCM_H_
|
|
|
|
#include <linux/skbuff.h>
|
|
#include <net/sock.h>
|
|
#include <net/strparser.h>
|
|
#include <uapi/linux/kcm.h>
|
|
|
|
extern unsigned int kcm_net_id;
|
|
|
|
#define KCM_STATS_ADD(stat, count) ((stat) += (count))
|
|
#define KCM_STATS_INCR(stat) ((stat)++)
|
|
|
|
struct kcm_psock_stats {
|
|
unsigned long long tx_msgs;
|
|
unsigned long long tx_bytes;
|
|
unsigned long long reserved;
|
|
unsigned long long unreserved;
|
|
unsigned int tx_aborts;
|
|
};
|
|
|
|
struct kcm_mux_stats {
|
|
unsigned long long rx_msgs;
|
|
unsigned long long rx_bytes;
|
|
unsigned long long tx_msgs;
|
|
unsigned long long tx_bytes;
|
|
unsigned int rx_ready_drops;
|
|
unsigned int tx_retries;
|
|
unsigned int psock_attach;
|
|
unsigned int psock_unattach_rsvd;
|
|
unsigned int psock_unattach;
|
|
};
|
|
|
|
struct kcm_stats {
|
|
unsigned long long rx_msgs;
|
|
unsigned long long rx_bytes;
|
|
unsigned long long tx_msgs;
|
|
unsigned long long tx_bytes;
|
|
};
|
|
|
|
struct kcm_tx_msg {
|
|
unsigned int sent;
|
|
unsigned int frag_offset;
|
|
unsigned int msg_flags;
|
|
bool started_tx;
|
|
struct sk_buff *frag_skb;
|
|
struct sk_buff *last_skb;
|
|
};
|
|
|
|
/* Socket structure for KCM client sockets */
|
|
struct kcm_sock {
|
|
struct sock sk;
|
|
struct kcm_mux *mux;
|
|
struct list_head kcm_sock_list;
|
|
int index;
|
|
u32 done : 1;
|
|
struct work_struct done_work;
|
|
|
|
struct kcm_stats stats;
|
|
|
|
/* Transmit */
|
|
struct kcm_psock *tx_psock;
|
|
struct work_struct tx_work;
|
|
struct list_head wait_psock_list;
|
|
struct sk_buff *seq_skb;
|
|
struct mutex tx_mutex;
|
|
u32 tx_stopped : 1;
|
|
|
|
/* Don't use bit fields here, these are set under different locks */
|
|
bool tx_wait;
|
|
bool tx_wait_more;
|
|
|
|
/* Receive */
|
|
struct kcm_psock *rx_psock;
|
|
struct list_head wait_rx_list; /* KCMs waiting for receiving */
|
|
bool rx_wait;
|
|
u32 rx_disabled : 1;
|
|
};
|
|
|
|
struct bpf_prog;
|
|
|
|
/* Structure for an attached lower socket */
|
|
struct kcm_psock {
|
|
struct sock *sk;
|
|
struct strparser strp;
|
|
struct kcm_mux *mux;
|
|
int index;
|
|
|
|
u32 tx_stopped : 1;
|
|
u32 done : 1;
|
|
u32 unattaching : 1;
|
|
|
|
void (*save_state_change)(struct sock *sk);
|
|
void (*save_data_ready)(struct sock *sk);
|
|
void (*save_write_space)(struct sock *sk);
|
|
|
|
struct list_head psock_list;
|
|
|
|
struct kcm_psock_stats stats;
|
|
|
|
/* Receive */
|
|
struct list_head psock_ready_list;
|
|
struct bpf_prog *bpf_prog;
|
|
struct kcm_sock *rx_kcm;
|
|
unsigned long long saved_rx_bytes;
|
|
unsigned long long saved_rx_msgs;
|
|
struct sk_buff *ready_rx_msg;
|
|
|
|
/* Transmit */
|
|
struct kcm_sock *tx_kcm;
|
|
struct list_head psock_avail_list;
|
|
unsigned long long saved_tx_bytes;
|
|
unsigned long long saved_tx_msgs;
|
|
};
|
|
|
|
/* Per net MUX list */
|
|
struct kcm_net {
|
|
struct mutex mutex;
|
|
struct kcm_psock_stats aggregate_psock_stats;
|
|
struct kcm_mux_stats aggregate_mux_stats;
|
|
struct strp_aggr_stats aggregate_strp_stats;
|
|
struct list_head mux_list;
|
|
int count;
|
|
};
|
|
|
|
/* Structure for a MUX */
|
|
struct kcm_mux {
|
|
struct list_head kcm_mux_list;
|
|
struct rcu_head rcu;
|
|
struct kcm_net *knet;
|
|
|
|
struct list_head kcm_socks; /* All KCM sockets on MUX */
|
|
int kcm_socks_cnt; /* Total KCM socket count for MUX */
|
|
struct list_head psocks; /* List of all psocks on MUX */
|
|
int psocks_cnt; /* Total attached sockets */
|
|
|
|
struct kcm_mux_stats stats;
|
|
struct kcm_psock_stats aggregate_psock_stats;
|
|
struct strp_aggr_stats aggregate_strp_stats;
|
|
|
|
/* Receive */
|
|
spinlock_t rx_lock ____cacheline_aligned_in_smp;
|
|
struct list_head kcm_rx_waiters; /* KCMs waiting for receiving */
|
|
struct list_head psocks_ready; /* List of psocks with a msg ready */
|
|
struct sk_buff_head rx_hold_queue;
|
|
|
|
/* Transmit */
|
|
spinlock_t lock ____cacheline_aligned_in_smp; /* TX and mux locking */
|
|
struct list_head psocks_avail; /* List of available psocks */
|
|
struct list_head kcm_tx_waiters; /* KCMs waiting for a TX psock */
|
|
};
|
|
|
|
#ifdef CONFIG_PROC_FS
|
|
int kcm_proc_init(void);
|
|
void kcm_proc_exit(void);
|
|
#else
|
|
static inline int kcm_proc_init(void) { return 0; }
|
|
static inline void kcm_proc_exit(void) { }
|
|
#endif
|
|
|
|
static inline void aggregate_psock_stats(struct kcm_psock_stats *stats,
|
|
struct kcm_psock_stats *agg_stats)
|
|
{
|
|
/* Save psock statistics in the mux when psock is being unattached. */
|
|
|
|
#define SAVE_PSOCK_STATS(_stat) (agg_stats->_stat += stats->_stat)
|
|
SAVE_PSOCK_STATS(tx_msgs);
|
|
SAVE_PSOCK_STATS(tx_bytes);
|
|
SAVE_PSOCK_STATS(reserved);
|
|
SAVE_PSOCK_STATS(unreserved);
|
|
SAVE_PSOCK_STATS(tx_aborts);
|
|
#undef SAVE_PSOCK_STATS
|
|
}
|
|
|
|
static inline void aggregate_mux_stats(struct kcm_mux_stats *stats,
|
|
struct kcm_mux_stats *agg_stats)
|
|
{
|
|
/* Save psock statistics in the mux when psock is being unattached. */
|
|
|
|
#define SAVE_MUX_STATS(_stat) (agg_stats->_stat += stats->_stat)
|
|
SAVE_MUX_STATS(rx_msgs);
|
|
SAVE_MUX_STATS(rx_bytes);
|
|
SAVE_MUX_STATS(tx_msgs);
|
|
SAVE_MUX_STATS(tx_bytes);
|
|
SAVE_MUX_STATS(rx_ready_drops);
|
|
SAVE_MUX_STATS(psock_attach);
|
|
SAVE_MUX_STATS(psock_unattach_rsvd);
|
|
SAVE_MUX_STATS(psock_unattach);
|
|
#undef SAVE_MUX_STATS
|
|
}
|
|
|
|
#endif /* __NET_KCM_H_ */
|