Skip to content

Commit 37a27b4

Browse files
authored
Wait for txg sync if the last DRR_FREEOBJECTS might result in a hole
If we receive a DRR_FREEOBJECTS as the first entry in an object range, this might end up producing a hole if the freed objects were the only existing objects in the block. If the txg starts syncing before we've processed any following DRR_OBJECT records, this leads to a possible race where the backing arc_buf_t gets its psize set to 0 in the arc_write_ready() callback while still being referenced from a dirty record in the open txg. To prevent this, we insert a txg_wait_synced call if the first record in the range was a DRR_FREEOBJECTS that actually resulted in one or more freed objects. Reviewed-by: Brian Behlendorf <[email protected]> Signed-off-by: David Hedberg <[email protected]> Sponsored by: Findity AB Closes #11893 Closes #14358
1 parent 73968de commit 37a27b4

File tree

5 files changed

+116
-1
lines changed

5 files changed

+116
-1
lines changed

module/zfs/dmu_recv.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,12 @@ static int zfs_recv_best_effort_corrective = 0;
7676
static const void *const dmu_recv_tag = "dmu_recv_tag";
7777
const char *const recv_clone_name = "%recv";
7878

79+
typedef enum {
80+
ORNS_NO,
81+
ORNS_YES,
82+
ORNS_MAYBE
83+
} or_need_sync_t;
84+
7985
static int receive_read_payload_and_next_header(dmu_recv_cookie_t *ra, int len,
8086
void *buf);
8187

@@ -129,6 +135,9 @@ struct receive_writer_arg {
129135
uint8_t or_mac[ZIO_DATA_MAC_LEN];
130136
boolean_t or_byteorder;
131137
zio_t *heal_pio;
138+
139+
/* Keep track of DRR_FREEOBJECTS right after DRR_OBJECT_RANGE */
140+
or_need_sync_t or_need_sync;
132141
};
133142

134143
typedef struct dmu_recv_begin_arg {
@@ -1914,10 +1923,22 @@ receive_object(struct receive_writer_arg *rwa, struct drr_object *drro,
19141923
/* object was freed and we are about to allocate a new one */
19151924
object_to_hold = DMU_NEW_OBJECT;
19161925
} else {
1926+
/*
1927+
* If the only record in this range so far was DRR_FREEOBJECTS
1928+
* with at least one actually freed object, it's possible that
1929+
* the block will now be converted to a hole. We need to wait
1930+
* for the txg to sync to prevent races.
1931+
*/
1932+
if (rwa->or_need_sync == ORNS_YES)
1933+
txg_wait_synced(dmu_objset_pool(rwa->os), 0);
1934+
19171935
/* object is free and we are about to allocate a new one */
19181936
object_to_hold = DMU_NEW_OBJECT;
19191937
}
19201938

1939+
/* Only relevant for the first object in the range */
1940+
rwa->or_need_sync = ORNS_NO;
1941+
19211942
/*
19221943
* If this is a multi-slot dnode there is a chance that this
19231944
* object will expand into a slot that is already used by
@@ -2111,6 +2132,9 @@ receive_freeobjects(struct receive_writer_arg *rwa,
21112132

21122133
if (err != 0)
21132134
return (err);
2135+
2136+
if (rwa->or_need_sync == ORNS_MAYBE)
2137+
rwa->or_need_sync = ORNS_YES;
21142138
}
21152139
if (next_err != ESRCH)
21162140
return (next_err);
@@ -2604,6 +2628,8 @@ receive_object_range(struct receive_writer_arg *rwa,
26042628
memcpy(rwa->or_mac, drror->drr_mac, ZIO_DATA_MAC_LEN);
26052629
rwa->or_byteorder = byteorder;
26062630

2631+
rwa->or_need_sync = ORNS_MAYBE;
2632+
26072633
return (0);
26082634
}
26092635

tests/runfiles/common.run

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -842,7 +842,7 @@ tests = ['recv_dedup', 'recv_dedup_encrypted_zvol', 'rsend_001_pos',
842842
'send-c_recv_lz4_disabled', 'send-c_mixed_compression',
843843
'send-c_stream_size_estimate', 'send-c_embedded_blocks', 'send-c_resume',
844844
'send-cpL_varied_recsize', 'send-c_recv_dedup', 'send-L_toggle',
845-
'send_encrypted_incremental.ksh',
845+
'send_encrypted_incremental.ksh', 'send_encrypted_freeobjects',
846846
'send_encrypted_hierarchy', 'send_encrypted_props',
847847
'send_encrypted_truncated_files', 'send_freeobjects', 'send_realloc_files',
848848
'send_realloc_encrypted_files', 'send_spill_block', 'send_holds',

tests/runfiles/sanity.run

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,7 @@ tests = ['recv_dedup', 'recv_dedup_encrypted_zvol', 'rsend_001_pos',
550550
'rsend_014_pos', 'rsend_016_neg', 'send-c_verify_contents',
551551
'send-c_volume', 'send-c_zstreamdump', 'send-c_recv_dedup',
552552
'send-L_toggle', 'send_encrypted_hierarchy', 'send_encrypted_props',
553+
'send_encrypted_freeobjects',
553554
'send_encrypted_truncated_files', 'send_freeobjects', 'send_holds',
554555
'send_mixed_raw', 'send-wR_encrypted_zvol', 'send_partial_dataset',
555556
'send_invalid']

tests/zfs-tests/tests/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1810,6 +1810,7 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \
18101810
functional/rsend/send_doall.ksh \
18111811
functional/rsend/send_encrypted_incremental.ksh \
18121812
functional/rsend/send_encrypted_files.ksh \
1813+
functional/rsend/send_encrypted_freeobjects.ksh \
18131814
functional/rsend/send_encrypted_hierarchy.ksh \
18141815
functional/rsend/send_encrypted_props.ksh \
18151816
functional/rsend/send_encrypted_truncated_files.ksh \
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
#!/bin/ksh
2+
3+
#
4+
# This file and its contents are supplied under the terms of the
5+
# Common Development and Distribution License ("CDDL"), version 1.0.
6+
# You may only use this file in accordance with the terms of version
7+
# 1.0 of the CDDL.
8+
#
9+
# A full copy of the text of the CDDL should have accompanied this
10+
# source. A copy of the CDDL is also available via the Internet at
11+
# http://www.illumos.org/license/CDDL.
12+
#
13+
14+
#
15+
# Copyright (c) 2017 by Lawrence Livermore National Security, LLC.
16+
# Copyright (c) 2023 by Findity AB
17+
#
18+
19+
. $STF_SUITE/tests/functional/rsend/rsend.kshlib
20+
21+
#
22+
# Description:
23+
# Verify that receiving a raw encrypted stream, with a FREEOBJECTS
24+
# removing all existing objects in a block followed by an OBJECT write
25+
# to the same block, does not result in a panic.
26+
#
27+
# Strategy:
28+
# 1. Create a new encrypted filesystem
29+
# 2. Create file f1 as the first object in some block (here object 128)
30+
# 3. Take snapshot A
31+
# 4. Create file f2 as the second object in the same block (here object 129)
32+
# 5. Delete f1
33+
# 6. Take snapshot B
34+
# 7. Receive a full raw encrypted send of A
35+
# 8. Receive an incremental raw send of B
36+
#
37+
verify_runnable "both"
38+
39+
function create_object_with_num
40+
{
41+
file=$1
42+
num=$2
43+
44+
tries=100
45+
for ((i=0; i<$tries; i++)); do
46+
touch $file
47+
onum=$(ls -li $file | awk '{print $1}')
48+
49+
if [[ $onum -ne $num ]] ; then
50+
rm -f $file
51+
else
52+
break
53+
fi
54+
done
55+
if [[ $i -eq $tries ]]; then
56+
log_fail "Failed to create object with number $num"
57+
fi
58+
}
59+
60+
log_assert "FREEOBJECTS followed by OBJECT in encrypted stream does not crash"
61+
62+
sendds=sendencfods
63+
recvds=recvencfods
64+
keyfile=/$POOL/keyencfods
65+
f1=/$POOL/$sendds/f1
66+
f2=/$POOL/$sendds/f2
67+
68+
log_must eval "echo 'password' > $keyfile"
69+
70+
#
71+
# xattr=sa and dnodesize=legacy for sequential object numbers, see
72+
# note in send_freeobjects.ksh.
73+
#
74+
log_must zfs create -o xattr=sa -o dnodesize=legacy -o encryption=on \
75+
-o keyformat=passphrase -o keylocation=file://$keyfile $POOL/$sendds
76+
77+
create_object_with_num $f1 128
78+
log_must zfs snap $POOL/$sendds@A
79+
create_object_with_num $f2 129
80+
log_must rm $f1
81+
log_must zfs snap $POOL/$sendds@B
82+
83+
log_must eval "zfs send -w $POOL/$sendds@A | zfs recv $POOL/$recvds"
84+
log_must eval "zfs send -w -i $POOL/$sendds@A $POOL/$sendds@B |" \
85+
"zfs recv $POOL/$recvds"
86+
87+
log_pass "FREEOBJECTS followed by OBJECT in encrypted stream did not crash"

0 commit comments

Comments
 (0)