Redhat reports: The listxattr syscall can corrupt user space under certain circumstances. The problem seems to be related to signed/unsigned conversion during size promotion. The function return_EIO returns an int but its used as a ssize_t with a comparison to 0. This causes the range check to fail and copy_to_user copies way too much. The command line "fsfuzz iso9660" can easily reproduce this behavior. The problem here is the bad_inode_ops, and how they're set up. isofs creates a bad inode, which is fine, but then any op called from that inode is supposed to return -EIO via this method: static int return_EIO(void) { return -EIO; } #define EIO_ERROR_SSIZE ((void *) (return_EIO_ssize)) static struct inode_operations bad_inode_ops = { ... .listxattr = EIO_ERROR, ... } but, ssize_t (*listxattr) (struct dentry *, char *, size_t); and ssize_t is 64 bits on x86_64 and others, while return_EIO returns only an int (32 bits everywhere). So thanks to the (void *) cast we don't promote the type correctly, looks like, and our EIO, -5, 0xfffffffa turns into 0x00000000fffffffa or 4294967291, and we splat all over the user's buffer and then some. The impact of this issue is kinda serious. You can theoretically read kernel memory or escalated privileges. However the pre-condition is that you need a bad inode first. This pre-condition is not easy to met if you don't have rights to mount a volume or have an automounter in place. So from our current analysis the automated exploitation is kinda hard to achieve.
Created attachment 104561 [details, diff] return_EIO_fixes
Holding until release date.
Public since ages...