rxrpc: Use skb_unshare() rather than skb_cow_data()
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>
This commit is contained in:
@@ -32,6 +32,8 @@ enum rxrpc_skb_trace {
|
||||
rxrpc_skb_received,
|
||||
rxrpc_skb_rotated,
|
||||
rxrpc_skb_seen,
|
||||
rxrpc_skb_unshared,
|
||||
rxrpc_skb_unshared_nomem,
|
||||
};
|
||||
|
||||
enum rxrpc_local_trace {
|
||||
@@ -231,7 +233,9 @@ enum rxrpc_tx_point {
|
||||
EM(rxrpc_skb_purged, "PUR") \
|
||||
EM(rxrpc_skb_received, "RCV") \
|
||||
EM(rxrpc_skb_rotated, "ROT") \
|
||||
E_(rxrpc_skb_seen, "SEE")
|
||||
EM(rxrpc_skb_seen, "SEE") \
|
||||
EM(rxrpc_skb_unshared, "UNS") \
|
||||
E_(rxrpc_skb_unshared_nomem, "US0")
|
||||
|
||||
#define rxrpc_local_traces \
|
||||
EM(rxrpc_local_got, "GOT") \
|
||||
@@ -633,9 +637,9 @@ TRACE_EVENT(rxrpc_call,
|
||||
|
||||
TRACE_EVENT(rxrpc_skb,
|
||||
TP_PROTO(struct sk_buff *skb, enum rxrpc_skb_trace op,
|
||||
int usage, int mod_count, const void *where),
|
||||
int usage, int mod_count, u8 flags, const void *where),
|
||||
|
||||
TP_ARGS(skb, op, usage, mod_count, where),
|
||||
TP_ARGS(skb, op, usage, mod_count, flags, where),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(struct sk_buff *, skb )
|
||||
@@ -648,7 +652,7 @@ TRACE_EVENT(rxrpc_skb,
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->skb = skb;
|
||||
__entry->flags = rxrpc_skb(skb)->rx_flags;
|
||||
__entry->flags = flags;
|
||||
__entry->op = op;
|
||||
__entry->usage = usage;
|
||||
__entry->mod_count = mod_count;
|
||||
|
Reference in New Issue
Block a user