Skip to content

Commit 09a5fe6

Browse files
committed
btrfs-progs: receive: fix reading header on strict alignment hosts
There's a report: ERROR: Failed to send/receive subvolume: .../testbackup.20240330T1102 -> .../testbackup.20240330T1102 ERROR: ... Command execution failed (exitcode=1) ERROR: ... sh: btrfs send '.../testbackup.20240330T1102' | ssh user@host.lan 'sudo -n btrfs receive '\''...'\''' ERROR: ... invalid tlv in cmd tlv_type = 816 This is send/receive between arm64 and armv5el hosts, with btrfs-progs 6.2.1. Last known working version is 5.16. This looked like another custom protocol extension by NAS vendors but this was a false trace and this is indeed a bug in stream parsing after changes to the v2 protocol. The most likely explanation is that the armv5 host requires strict alignment for reads (32bit type must be 4 byte aligned) but the way the raw data buffer is mapped to the cmd structure in read_cmd() does not guarantee that. Issue: #770 Fixes: aa1ca37 ("btrfs-progs: receive: support v2 send stream DATA tlv format") Signed-off-by: David Sterba <dsterba@suse.com>
1 parent c1d8bd8 commit 09a5fe6

File tree

1 file changed

+9
-8
lines changed

1 file changed

+9
-8
lines changed

common/send-stream.c

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -131,9 +131,10 @@ static int read_cmd(struct btrfs_send_stream *sctx)
131131
goto out;
132132
}
133133

134+
/* The read_buf does not guarantee any aligmnet for any structures. */
134135
cmd_hdr = (struct btrfs_cmd_header *)sctx->read_buf;
135-
cmd_len = le32_to_cpu(cmd_hdr->len);
136-
cmd = le16_to_cpu(cmd_hdr->cmd);
136+
cmd_len = get_unaligned_le32(&cmd_hdr->len);
137+
cmd = get_unaligned_le16(&cmd_hdr->cmd);
137138
buf_len = sizeof(*cmd_hdr) + cmd_len;
138139
if (sctx->read_buf_size < buf_len) {
139140
void *new_read_buf;
@@ -160,9 +161,9 @@ static int read_cmd(struct btrfs_send_stream *sctx)
160161
goto out;
161162
}
162163

163-
crc = le32_to_cpu(cmd_hdr->crc);
164+
crc = get_unaligned_le32(&cmd_hdr->crc);
164165
/* In send, CRC is computed with header crc = 0, replicate that */
165-
cmd_hdr->crc = 0;
166+
put_unaligned_le32(0, &cmd_hdr->crc);
166167

167168
crc2 = crc32c(0, (unsigned char*)sctx->read_buf,
168169
sizeof(*cmd_hdr) + cmd_len);
@@ -183,7 +184,7 @@ static int read_cmd(struct btrfs_send_stream *sctx)
183184
ret = -EINVAL;
184185
goto out;
185186
}
186-
tlv_type = le16_to_cpu(*(__le16 *)data);
187+
tlv_type = get_unaligned_le16(data);
187188

188189
if (tlv_type == 0 || tlv_type > __BTRFS_SEND_A_MAX) {
189190
error("invalid tlv in cmd tlv_type = %hu", tlv_type);
@@ -204,7 +205,7 @@ static int read_cmd(struct btrfs_send_stream *sctx)
204205
ret = -EINVAL;
205206
goto out;
206207
}
207-
send_attr->tlv_len = le16_to_cpu(*(__le16 *)data);
208+
send_attr->tlv_len = get_unaligned_le16(data);
208209
pos += sizeof(__le16);
209210
data += sizeof(__le16);
210211
}
@@ -322,8 +323,8 @@ static int tlv_get_timespec(struct btrfs_send_stream *sctx,
322323
TLV_GET(sctx, attr, (void**)&bts, &len);
323324
TLV_CHECK_LEN(sizeof(*bts), len);
324325

325-
ts->tv_sec = le64_to_cpu(bts->sec);
326-
ts->tv_nsec = le32_to_cpu(bts->nsec);
326+
ts->tv_sec = get_unaligned_le64(&bts->sec);
327+
ts->tv_nsec = get_unaligned_le32(&bts->nsec);
327328
ret = 0;
328329

329330
tlv_get_failed:

0 commit comments

Comments
 (0)