Bug 1757902

Summary: fix compat statfs64() returning EOVERFLOW for when _FILE_OFFSET_BITS=64
Product: Red Hat Enterprise Linux 8 Reporter: Glen Newell <gnewell>
Component: kernelAssignee: Eric Sandeen <esandeen>
kernel sub component: VFS QA Contact: Kun Wang <kunwan>
Status: CLOSED ERRATA Docs Contact:
Severity: high    
Priority: high CC: aviro, chhudson, dhowells, dwysocha, esandeen, fsorenso, mkderoy, mszeredi, swhiteho, xzhou, yozone
Version: 8.2Keywords: Reproducer
Target Milestone: rcFlags: pm-rhel: mirror+
Target Release: 8.0   
Hardware: Unspecified   
OS: Unspecified   
Whiteboard:
Fixed In Version: kernel-4.18.0-148.el8 Doc Type: If docs needed, set a value
Doc Text:
Story Points: ---
Clone Of:
: 1758001 (view as bug list) Environment:
Last Closed: 2020-04-28 16:28:55 UTC Type: Bug
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:
Bug Depends On:    
Bug Blocks: 1758001    
Attachments:
Description Flags
updated reproducer none

Comment 1 Eric Sandeen 2019-10-02 18:46:37 UTC
It would be worth stating the details of a filesystem which will cause the problem, and which field is causing the overflow for the customer.  From reading the attached cases I assume it's f_files / f_ffree, but please confirm.

Comment 2 Eric Sandeen 2019-10-02 19:18:49 UTC
This was essentially regressed by commit 64d2ab32efe3

viro had proposed:

---
So the whole put_compat_statfs64() thing should be
static int put_compat_statfs64(struct compat_statfs64 __user *ubuf, struct kstatfs *kbuf)
{
	struct compat_statfs64 buf;

	if ((kbuf->f_bsize | kbuf->f_frsize) & 0xffffffff00000000ULL)
		return -EOVERFLOW;

	memset(&buf, 0, sizeof(struct compat_statfs64));
	buf.f_type = kbuf->f_type;
	buf.f_bsize = kbuf->f_bsize;
	buf.f_blocks = kbuf->f_blocks;
	buf.f_bfree = kbuf->f_bfree;
	buf.f_bavail = kbuf->f_bavail;
	buf.f_files = kbuf->f_files;
	buf.f_ffree = kbuf->f_ffree;
	buf.f_namelen = kbuf->f_namelen;
	buf.f_fsid.val[0] = kbuf->f_fsid.val[0];
	buf.f_fsid.val[1] = kbuf->f_fsid.val[1];
	buf.f_frsize = kbuf->f_frsize;
	buf.f_flags = kbuf->f_flags;
	if (copy_to_user(ubuf, &buf, sizeof(struct compat_statfs64)))
		return -EFAULT;
	return 0;
}
and that's it for compat_statfs64 bug
----

but that never went anywhere.  Al, did it just get lost or was there some other concern?

Comment 5 Eric Sandeen 2019-10-02 19:59:20 UTC
Just as a note, RHEL7 kernel-3.10.0-585.el7 and later (i.e. RHEL7.4+) appears to also have this flaw.

Comment 6 Michael DeRoy 2019-10-02 20:17:34 UTC
Thank you for looking at this. Here are the details I specified in the support ticket.
example filesystem was created with a number of inodes that is above the 32-bit integer max...we did this with a 2TB partition created with these options (our customer NAS had a large number of inodes and we were able to reproduce the issue this way)
mkfs.xfs -fn ftype=1 -b size=1024 -i maxpct=99 -i size=512 /dev/nvme0n1p3

a simple c++ program reproduces this issue

// Simple C++ program to display "Hello World" 
  
// Header file for input output functions 
#include <iostream>  
#include <sys/statvfs.h>  
#include <errno.h>
using namespace std; 
  
// main function - 
// where the execution of program begins 
int main() 
{ 
	// prints hello world 
	cout<<"Hello World" << endl; 
      
	struct statvfs statbuf;
	int ret = statvfs("/badfs",&statbuf);
	cout << "errno " << errno << endl;
	return 0; 
} 

[root@e1n1 mkderoy]# g++ test.cpp
[root@e1n1 mkderoy]# ./a.out
Hello World
errno 0
[root@e1n1 mkderoy]# g++ -m32 test.cpp
[root@e1n1 mkderoy]# ./a.out
Hello World
errno 75
and even with largefile support it dies
[root@e1n1 mkderoy]# g++ -m32 -D_FILE_OFFSET_BITS=64 test.cpp
[root@e1n1 mkderoy]# ./a.out
Hello World
errno 75

Compiling with -D_FILE_OFFSET_BITS=64 works on Rhel6 and not Rhel7

Comment 9 Eric Sandeen 2019-10-02 20:55:45 UTC
If the filesystem in question is XFS, and /IF/ the customer isn't actually using nearly 2^32 files, one option may be to use xfs_growfs to reduce the maximum percentage of space which is allowable for inode allocation:

       -m     Specify a new value for  the  maximum  percentage  of  space  in  the
              filesystem  that  can  be allocated as inodes. In mkfs.xfs(8) this is
              specified with -i maxpct=nn.

This will reduce the value reported in f_files and f_ffree, and a sufficiently small percentage value will reduce this to something that doesn't overflow.

Comment 10 Eric Sandeen 2019-10-02 21:19:09 UTC
I've sent a patch upstream:

https://lkml.org/lkml/2019/10/2/891

Comment 12 Frank Sorenson 2019-10-03 20:04:24 UTC
Created attachment 1622406 [details]
updated reproducer

Reproducer updated:
 * test both statfs() and statfs64()
 * require expected compile-time options
 * verify that at least one of the 64-bit fields would have overflowed a 32-bit value
 * success/failure return value

 # mount -t tmpfs -o nr_inodes=4294967297 tmpfs /mnt
 # gcc compat_statfs.c -o compat_statfs -m32 -D_FILE_OFFSET_BITS=64
 # ./compat_statfs /mnt

Comment 16 Frank Sorenson 2019-10-04 13:45:09 UTC
The RHEL 7 version of this bug is bz1758001

Comment 17 Frank Sorenson 2019-10-04 13:46:08 UTC
(In reply to Frank Sorenson from comment #12)
> Created attachment 1622406 [details]
> updated reproducer
> 
> Reproducer updated:
>  * test both statfs() and statfs64()

ugh.  This should have been statfs() and fstatfs()

Comment 21 Phillip Lougher 2019-11-08 16:44:06 UTC
Patch(es) available on kernel-4.18.0-148.el8

Comment 26 errata-xmlrpc 2020-04-28 16:28:55 UTC
Since the problem described in this bug report should be
resolved in a recent advisory, it has been closed with a
resolution of ERRATA.

For information on the advisory, and where to find the updated
files, follow the link below.

If the solution does not work for you, open a new bug report.

https://access.redhat.com/errata/RHSA-2020:1769