flist/receiver: zero/empty the device & symlink extra slots

recv_file_entry (normal and XMIT_HLINKED abbrev branches) and make_file
left F_RDEV_P / the symlink-name slot uninitialized when the matching
preserve option was off, so a later read walked into adjacent pool
memory. Empty the symlink name when !preserve_links, zero F_RDEV_P when
!preserve_devices, and mirror both in the abbreviated branch. receiver.c
saves/restores the --write-devices S_IFBLK mode flip around receive_data
so dest_mode() never sees the mutated mode.

Co-authored-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Andrew TridgellandGreg Kroah-Hartman committed 2026-06-22 09:21:59 +10:00
1 parent c09cb8f43e
commit 762d0be048
2 files changed
+67 -5

No files matched your search

+45 -1
View File
@@ -889,9 +889,17 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
rdev_major = DEV_MAJOR(devp);
rdev = MAKEDEV(rdev_major, DEV_MINOR(devp));
extra_len += DEV_EXTRA_CNT * EXTRA_LEN;
} else if (IS_DEVICE(mode)) {
/* Abbrev-branch counterpart to the !preserve_devices
* stub-alloc below: zeroed F_RDEV_P slots. */
extra_len += DEV_EXTRA_CNT * EXTRA_LEN;
}
if (preserve_links && S_ISLNK(mode))
linkname_len = strlen(F_SYMLINK(first)) + 1;
else if (S_ISLNK(mode))
/* Abbrev-branch counterpart to the !preserve_links
* stub-alloc below: empty linkname. */
linkname_len = 1;
else
linkname_len = 0;
real_ISREG_entry = S_ISREG(mode) ? 1 : 0;
@@ -1013,6 +1021,15 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
if (IS_DEVICE(mode))
extra_len += DEV_EXTRA_CNT * EXTRA_LEN;
file_length = 0;
} else if (IS_DEVICE(mode)) {
/* Peer/batch sent an S_IFCHR/S_IFBLK entry but we are not
* preserving devices. A cooperating sender wouldn't do this;
* a crafted batch can. Allocate (and zero, via the memset
* below) the DEV_EXTRA_CNT slots so F_RDEV_P() callers
* (set_stat_xattr under --fake-super, generator IS_DEVICE
* paths) read {0,0} instead of the previous pool slot. */
extra_len += DEV_EXTRA_CNT * EXTRA_LEN;
file_length = 0;
} else if (protocol_version < 28)
rdev = MAKEDEV(0, 0);
@@ -1033,6 +1050,14 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
#endif
if (munge_symlinks)
linkname_len += SYMLINK_PREFIX_LEN;
} else if (S_ISLNK(mode)) {
/* Peer/batch sent an S_IFLNK entry but we are not preserving
* links (no -l, and the batch stream-flags didn't set it). A
* cooperating sender wouldn't do this; a crafted batch can.
* Allocate one byte for an empty linkname so F_SYMLINK()
* callers (log.c %L, generator.c) read a valid "" instead of
* the next pool slot's redzone. */
linkname_len = 1;
}
else
#endif
@@ -1228,7 +1253,11 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
#ifdef SUPPORT_LINKS
if (linkname_len) {
bp += basename_len;
if (first_hlink_ndx >= flist->ndx_start) {
if (!preserve_links) {
/* The empty-linkname case allocated above; nothing on
* the wire to read. Just terminate it. */
*bp = '\0';
} else if (first_hlink_ndx >= flist->ndx_start) {
struct file_struct *first = flist->files[first_hlink_ndx - flist->ndx_start];
memcpy(bp, F_SYMLINK(first), linkname_len);
} else {
@@ -1532,6 +1561,18 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
extra_len += SUM_EXTRA_CNT * EXTRA_LEN;
}
#ifdef HAVE_STRUCT_STAT_ST_RDEV
/* The sender path historically passes rdev via the tmp_rdev static
* (read by send_file_entry()), so make_file() never reserved
* DEV_EXTRA_CNT in the file_struct itself. But receiver-side callers
* (recv_generator's --inplace --backup back_file, backup.c make_backup)
* hand this struct to set_file_attrs() -> set_stat_xattr(), which reads
* F_RDEV_P(file) under --fake-super. Reserve and populate the slots
* so the struct is self-contained, matching recv_file_entry(). */
if (IS_DEVICE(st.st_mode))
extra_len += DEV_EXTRA_CNT * EXTRA_LEN;
#endif
#if EXTRA_ROUNDING > 0
if (extra_len & (EXTRA_ROUNDING * EXTRA_LEN))
extra_len = (extra_len | (EXTRA_ROUNDING * EXTRA_LEN)) + EXTRA_LEN;
@@ -1565,7 +1606,10 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
#ifdef HAVE_STRUCT_STAT_ST_RDEV
if (IS_DEVICE(st.st_mode)) {
uint32 *devp = F_RDEV_P(file);
tmp_rdev = st.st_rdev;
DEV_MAJOR(devp) = major(st.st_rdev);
DEV_MINOR(devp) = minor(st.st_rdev);
st.st_size = 0;
} else if (IS_SPECIAL(st.st_mode))
st.st_size = 0;
+22 -4
View File
@@ -704,7 +704,8 @@ int recv_files(int f_in, int f_out, char *local_name)
#ifdef SUPPORT_ACLS
const char *parent_dirname = "";
#endif
int ndx, recv_ok, one_inplace;
int ndx, recv_ok, one_inplace, write_to_device;
mode_t write_devices_saved_mode = 0;
if (DEBUG_GTE(RECV, 1))
rprintf(FINFO, "recv_files(%d) starting\n", cur_flist->used);
@@ -1025,11 +1026,10 @@ int recv_files(int f_in, int f_out, char *local_name)
continue;
}
if (write_devices && IS_DEVICE(st.st_mode)) {
write_to_device = write_devices && IS_DEVICE(st.st_mode);
if (write_to_device) {
if (fd1 != -1 && st.st_size == 0)
st.st_size = get_device_size(fd1, fname);
/* Mark the file entry as a device so that we don't try to truncate it later on. */
file->mode = S_IFBLK | (file->mode & ACCESSPERMS);
} else if (fd1 != -1 && !(S_ISREG(st.st_mode))) {
close(fd1);
fd1 = -1;
@@ -1103,9 +1103,27 @@ int recv_files(int f_in, int f_out, char *local_name)
else if (!am_server && INFO_GTE(NAME, 1) && INFO_EQ(PROGRESS, 1))
rprintf(FINFO, "%s\n", fname);
/* --write-devices writes a regular source file's content into an
* existing destination device. Flip file->mode to S_IFBLK just for
* receive_data()'s ftruncate gate (!IS_DEVICE), then restore it right
* after -- the file_struct was built as a regular file with no
* DEV_EXTRA_CNT, so leaving it S_IFBLK would make set_stat_xattr's
* F_RDEV_P() read past the allocation. Kept tight here (after the
* pre-transfer log and the fd2 bail-out) so no continue skips the
* restore and dest_mode() above ran on the real mode. */
if (write_to_device) {
write_devices_saved_mode = file->mode;
file->mode = S_IFBLK | (file->mode & ACCESSPERMS);
}
/* recv file data */
recv_ok = receive_data(f_in, fnamecmp, fd1, st.st_size, fname, fd2, file, inplace || one_inplace);
if (write_to_device) {
file->mode = write_devices_saved_mode;
write_devices_saved_mode = 0;
}
log_item(log_code, file, iflags, NULL);
if (want_progress_now)
instant_progress(fname);