Description
The exfat driver fails to write files with a size > 4GB when these conditions are met:
- The kernel is built for a 32bit system
- The kernel config switch CONFIG_LBDAF is not set (i.e. no files > 2TB are supported)
We stumbled upon this when integrating the driver into our 32bit embedded Linux ecosystem.
The reason for this is an integer overflow when converting number of blocks into a file position, as for instance here:
https://github.com/namjaejeon/linux-exfat-oot/blob/5.14.1/inode.c#L338
Line:
pos = EXFAT_BLK_TO_B((iblock + 1), sb);
which translates to:
pos = (iblock + 1) << sb->s_blocksize_bits;
iblock
is of type sector_t
. Normally this is a 64bit integer, but on a 32bit system without CONFIG_LBDAF
being set, it is just 32bit.
pos
, on the other hand is a full 64bit integer
Unfortunately, the compiler performs the calculation in 32bit range without issuing a warning. As a result, an overflow will occur as soon as the file size reaches 4GB
A simple fix would be:
pos = EXFAT_BLK_TO_B((loff_t)(iblock + 1), sb);
This makes sure that the left side is cast to 64bit before shifting it.
Attention:
We've located a few more such lines where potentially 32bit-overflow-prone left-shifts are done, namely:
https://github.com/namjaejeon/linux-exfat-oot/blob/5.14.1/dir.c#L171
https://github.com/namjaejeon/linux-exfat-oot/blob/5.14.1/dir.c#L187
https://github.com/namjaejeon/linux-exfat-oot/blob/5.14.1/inode.c#L249
https://github.com/namjaejeon/linux-exfat-oot/blob/5.14.1/inode.c#L338
https://github.com/namjaejeon/linux-exfat-oot/blob/5.14.1/inode.c#L357
https://github.com/namjaejeon/linux-exfat-oot/blob/5.14.1/namei.c#L402
https://github.com/namjaejeon/linux-exfat-oot/blob/5.14.1/namei.c#L1292
https://github.com/namjaejeon/linux-exfat-oot/blob/5.14.1/super.c#L592
In general, all lines where types sector_t
or blkcnt_t
are used with a left-shift operator should be considered.