Package: rsync
Version: 2.6.9-2etch2
Severity: normal
Tags: patch

Hi.

When rsync runs with both --hard-links and --acls, the receiving process can segfault while processing the ACL on any files with a link count > 1. Neither of the (no)acl mount options nor the actual presence of any ACEs seem to make much difference.

It looks like the behavior is due to dereference of an uninitialized portion of a struct statx declared in hard_link_cluster(). Elsewhere in the code, there exist ifdefs to explicitly initialize the extra pointers that extend struct stat when SUPPORT_ACLS is enabled. The bug is similar to one that affected rsync-3.0.3-2 in lenny:
        http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=498083


No idea if this will get patched in etch since lenny is tracking rsync3, but some debug info & a simple patch are attached anyway.


Thanks.
-R


# cat /etc/debian_version ; uname -a
4.0
Linux debian 2.6.18-6-686 #1 SMP Fri Dec 12 16:48:28 UTC 2008 i686 GNU/ Linux

# dpkg -s rsync | egrep '^(Version|Depends):'
Version: 2.6.9-2etch2
Depends: libacl1 (>= 2.2.11-1), libc6 (>= 2.3.6-6), libpopt0 (>= 1.10), lsb-base (>= 3.0)

# dpkg -s libacl1 libc6 libpopt0 lsb-base |egrep '^(Package|Name| Version):'
Package: libacl1
Version: 2.2.41-1
Package: libc6
Version: 2.3.6.ds1-13etch8
Package: libpopt0
Version: 1.10-3
Package: lsb-base
Version: 3.1-23.2etch1

# for ((i=1;i<=2;i++)) ; do dd if=/dev/zero of=/var/tmp/vol${i} bs=1k count=10240 ; losetup /dev/loop${i} /var/tmp/vol${i} ; mke2fs -j /dev/ loop${i} ; mkdir /mnt/vol${i} ; mount -o defaults,acl /dev/loop${i} / mnt/vol${i} ; done
10240+0 records in
10240+0 records out
10485760 bytes (10 MB) copied, 0.0675322 seconds, 155 MB/s
mke2fs 1.40-WIP (14-Nov-2006)
Filesystem label=
OS type: Linux
Block size=1024 (log=0)
Fragment size=1024 (log=0)
2560 inodes, 10240 blocks
512 blocks (5.00%) reserved for the super user
First data block=1
Maximum filesystem blocks=10485760
2 block groups
8192 blocks per group, 8192 fragments per group
1280 inodes per group
Superblock backups stored on blocks:
        8193

Writing inode tables: done
Creating journal (1400 blocks): done
Writing superblocks and filesystem accounting information: done

This filesystem will be automatically checked every 24 mounts or
180 days, whichever comes first.  Use tune2fs -c or -i to override.
10240+0 records in
10240+0 records out
10485760 bytes (10 MB) copied, 0.0573582 seconds, 183 MB/s
mke2fs 1.40-WIP (14-Nov-2006)
Filesystem label=
OS type: Linux
Block size=1024 (log=0)
Fragment size=1024 (log=0)
2560 inodes, 10240 blocks
512 blocks (5.00%) reserved for the super user
First data block=1
Maximum filesystem blocks=10485760
2 block groups
8192 blocks per group, 8192 fragments per group
1280 inodes per group
Superblock backups stored on blocks:
        8193

Writing inode tables: done
Creating journal (1400 blocks): done
Writing superblocks and filesystem accounting information: done

This filesystem will be automatically checked every 27 mounts or
180 days, whichever comes first.  Use tune2fs -c or -i to override.

# mount
/dev/hda1 on / type ext3 (rw,errors=remount-ro)
tmpfs on /lib/init/rw type tmpfs (rw,nosuid,mode=0755)
proc on /proc type proc (rw,noexec,nosuid,nodev)
sysfs on /sys type sysfs (rw,noexec,nosuid,nodev)
udev on /dev type tmpfs (rw,mode=0755)
tmpfs on /dev/shm type tmpfs (rw,nosuid,nodev)
devpts on /dev/pts type devpts (rw,noexec,nosuid,gid=5,mode=620)
/dev/hdd on /vicepa type ext3 (rw,errors=remount-ro)
AFS on /afs type afs (rw)
/dev/hdb on /mnt/gnomehome type reiserfs (rw)
/dev/loop1 on /mnt/vol1 type ext3 (rw,acl)
/dev/loop2 on /mnt/vol2 type ext3 (rw,acl)

# ulimit -c unlimited

# mkdir /mnt/vol1/acldir
# touch /mnt/vol1/acldir/f{1,2}
# ln /mnt/vol1/acldir/{f,l}1
# ln /mnt/vol1/acldir/{f,l}2
# touch /mnt/vol1/acldir/thecheesestandsalone
# setfacl -m user:nobody:rwx /mnt/vol1/acldir/f2

# ls -li /mnt/vol1/acldir/
total 2
1282 -rw-r--r--  2 root root 0 2009-01-28 15:27 f1
1283 -rw-rwxr--+ 2 root root 0 2009-01-28 15:27 f2
1282 -rw-r--r--  2 root root 0 2009-01-28 15:27 l1
1283 -rw-rwxr--+ 2 root root 0 2009-01-28 15:27 l2
1284 -rw-r--r--  1 root root 0 2009-01-28 15:27 thecheesestandsalone

# rsync --verbose --archive --hard-links --acls /mnt/vol1/ /mnt/vol2/
building file list ... done
./
acldir/
acldir/l1
acldir/l2
acldir/thecheesestandsalone
lost+found/
rsync: connection unexpectedly closed (96 bytes received so far) [sender] rsync error: error in rsync protocol data stream (code 12) at io.c(453) [sender=2.6.9]

# gdb /usr/bin/rsync /mnt/vol2/core
GNU gdb 6.4.90-debian
Copyright (C) 2006 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i486-linux-gnu"...Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".


warning: Can't read pathname for load map: Input/output error.
Reading symbols from /lib/libacl.so.1...done.
Loaded symbols for /lib/libacl.so.1
Reading symbols from /lib/libpopt.so.0...done.
Loaded symbols for /lib/libpopt.so.0
Reading symbols from /lib/tls/i686/cmov/libc.so.6...done.
Loaded symbols for /lib/tls/i686/cmov/libc.so.6
Reading symbols from /lib/libattr.so.1...done.
Loaded symbols for /lib/libattr.so.1
Reading symbols from /lib/ld-linux.so.2...Reading symbols from /usr/ lib/debug/lib/ld-2.3.6.so...done.
done.
Loaded symbols for /lib/ld-linux.so.2
Reading symbols from /lib/tls/i686/cmov/libnss_compat.so.2...done.
Loaded symbols for /lib/tls/i686/cmov/libnss_compat.so.2
Reading symbols from /lib/tls/i686/cmov/libnsl.so.1...done.
Loaded symbols for /lib/tls/i686/cmov/libnsl.so.1
Reading symbols from /lib/tls/i686/cmov/libnss_nis.so.2...done.
Loaded symbols for /lib/tls/i686/cmov/libnss_nis.so.2
Reading symbols from /lib/tls/i686/cmov/libnss_files.so.2...done.
Loaded symbols for /lib/tls/i686/cmov/libnss_files.so.2
Core was generated by `rsync --verbose --archive --hard-links --acls / mnt/vol1/ /mnt/vol2/'.
Program terminated with signal 11, Segmentation fault.
#0  rsync_acl_free (racl=0x2) at acls.c:190
190             if (racl->users.idas)
(gdb) ptype racl
type = struct rsync_acl {
    ida_entries users;
    ida_entries groups;
    unsigned char user_obj;
    unsigned char group_obj;
    unsigned char mask;
    unsigned char other;
} *
(gdb) bt
#0  rsync_acl_free (racl=0x2) at acls.c:190
#1  0x0806ea56 in free_acl (sxp=0xbf8d2f40) at acls.c:200
#2 0x0806925c in hard_link_cluster (file=0xb7d52f08, master=4, itemizing=1, code=FLOG) at hlink.c:323 #3 0x0804bc2a in check_for_finished_hlinks (itemizing=1, code=FLOG) at generator.c:611
#4  0x080689aa in get_redo_num (itemizing=1, code=FLOG) at io.c:393
#5 0x0804f225 in generate_files (f_out=1, flist=0x80a2ab0, local_name=0x0) at generator.c:1558 #6 0x080588d1 in do_recv (f_in=0, f_out=1, flist=0x80a2ab0, local_name=0x0) at main.c:764 #7 0x080590fb in start_server (f_in=0, f_out=1, argc=2, argv=0xbf8d41cc) at main.c:871
#8  0x0805a399 in child_main (argc=2, argv=0xbf8d41cc) at main.c:878
#9 0x08070868 in local_child (argc=2, argv=0xbf8d41cc, f_in=0xbf8d5200, f_out=0xbf8d51fc,
    child_main=0x805a370 <child_main>) at pipe.c:149
#10 0x08059c10 in main (argc=1915617326, argv=0x636e7973) at main.c:438
(gdb) up
#1  0x0806ea56 in free_acl (sxp=0xbf8d2f40) at acls.c:200
200                     rsync_acl_free(sxp->acc_acl);
(gdb) ptype sxp
type = struct {
    struct stat st;
    struct rsync_acl *acc_acl;
    struct rsync_acl *def_acl;
} *
(gdb) print *sxp
$1 = {st = {st_dev = 0, __pad1 = 1794, __st_ino = 0, st_mode = 0, st_nlink = 2, st_uid = 16877, st_gid = 2, st_rdev = 3085852609, __pad2 = 0, st_size = 0, st_blksize = 1024, st_blocks = 17592186044416, st_atim = { tv_sec = 2, tv_nsec = 17}, st_mtim = {tv_sec = 1233175523, tv_nsec = 0}, st_ctim = { tv_sec = -1208334636, tv_nsec = 0}, st_ino = 1233175523}, acc_acl = 0x2, def_acl = 0x0}
(gdb) up
#2 0x0806925c in hard_link_cluster (file=0xb7d52f08, master=4, itemizing=1, code=FLOG) at hlink.c:323
323                             free_acl(&sx);
(gdb) list hard_link_cluster
290     #endif
291     
292     
293 void hard_link_cluster(struct file_struct *file, int master, int itemizing,
294                            enum logcode code)
295     {
296     #ifdef SUPPORT_HARD_LINKS
297             char hlink1[MAXPATHLEN];
298             char *hlink2;
299             statx sx;
(gdb)
300             STRUCT_STAT st;
301             int statret, ndx = master;
302     
303             file->F_HLINDEX = FINISHED_LINK;
304             if (link_stat(f_name(file, hlink1), &st, 0) < 0)
305                     return;
306             if (!(file->flags & FLAG_HLINK_TOL)) {
307                     while (!(file->flags & FLAG_HLINK_EOL)) {
308                             ndx = file->F_NEXT;
309                             file = FPTR(ndx);
(gdb)
310                     }
311             }
312             do {
313                     ndx = file->F_NEXT;
314                     file = FPTR(ndx);
315                     if (file->F_HLINDEX != SKIPPED_LINK)
316                             continue;
317                     hlink2 = f_name(file, NULL);
318                     statret = link_stat(hlink2, &sx.st, 0);
319                     maybe_hard_link(file, ndx, hlink2, statret, &sx,
(gdb)
320                                     hlink1, &st, itemizing, code);
321     #ifdef SUPPORT_ACLS
322                     if (preserve_acls)
323                             free_acl(&sx);
324     #endif
325                     if (remove_source_files == 1 && do_xfers) {
326                             char numbuf[4];
327                             SIVAL(numbuf, 0, ndx);
328                             send_msg(MSG_SUCCESS, numbuf, 4);
329                     }
(gdb) list maybe_hard_link
149     #ifdef SUPPORT_HARD_LINKS
150     static int maybe_hard_link(struct file_struct *file, int ndx,
151                                char *fname, int statret, statx *sxp,
152                                char *toname, STRUCT_STAT *to_st,
153                                int itemizing, enum logcode code)
154     {
155             if (statret == 0) {
156                     if (sxp->st.st_dev == to_st->st_dev
157                      && sxp->st.st_ino == to_st->st_ino) {
158                             if (itemizing) {
(gdb)
159     #ifdef SUPPORT_ACLS
160                                     if (preserve_acls && !ACL_READY(*sxp))
161                                             get_acl(fname, sxp);
162     #endif
163                                     itemize(file, ndx, statret, sxp,
164                                             ITEM_LOCAL_CHANGE | 
ITEM_XNAME_FOLLOWS,
165                                             0, "");
166                             }
167                             return 0;
168                     }
(gdb) quit

Attachment: acl-statx-init.patch
Description: Binary data



Reply via email to