[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <202411021337.85E9BB06@keescook>
Date: Sat, 2 Nov 2024 13:43:48 -0700
From: Kees Cook <kees@...nel.org>
To: Thomas Weißschuh <linux@...ssschuh.net>
Cc: linux-hardening@...r.kernel.org, linux-kernel@...r.kernel.org
Subject: Re: Fortify compilation warning in __padata_set_cpumask()
On Thu, Oct 31, 2024 at 08:40:33PM +0000, Thomas Weißschuh wrote:
> Hi Kees,
>
> I'm running into compilation warnings/errors due to fortify-string.h.
>
> Environment:
> - Commit 0fc810ae3ae110f9e2fcccce80fc8c8d62f97907 (current mainline master)
> - gcc (GCC) 14.2.1 20240910
> - Relevant config (from an Arch Linux distro config):
> CONFIG_64BIT=y
> CONFIG_X86_64=y
> CONFIG_NR_CPUS=320
> CONFIG_NR_CPUS_RANGE_BEGIN=2
> CONFIG_NR_CPUS_RANGE_END=512
> CONFIG_NR_CPUS_RANGE_DEFAULT=64
> CONFIG_PADATA=y
>
> Warning:
>
> CC kernel/padata.o
> In file included from ./include/linux/string.h:390,
> from ./include/linux/bitmap.h:13,
> from ./include/linux/cpumask.h:12,
> from ./arch/x86/include/asm/paravirt.h:21,
> from ./arch/x86/include/asm/irqflags.h:80,
> from ./include/linux/irqflags.h:18,
> from ./include/linux/spinlock.h:59,
> from ./include/linux/swait.h:7,
> from ./include/linux/completion.h:12,
> from kernel/padata.c:14:
> In function ‘bitmap_copy’,
> inlined from ‘cpumask_copy’ at ./include/linux/cpumask.h:839:2,
> inlined from ‘__padata_set_cpumasks’ at kernel/padata.c:730:2:
> ./include/linux/fortify-string.h:114:33: error: ‘__builtin_memcpy’ reading between 41 and 536870904 bytes from a region of size 40 [-Werror=stringop-overread]
> 114 | #define __underlying_memcpy __builtin_memcpy
> | ^
> ./include/linux/fortify-string.h:633:9: note: in expansion of macro ‘__underlying_memcpy’
> 633 | __underlying_##op(p, q, __fortify_size); \
> | ^~~~~~~~~~~~~
> ./include/linux/fortify-string.h:678:26: note: in expansion of macro ‘__fortify_memcpy_chk’
> 678 | #define memcpy(p, q, s) __fortify_memcpy_chk(p, q, s, \
> | ^~~~~~~~~~~~~~~~~~~~
> ./include/linux/bitmap.h:259:17: note: in expansion of macro ‘memcpy’
> 259 | memcpy(dst, src, len);
> | ^~~~~~
> kernel/padata.c: In function ‘__padata_set_cpumasks’:
> kernel/padata.c:713:48: note: source object ‘pcpumask’ of size [0, 40]
> 713 | cpumask_var_t pcpumask,
> | ~~~~~~~~~~~~~~^~~~~~~~
> In function ‘bitmap_copy’,
> inlined from ‘cpumask_copy’ at ./include/linux/cpumask.h:839:2,
> inlined from ‘__padata_set_cpumasks’ at kernel/padata.c:730:2:
> ./include/linux/fortify-string.h:114:33: error: ‘__builtin_memcpy’ reading between 41 and 536870904 bytes from a region of size 40 [-Werror=stringop-overread]
> 114 | #define __underlying_memcpy __builtin_memcpy
> | ^
> ./include/linux/fortify-string.h:633:9: note: in expansion of macro ‘__underlying_memcpy’
> 633 | __underlying_##op(p, q, __fortify_size); \
> | ^~~~~~~~~~~~~
> ./include/linux/fortify-string.h:678:26: note: in expansion of macro ‘__fortify_memcpy_chk’
> 678 | #define memcpy(p, q, s) __fortify_memcpy_chk(p, q, s, \
> | ^~~~~~~~~~~~~~~~~~~~
> ./include/linux/bitmap.h:259:17: note: in expansion of macro ‘memcpy’
> 259 | memcpy(dst, src, len);
> | ^~~~~~
> kernel/padata.c: In function ‘__padata_set_cpumasks’:
> kernel/padata.c:713:48: note: source object ‘pcpumask’ of size [0, 40]
> 713 | cpumask_var_t pcpumask,
> | ~~~~~~~~~~~~~~^~~~~~~~
> cc1: all warnings being treated as errors
>
> Code:
>
> 712 static int __padata_set_cpumasks(struct padata_instance *pinst,
> 713 cpumask_var_t pcpumask,
> 714 cpumask_var_t cbcpumask)
> 715 {
> 716 int valid;
> 717 int err;
> 718
> 719 valid = padata_validate_cpumask(pinst, pcpumask);
> 720 if (!valid) {
> 721 __padata_stop(pinst);
> 722 goto out_replace;
> 723 }
> 724
> 725 valid = padata_validate_cpumask(pinst, cbcpumask);
> 726 if (!valid)
> 727 __padata_stop(pinst);
> 728
> 729 out_replace:
> 730 cpumask_copy(pinst->cpumask.pcpu, pcpumask);
> 731 cpumask_copy(pinst->cpumask.cbcpu, cbcpumask);
> 732
> 733 err = padata_setup_cpumasks(pinst) ?: padata_replace(pinst);
> 734
> 735 if (valid)
> 736 __padata_start(pinst);
> 737
> 738 return err;
> 739 }
>
>
> The weird thing is, that only the cpumask_copy() in line 730 triggers
> this warning. The one in line 731 doesn't. Also this is the only
> instance of the warning I see in the whole build.
>
> The warning goes away with the following change, but that introduces
> runtime overhead and feels wrong. Also it doesn't explain why this
> specific call is different from all others.
>
> diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h
> index 9278a50d514f..ded9d1bcef03 100644
> --- a/include/linux/cpumask.h
> +++ b/include/linux/cpumask.h
> @@ -836,7 +838,7 @@ void cpumask_shift_left(struct cpumask *dstp, const struct cpumask *srcp, int n)
> static __always_inline
> void cpumask_copy(struct cpumask *dstp, const struct cpumask *srcp)
> {
> - bitmap_copy(cpumask_bits(dstp), cpumask_bits(srcp), large_cpumask_bits);
> + bitmap_copy(cpumask_bits(dstp), cpumask_bits(srcp), MIN(NR_CPUS, large_cpumask_bits));
> }
>
> /**
>
> Any ideas?
My notes on looking at this:
#define DECLARE_BITMAP(name,bits) \
unsigned long name[BITS_TO_LONGS(bits)]
...
typedef struct cpumask { DECLARE_BITMAP(bits, NR_CPUS); } cpumask_t;
#ifdef CONFIG_CPUMASK_OFFSTACK
typedef struct cpumask *cpumask_var_t;
#else
typedef struct cpumask cpumask_var_t[1];
#endif /* CONFIG_CPUMASK_OFFSTACK */
...
#define cpumask_bits(maskp) ((maskp)->bits)
...
int padata_set_cpumask(struct padata_instance *pinst, int cpumask_type,
cpumask_var_t cpumask)
...
struct cpumask *serial_mask, *parallel_mask;
...
parallel_mask = cpumask;
...or...
parallel_mask = pinst->cpumask.pcpu;
...
err = __padata_set_cpumasks(pinst, parallel_mask, serial_mask);
...
static int __padata_set_cpumasks(struct padata_instance *pinst,
cpumask_var_t pcpumask,
cpumask_var_t cbcpumask)
...
cpumask_copy(pinst->cpumask.cbcpu, cbcpumask);
...
static __always_inline
void cpumask_copy(struct cpumask *dstp, const struct cpumask *srcp)
{
bitmap_copy(cpumask_bits(dstp), cpumask_bits(srcp),
large_cpumask_bits);
}
...
extern unsigned int nr_cpu_ids;
...
#if NR_CPUS <= BITS_PER_LONG // false: 320 <= 64
#define small_cpumask_bits ((unsigned int)NR_CPUS)
#define large_cpumask_bits ((unsigned int)NR_CPUS)
#elif NR_CPUS <= 4*BITS_PER_LONG // false: 320 <= 256
#define small_cpumask_bits nr_cpu_ids
#define large_cpumask_bits ((unsigned int)NR_CPUS)
#else
#define small_cpumask_bits nr_cpu_ids
#define large_cpumask_bits nr_cpu_ids // not a compile-time constant
#endif
...
static __always_inline
void bitmap_copy(unsigned long *dst, const unsigned long *src, unsigned
int nbits)
{
unsigned int len = bitmap_size(nbits);
if (small_const_nbits(nbits))
*dst = *src;
else
memcpy(dst, src, len);
}
So the result is:
memcpy(pcpumask->bits, cbcpumask->bits, nr_cpu_ids)
And the error is that the compiler has determined that nc_cpu_ids got
limited to possibly have a value between 41 and 536870904. This is an
odd limit, though: 0x1FFFFFF8, but does feel constructable -- it's close
to some magic numbers.
There are some new diagnostics being added to GCC that might help track
this down[1]. I'm waiting for a v4 before I take it for a spin.
-Kees
[1] https://gcc.gnu.org/pipermail/gcc-patches/2024-October/666870.html
--
Kees Cook
Powered by blists - more mailing lists