forked from Minki/linux
d0d5c0cd1e
The in-place decryption routines in AF_RXRPC's rxkad security module
currently call skb_cow_data() to make sure the data isn't shared and that
the skb can be written over. This has a problem, however, as the softirq
handler may be still holding a ref or the Rx ring may be holding multiple
refs when skb_cow_data() is called in rxkad_verify_packet() - and so
skb_shared() returns true and __pskb_pull_tail() dislikes that. If this
occurs, something like the following report will be generated.
kernel BUG at net/core/skbuff.c:1463!
...
RIP: 0010:pskb_expand_head+0x253/0x2b0
...
Call Trace:
__pskb_pull_tail+0x49/0x460
skb_cow_data+0x6f/0x300
rxkad_verify_packet+0x18b/0xb10 [rxrpc]
rxrpc_recvmsg_data.isra.11+0x4a8/0xa10 [rxrpc]
rxrpc_kernel_recv_data+0x126/0x240 [rxrpc]
afs_extract_data+0x51/0x2d0 [kafs]
afs_deliver_fs_fetch_data+0x188/0x400 [kafs]
afs_deliver_to_call+0xac/0x430 [kafs]
afs_wait_for_call_to_complete+0x22f/0x3d0 [kafs]
afs_make_call+0x282/0x3f0 [kafs]
afs_fs_fetch_data+0x164/0x300 [kafs]
afs_fetch_data+0x54/0x130 [kafs]
afs_readpages+0x20d/0x340 [kafs]
read_pages+0x66/0x180
__do_page_cache_readahead+0x188/0x1a0
ondemand_readahead+0x17d/0x2e0
generic_file_read_iter+0x740/0xc10
__vfs_read+0x145/0x1a0
vfs_read+0x8c/0x140
ksys_read+0x4a/0xb0
do_syscall_64+0x43/0xf0
entry_SYSCALL_64_after_hwframe+0x44/0xa9
Fix this by using skb_unshare() instead in the input path for DATA packets
that have a security index != 0. Non-DATA packets don't need in-place
encryption and neither do unencrypted DATA packets.
Fixes: 248f219cb8
("rxrpc: Rewrite the data and ack handling code")
Reported-by: Julian Wollrath <jwollrath@web.de>
Signed-off-by: David Howells <dhowells@redhat.com>
97 lines
2.6 KiB
C
97 lines
2.6 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/* ar-skbuff.c: socket buffer destruction handling
|
|
*
|
|
* Copyright (C) 2007 Red Hat, Inc. All Rights Reserved.
|
|
* Written by David Howells (dhowells@redhat.com)
|
|
*/
|
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/net.h>
|
|
#include <linux/skbuff.h>
|
|
#include <net/sock.h>
|
|
#include <net/af_rxrpc.h>
|
|
#include "ar-internal.h"
|
|
|
|
#define is_tx_skb(skb) (rxrpc_skb(skb)->rx_flags & RXRPC_SKB_TX_BUFFER)
|
|
#define select_skb_count(skb) (is_tx_skb(skb) ? &rxrpc_n_tx_skbs : &rxrpc_n_rx_skbs)
|
|
|
|
/*
|
|
* Note the allocation or reception of a socket buffer.
|
|
*/
|
|
void rxrpc_new_skb(struct sk_buff *skb, enum rxrpc_skb_trace op)
|
|
{
|
|
const void *here = __builtin_return_address(0);
|
|
int n = atomic_inc_return(select_skb_count(skb));
|
|
trace_rxrpc_skb(skb, op, refcount_read(&skb->users), n,
|
|
rxrpc_skb(skb)->rx_flags, here);
|
|
}
|
|
|
|
/*
|
|
* Note the re-emergence of a socket buffer from a queue or buffer.
|
|
*/
|
|
void rxrpc_see_skb(struct sk_buff *skb, enum rxrpc_skb_trace op)
|
|
{
|
|
const void *here = __builtin_return_address(0);
|
|
if (skb) {
|
|
int n = atomic_read(select_skb_count(skb));
|
|
trace_rxrpc_skb(skb, op, refcount_read(&skb->users), n,
|
|
rxrpc_skb(skb)->rx_flags, here);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Note the addition of a ref on a socket buffer.
|
|
*/
|
|
void rxrpc_get_skb(struct sk_buff *skb, enum rxrpc_skb_trace op)
|
|
{
|
|
const void *here = __builtin_return_address(0);
|
|
int n = atomic_inc_return(select_skb_count(skb));
|
|
trace_rxrpc_skb(skb, op, refcount_read(&skb->users), n,
|
|
rxrpc_skb(skb)->rx_flags, here);
|
|
skb_get(skb);
|
|
}
|
|
|
|
/*
|
|
* Note the dropping of a ref on a socket buffer by the core.
|
|
*/
|
|
void rxrpc_eaten_skb(struct sk_buff *skb, enum rxrpc_skb_trace op)
|
|
{
|
|
const void *here = __builtin_return_address(0);
|
|
int n = atomic_inc_return(&rxrpc_n_rx_skbs);
|
|
trace_rxrpc_skb(skb, op, 0, n, 0, here);
|
|
}
|
|
|
|
/*
|
|
* Note the destruction of a socket buffer.
|
|
*/
|
|
void rxrpc_free_skb(struct sk_buff *skb, enum rxrpc_skb_trace op)
|
|
{
|
|
const void *here = __builtin_return_address(0);
|
|
if (skb) {
|
|
int n;
|
|
CHECK_SLAB_OKAY(&skb->users);
|
|
n = atomic_dec_return(select_skb_count(skb));
|
|
trace_rxrpc_skb(skb, op, refcount_read(&skb->users), n,
|
|
rxrpc_skb(skb)->rx_flags, here);
|
|
kfree_skb(skb);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Clear a queue of socket buffers.
|
|
*/
|
|
void rxrpc_purge_queue(struct sk_buff_head *list)
|
|
{
|
|
const void *here = __builtin_return_address(0);
|
|
struct sk_buff *skb;
|
|
while ((skb = skb_dequeue((list))) != NULL) {
|
|
int n = atomic_dec_return(select_skb_count(skb));
|
|
trace_rxrpc_skb(skb, rxrpc_skb_purged,
|
|
refcount_read(&skb->users), n,
|
|
rxrpc_skb(skb)->rx_flags, here);
|
|
kfree_skb(skb);
|
|
}
|
|
}
|