Hi Matteo,

Matteo Croce <[email protected]> writes:

> copy_cat() is doing unnecessary math to derive the maximum size allowed
> by the copy_file_range() syscall. This calculates an excessively big
> number which is 0x7FFFFFFFC0000000.
> Linux syscalls have a much lower limit for single I/O operations,
> which is defined in kernel as `INT_MAX - PAGE_SIZE`, so lower the limit
> to that.
>
> evidence of the limit is shown below:
>
> $ uname -a
> Linux v 6.17.5 #191 SMP Thu Oct 23 21:43:33 CEST 2025 x86_64 GNU/Linux
>
> $ ll random
> -rw-r--r-- 1 teknoraver users 8.0G Dec 12 19:19 random
>
> $ strace -e copy_file_range src/cat random >random2
> copy_file_range(3, NULL, 1, NULL, 9223372035781033984, 0) = 2147479552
> copy_file_range(3, NULL, 1, NULL, 9223372035781033984, 0) = 2147479552
> copy_file_range(3, NULL, 1, NULL, 9223372035781033984, 0) = 2147479552
> copy_file_range(3, NULL, 1, NULL, 9223372035781033984, 0) = 2147479552
> copy_file_range(3, NULL, 1, NULL, 9223372035781033984, 0) = 16384
> copy_file_range(3, NULL, 1, NULL, 9223372035781033984, 0) = 0
> ---
>  src/cat.c | 8 ++++----
>  1 file changed, 4 insertions(+), 4 deletions(-)
>
> diff --git a/src/cat.c b/src/cat.c
> index c02210301..811955459 100644
> --- a/src/cat.c
> +++ b/src/cat.c
> @@ -503,10 +503,10 @@ cat (char *inbuf, idx_t insize, char *outbuf, idx_t 
> outsize,
>  static int
>  copy_cat (void)
>  {
> -  /* Copy at most COPY_MAX bytes at a time; this is min
> -     (SSIZE_MAX, SIZE_MAX) truncated to a value that is
> -     surely aligned well.  */
> -  ssize_t copy_max = MIN (SSIZE_MAX, SIZE_MAX) >> 30 << 30;
> +  /* According to the read(2) man page, on Linux syscalls will transfer at 
> most
> +   * 0x7ffff000 (2,147,479,552) bytes on both 32-bit and 64-bit systems.
> +   * In kernel this is defined as MAX_RW_COUNT which is INT_MAX - PAGE_SIZE. 
> */
> +  ssize_t copy_max = 0x7ffff000;
>  
>    /* copy_file_range does not support some cases, and it
>       incorrectly returns 0 when reading from the proc file

I would rather keep this limit as-is.

SSIZE_MAX and SIZE_MAX are constants, so a decent compiler will
certainly optimize the operations away.

Also, systems other than Linux have copy_file_range nowadays. As long as
using a large value is safe it is fine. A recent glibc bug made it not
okay, but we check for it in Gnulib [1].

Thanks,
Collin

[1] https://sourceware.org/PR33245

Reply via email to