From: "Jason A. Donenfeld" <Jason@zx2c4.com>
To: Kees Cook <keescook@chromium.org>
Cc: linux-kernel@vger.kernel.org, patches@lists.linux.dev,
"Andreas Noever" <andreas.noever@gmail.com>,
"Andrew Morton" <akpm@linux-foundation.org>,
"Andy Shevchenko" <andriy.shevchenko@linux.intel.com>,
"Borislav Petkov" <bp@alien8.de>,
"Catalin Marinas" <catalin.marinas@arm.com>,
"Christoph Böhmwalder" <christoph.boehmwalder@linbit.com>,
"Christoph Hellwig" <hch@lst.de>,
"Christophe Leroy" <christophe.leroy@csgroup.eu>,
"Daniel Borkmann" <daniel@iogearbox.net>,
"Dave Airlie" <airlied@redhat.com>,
"Dave Hansen" <dave.hansen@linux.intel.com>,
"David S . Miller" <davem@davemloft.net>,
"Eric Dumazet" <edumazet@google.com>,
"Florian Westphal" <fw@strlen.de>,
"Greg Kroah-Hartman" <gregkh@linuxfoundation.org>,
"H . Peter Anvin" <hpa@zytor.com>,
"Heiko Carstens" <hca@linux.ibm.com>,
"Helge Deller" <deller@gmx.de>,
"Herbert Xu" <herbert@gondor.apana.org.au>,
"Huacai Chen" <chenhuacai@kernel.org>,
"Hugh Dickins" <hughd@google.com>,
"Jakub Kicinski" <kuba@kernel.org>,
"James E . J . Bottomley" <jejb@linux.ibm.com>,
"Jan Kara" <jack@suse.com>, "Jason Gunthorpe" <jgg@ziepe.ca>,
"Jens Axboe" <axboe@kernel.dk>,
"Johannes Berg" <johannes@sipsolutions.net>,
"Jonathan Corbet" <corbet@lwn.net>,
"Jozsef Kadlecsik" <kadlec@netfilter.org>,
"KP Singh" <kpsingh@kernel.org>, "Marco Elver" <elver@google.com>,
"Mauro Carvalho Chehab" <mchehab@kernel.org>,
"Michael Ellerman" <mpe@ellerman.id.au>,
"Pablo Neira Ayuso" <pablo@netfilter.org>,
"Paolo Abeni" <pabeni@redhat.com>,
"Peter Zijlstra" <peterz@infradead.org>,
"Richard Weinberger" <richard@nod.at>,
"Russell King" <linux@armlinux.org.uk>,
"Theodore Ts'o" <tytso@mit.edu>,
"Thomas Bogendoerfer" <tsbogend@alpha.franken.de>,
"Thomas Gleixner" <tglx@linutronix.de>,
"Thomas Graf" <tgraf@suug.ch>,
"Ulf Hansson" <ulf.hansson@linaro.org>,
"Vignesh Raghavendra" <vigneshr@ti.com>,
"WANG Xuerui" <kernel@xen0n.name>,
"Will Deacon" <will@kernel.org>,
"Yury Norov" <yury.norov@gmail.com>,
dri-devel@lists.freedesktop.org, kasan-dev@googlegroups.com,
kernel-janitors@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
linux-block@vger.kernel.org, linux-crypto@vger.kernel.org,
linux-doc@vger.kernel.org, linux-fsdevel@vger.kernel.org,
linux-media@vger.kernel.org, linux-mips@vger.kernel.org,
linux-mm@kvack.org, linux-mmc@vger.kernel.org,
linux-mtd@lists.infradead.org, linux-nvme@lists.infradead.org,
linux-parisc@vger.kernel.org, linux-rdma@vger.kernel.org,
linux-s390@vger.kernel.org, linux-um@lists.infradead.org,
linux-usb@vger.kernel.org, linux-wireless@vger.kernel.org,
linuxppc-dev@lists.ozlabs.org, loongarch@lists.linux.dev,
netdev@vger.kernel.org, sparclinux@vger.kernel.org,
x86@kernel.org, "Jan Kara" <jack@suse.cz>
Subject: Re: [PATCH v4 2/6] treewide: use prandom_u32_max() when possible
Date: Fri, 7 Oct 2022 20:21:28 -0600 [thread overview]
Message-ID: <Y0DeqDC3EnA4b6ZB@zx2c4.com> (raw)
In-Reply-To: <202210071241.445289C5@keescook>
On Fri, Oct 07, 2022 at 03:47:44PM -0700, Kees Cook wrote:
> On Fri, Oct 07, 2022 at 12:01:03PM -0600, Jason A. Donenfeld wrote:
> > Rather than incurring a division or requesting too many random bytes for
> > the given range, use the prandom_u32_max() function, which only takes
> > the minimum required bytes from the RNG and avoids divisions.
>
> I actually meant splitting the by-hand stuff by subsystem, but nearly
> all of these can be done mechanically too, so it shouldn't be bad. Notes
> below...
Oh, cool, more coccinelle. You're basically giving me a class on these
recipes. Much appreciated.
> > [...]
> > diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
> > index 92bcc1768f0b..87203429f802 100644
> > --- a/arch/arm64/kernel/process.c
> > +++ b/arch/arm64/kernel/process.c
> > @@ -595,7 +595,7 @@ unsigned long __get_wchan(struct task_struct *p)
> > unsigned long arch_align_stack(unsigned long sp)
> > {
> > if (!(current->personality & ADDR_NO_RANDOMIZE) && randomize_va_space)
> > - sp -= get_random_int() & ~PAGE_MASK;
> > + sp -= prandom_u32_max(PAGE_SIZE);
> > return sp & ~0xf;
> > }
> >
>
> @mask@
> expression MASK;
> @@
>
> - (get_random_int() & ~(MASK))
> + prandom_u32_max(MASK)
Not quite! PAGE_MASK != PAGE_SIZE. In this case, things get a litttttle
more complicated where you can do:
get_random_int() & MASK == prandom_u32_max(MASK + 1)
*only if all the top bits of MASK are set* That is, if MASK one less
than a power of two. Or if MASK & (MASK + 1) == 0.
(If those top bits aren't set, you can technically do
prandom_u32_max(MASK >> n + 1) << n. That'd be a nice thing to work out.
But yeesh, maybe a bit much for the time being and probably a bit beyond
coccinelle.)
This case here, though, is a bit more special, where we can just rely on
an obvious given kernel identity. Namely, PAGE_MASK == ~(PAGE_SIZE - 1).
So ~PAGE_MASK == PAGE_SIZE - 1.
So get_random_int() & ~PAGE_MASK == prandom_u32_max(PAGE_SIZE - 1 + 1).
So get_random_int() & ~PAGE_MASK == prandom_u32_max(PAGE_SIZE).
And most importantly, this makes the code more readable, since everybody
knows what bounding by PAGE_SIZE means, where as what on earth is
happening with the &~PAGE_MASK thing. So it's a good change. I'll try to
teach coccinelle about that special case.
> > diff --git a/arch/loongarch/kernel/vdso.c b/arch/loongarch/kernel/vdso.c
> > index f32c38abd791..8c9826062652 100644
> > --- a/arch/loongarch/kernel/vdso.c
> > +++ b/arch/loongarch/kernel/vdso.c
> > @@ -78,7 +78,7 @@ static unsigned long vdso_base(void)
> > unsigned long base = STACK_TOP;
> >
> > if (current->flags & PF_RANDOMIZE) {
> > - base += get_random_int() & (VDSO_RANDOMIZE_SIZE - 1);
> > + base += prandom_u32_max(VDSO_RANDOMIZE_SIZE);
> > base = PAGE_ALIGN(base);
> > }
> >
>
> @minus_one@
> expression FULL;
> @@
>
> - (get_random_int() & ((FULL) - 1)
> + prandom_u32_max(FULL)
Ahh, well, okay, this is the example I mentioned above. Only works if
FULL is saturated. Any clever way to get coccinelle to prove that? Can
it look at the value of constants?
>
> > diff --git a/arch/parisc/kernel/vdso.c b/arch/parisc/kernel/vdso.c
> > index 63dc44c4c246..47e5960a2f96 100644
> > --- a/arch/parisc/kernel/vdso.c
> > +++ b/arch/parisc/kernel/vdso.c
> > @@ -75,7 +75,7 @@ int arch_setup_additional_pages(struct linux_binprm *bprm,
> >
> > map_base = mm->mmap_base;
> > if (current->flags & PF_RANDOMIZE)
> > - map_base -= (get_random_int() & 0x1f) * PAGE_SIZE;
> > + map_base -= prandom_u32_max(0x20) * PAGE_SIZE;
> >
> > vdso_text_start = get_unmapped_area(NULL, map_base, vdso_text_len, 0, 0);
> >
>
> These are more fun, but Coccinelle can still do them with a little
> Pythonic help:
>
> // Find a potential literal
> @literal_mask@
> expression LITERAL;
> identifier randfunc =~ "get_random_int|prandom_u32|get_random_u32";
> position p;
> @@
>
> (randfunc()@p & (LITERAL))
>
> // Add one to the literal.
> @script:python add_one@
> literal << literal_mask.LITERAL;
> RESULT;
> @@
>
> if literal.startswith('0x'):
> value = int(literal, 16) + 1
> coccinelle.RESULT = cocci.make_expr("0x%x" % (value))
> elif literal[0] in '123456789':
> value = int(literal, 10) + 1
> coccinelle.RESULT = cocci.make_expr("%d" % (value))
> else:
> print("I don't know how to handle: %s" % (literal))
>
> // Replace the literal mask with the calculated result.
> @plus_one@
> expression literal_mask.LITERAL;
> position literal_mask.p;
> expression add_one.RESULT;
> identifier FUNC;
> @@
>
> - (FUNC()@p & (LITERAL))
> + prandom_u32_max(RESULT)
Oh that's pretty cool. I can do the saturation check in python, since
`value` holds the parsed result. Neat.
> > diff --git a/fs/ext2/ialloc.c b/fs/ext2/ialloc.c
> > index 998dd2ac8008..f4944c4dee60 100644
> > --- a/fs/ext2/ialloc.c
> > +++ b/fs/ext2/ialloc.c
> > @@ -277,8 +277,7 @@ static int find_group_orlov(struct super_block *sb, struct inode *parent)
> > int best_ndir = inodes_per_group;
> > int best_group = -1;
> >
> > - group = prandom_u32();
> > - parent_group = (unsigned)group % ngroups;
> > + parent_group = prandom_u32_max(ngroups);
> > for (i = 0; i < ngroups; i++) {
> > group = (parent_group + i) % ngroups;
> > desc = ext2_get_group_desc (sb, group, NULL);
>
> Okay, that one is too much for me -- checking that group is never used
> after the assignment removal is likely possible, but beyond my cocci
> know-how. :)
Yea this is a tricky one, which I initially didn't do by hand, but Jan
seemed fine with it, and it's clear if you look at it. Trixy cocci
indeed.
> > diff --git a/lib/test_hexdump.c b/lib/test_hexdump.c
> > index 0927f44cd478..41a0321f641a 100644
> > --- a/lib/test_hexdump.c
> > +++ b/lib/test_hexdump.c
> > @@ -208,7 +208,7 @@ static void __init test_hexdump_overflow(size_t buflen, size_t len,
> > static void __init test_hexdump_overflow_set(size_t buflen, bool ascii)
> > {
> > unsigned int i = 0;
> > - int rs = (prandom_u32_max(2) + 1) * 16;
> > + int rs = prandom_u32_max(2) + 1 * 16;
> >
> > do {
> > int gs = 1 << i;
>
> This looks wrong. Cocci says:
>
> - int rs = (get_random_int() % 2 + 1) * 16;
> + int rs = (prandom_u32_max(2) + 1) * 16;
!! Nice catch.
Alright, I'll give this a try with more cocci. The big difficulty at the
moment is the power of 2 constant checking thing. If you have any
pointers on that, would be nice.
Thanks a bunch for the guidance.
Jason
WARNING: multiple messages have this Message-ID (diff)
From: "Jason A. Donenfeld" <Jason@zx2c4.com>
To: Kees Cook <keescook@chromium.org>
Cc: linux-kernel@vger.kernel.org, patches@lists.linux.dev,
"Andreas Noever" <andreas.noever@gmail.com>,
"Andrew Morton" <akpm@linux-foundation.org>,
"Andy Shevchenko" <andriy.shevchenko@linux.intel.com>,
"Borislav Petkov" <bp@alien8.de>,
"Catalin Marinas" <catalin.marinas@arm.com>,
"Christoph Böhmwalder" <christoph.boehmwalder@linbit.com>,
"Christoph Hellwig" <hch@lst.de>,
"Christophe Leroy" <christophe.leroy@csgroup.eu>,
"Daniel Borkmann" <daniel@iogearbox.net>,
"Dave Airlie" <airlied@redhat.com>,
"Dave Hansen" <dave.hansen@linux.intel.com>,
"David S . Miller" <davem@davemloft.net>,
"Eric Dumazet" <edumazet@google.com>,
"Florian Westphal" <fw@strlen.de>,
"Greg Kroah-Hartman" <gregkh@linuxfoundation.org>,
"H . Peter Anvin" <hpa@zytor.com>,
"Heiko Carstens" <hca@linux.ibm.com>,
"Helge Deller" <deller@gmx.de>,
"Herbert Xu" <herbert@gondor.apana.org.au>,
"Huacai Chen" <chenhuacai@kernel.org>,
"Hugh Dickins" <hughd@google.com>,
"Jakub Kicinski" <kuba@kernel.org>,
"James E . J . Bottomley" <jejb@linux.ibm.com>,
"Jan Kara" <jack@suse.com>, "Jason Gunthorpe" <jgg@ziepe.ca>,
"Jens Axboe" <axboe@kernel.dk>,
"Johannes Berg" <johannes@sipsolutions.net>,
"Jonathan Corbet" <corbet@lwn.net>,
"Jozsef Kadlecsik" <kadlec@netfilter.org>,
"KP Singh" <kpsingh@kernel.org>, "Marco Elver" <elver@google.com>,
"Mauro Carvalho Chehab" <mchehab@kernel.org>,
"Michael Ellerman" <mpe@ellerman.id.au>,
"Pablo Neira Ayuso" <pablo@netfilter.org>,
"Paolo Abeni" <pabeni@redhat.com>,
"Peter Zijlstra" <peterz@infradead.org>,
"Richard Weinberger" <richard@nod.at>,
"Russell King" <linux@armlinux.org.uk>,
"Theodore Ts'o" <tytso@mit.edu>,
"Thomas Bogendoerfer" <tsbogend@alpha.franken.de>,
"Thomas Gleixner" <tglx@linutronix.de>,
"Thomas Graf" <tgraf@suug.ch>,
"Ulf Hansson" <ulf.hansson@linaro.org>,
"Vignesh Raghavendra" <vigneshr@ti.com>,
"WANG Xuerui" <kernel@xen0n.name>,
"Will Deacon" <will@kernel.org>,
"Yury Norov" <yury.norov@gmail.com>,
dri-devel@lists.freedesktop.org, kasan-dev@googlegroups.com,
kernel-janitors@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
linux-block@vger.kernel.org, linux-crypto@vger.kernel.org,
linux-doc@vger.kernel.org, linux-fsdevel@vger.kernel.org,
linux-media@vger.kernel.org, linux-mips@vger.kernel.org,
linux-mm@kvack.org, linux-mmc@vger.kernel.org,
linux-mtd@lists.infradead.org, linux-nvme@lists.infradead.org,
linux-parisc@vger.kernel.org, linux-rdma@vger.kernel.org,
linux-s390@vger.kernel.org, linux-um@lists.infradead.org,
linux-usb@vger.kernel.org, linux-wireless@vger.kernel.org,
linuxppc-dev@lists.ozlabs.org, loongarch@lists.linux.dev,
netdev@vger.kernel.org, sparclinux@vger.kernel.org,
x86@kernel.org, "Jan Kara" <jack@suse.cz>
Subject: Re: [PATCH v4 2/6] treewide: use prandom_u32_max() when possible
Date: Fri, 7 Oct 2022 20:21:28 -0600 [thread overview]
Message-ID: <Y0DeqDC3EnA4b6ZB@zx2c4.com> (raw)
In-Reply-To: <202210071241.445289C5@keescook>
On Fri, Oct 07, 2022 at 03:47:44PM -0700, Kees Cook wrote:
> On Fri, Oct 07, 2022 at 12:01:03PM -0600, Jason A. Donenfeld wrote:
> > Rather than incurring a division or requesting too many random bytes for
> > the given range, use the prandom_u32_max() function, which only takes
> > the minimum required bytes from the RNG and avoids divisions.
>
> I actually meant splitting the by-hand stuff by subsystem, but nearly
> all of these can be done mechanically too, so it shouldn't be bad. Notes
> below...
Oh, cool, more coccinelle. You're basically giving me a class on these
recipes. Much appreciated.
> > [...]
> > diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
> > index 92bcc1768f0b..87203429f802 100644
> > --- a/arch/arm64/kernel/process.c
> > +++ b/arch/arm64/kernel/process.c
> > @@ -595,7 +595,7 @@ unsigned long __get_wchan(struct task_struct *p)
> > unsigned long arch_align_stack(unsigned long sp)
> > {
> > if (!(current->personality & ADDR_NO_RANDOMIZE) && randomize_va_space)
> > - sp -= get_random_int() & ~PAGE_MASK;
> > + sp -= prandom_u32_max(PAGE_SIZE);
> > return sp & ~0xf;
> > }
> >
>
> @mask@
> expression MASK;
> @@
>
> - (get_random_int() & ~(MASK))
> + prandom_u32_max(MASK)
Not quite! PAGE_MASK != PAGE_SIZE. In this case, things get a litttttle
more complicated where you can do:
get_random_int() & MASK == prandom_u32_max(MASK + 1)
*only if all the top bits of MASK are set* That is, if MASK one less
than a power of two. Or if MASK & (MASK + 1) == 0.
(If those top bits aren't set, you can technically do
prandom_u32_max(MASK >> n + 1) << n. That'd be a nice thing to work out.
But yeesh, maybe a bit much for the time being and probably a bit beyond
coccinelle.)
This case here, though, is a bit more special, where we can just rely on
an obvious given kernel identity. Namely, PAGE_MASK == ~(PAGE_SIZE - 1).
So ~PAGE_MASK == PAGE_SIZE - 1.
So get_random_int() & ~PAGE_MASK == prandom_u32_max(PAGE_SIZE - 1 + 1).
So get_random_int() & ~PAGE_MASK == prandom_u32_max(PAGE_SIZE).
And most importantly, this makes the code more readable, since everybody
knows what bounding by PAGE_SIZE means, where as what on earth is
happening with the &~PAGE_MASK thing. So it's a good change. I'll try to
teach coccinelle about that special case.
> > diff --git a/arch/loongarch/kernel/vdso.c b/arch/loongarch/kernel/vdso.c
> > index f32c38abd791..8c9826062652 100644
> > --- a/arch/loongarch/kernel/vdso.c
> > +++ b/arch/loongarch/kernel/vdso.c
> > @@ -78,7 +78,7 @@ static unsigned long vdso_base(void)
> > unsigned long base = STACK_TOP;
> >
> > if (current->flags & PF_RANDOMIZE) {
> > - base += get_random_int() & (VDSO_RANDOMIZE_SIZE - 1);
> > + base += prandom_u32_max(VDSO_RANDOMIZE_SIZE);
> > base = PAGE_ALIGN(base);
> > }
> >
>
> @minus_one@
> expression FULL;
> @@
>
> - (get_random_int() & ((FULL) - 1)
> + prandom_u32_max(FULL)
Ahh, well, okay, this is the example I mentioned above. Only works if
FULL is saturated. Any clever way to get coccinelle to prove that? Can
it look at the value of constants?
>
> > diff --git a/arch/parisc/kernel/vdso.c b/arch/parisc/kernel/vdso.c
> > index 63dc44c4c246..47e5960a2f96 100644
> > --- a/arch/parisc/kernel/vdso.c
> > +++ b/arch/parisc/kernel/vdso.c
> > @@ -75,7 +75,7 @@ int arch_setup_additional_pages(struct linux_binprm *bprm,
> >
> > map_base = mm->mmap_base;
> > if (current->flags & PF_RANDOMIZE)
> > - map_base -= (get_random_int() & 0x1f) * PAGE_SIZE;
> > + map_base -= prandom_u32_max(0x20) * PAGE_SIZE;
> >
> > vdso_text_start = get_unmapped_area(NULL, map_base, vdso_text_len, 0, 0);
> >
>
> These are more fun, but Coccinelle can still do them with a little
> Pythonic help:
>
> // Find a potential literal
> @literal_mask@
> expression LITERAL;
> identifier randfunc =~ "get_random_int|prandom_u32|get_random_u32";
> position p;
> @@
>
> (randfunc()@p & (LITERAL))
>
> // Add one to the literal.
> @script:python add_one@
> literal << literal_mask.LITERAL;
> RESULT;
> @@
>
> if literal.startswith('0x'):
> value = int(literal, 16) + 1
> coccinelle.RESULT = cocci.make_expr("0x%x" % (value))
> elif literal[0] in '123456789':
> value = int(literal, 10) + 1
> coccinelle.RESULT = cocci.make_expr("%d" % (value))
> else:
> print("I don't know how to handle: %s" % (literal))
>
> // Replace the literal mask with the calculated result.
> @plus_one@
> expression literal_mask.LITERAL;
> position literal_mask.p;
> expression add_one.RESULT;
> identifier FUNC;
> @@
>
> - (FUNC()@p & (LITERAL))
> + prandom_u32_max(RESULT)
Oh that's pretty cool. I can do the saturation check in python, since
`value` holds the parsed result. Neat.
> > diff --git a/fs/ext2/ialloc.c b/fs/ext2/ialloc.c
> > index 998dd2ac8008..f4944c4dee60 100644
> > --- a/fs/ext2/ialloc.c
> > +++ b/fs/ext2/ialloc.c
> > @@ -277,8 +277,7 @@ static int find_group_orlov(struct super_block *sb, struct inode *parent)
> > int best_ndir = inodes_per_group;
> > int best_group = -1;
> >
> > - group = prandom_u32();
> > - parent_group = (unsigned)group % ngroups;
> > + parent_group = prandom_u32_max(ngroups);
> > for (i = 0; i < ngroups; i++) {
> > group = (parent_group + i) % ngroups;
> > desc = ext2_get_group_desc (sb, group, NULL);
>
> Okay, that one is too much for me -- checking that group is never used
> after the assignment removal is likely possible, but beyond my cocci
> know-how. :)
Yea this is a tricky one, which I initially didn't do by hand, but Jan
seemed fine with it, and it's clear if you look at it. Trixy cocci
indeed.
> > diff --git a/lib/test_hexdump.c b/lib/test_hexdump.c
> > index 0927f44cd478..41a0321f641a 100644
> > --- a/lib/test_hexdump.c
> > +++ b/lib/test_hexdump.c
> > @@ -208,7 +208,7 @@ static void __init test_hexdump_overflow(size_t buflen, size_t len,
> > static void __init test_hexdump_overflow_set(size_t buflen, bool ascii)
> > {
> > unsigned int i = 0;
> > - int rs = (prandom_u32_max(2) + 1) * 16;
> > + int rs = prandom_u32_max(2) + 1 * 16;
> >
> > do {
> > int gs = 1 << i;
>
> This looks wrong. Cocci says:
>
> - int rs = (get_random_int() % 2 + 1) * 16;
> + int rs = (prandom_u32_max(2) + 1) * 16;
!! Nice catch.
Alright, I'll give this a try with more cocci. The big difficulty at the
moment is the power of 2 constant checking thing. If you have any
pointers on that, would be nice.
Thanks a bunch for the guidance.
Jason
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
WARNING: multiple messages have this Message-ID (diff)
From: "Jason A. Donenfeld" <Jason@zx2c4.com>
To: Kees Cook <keescook@chromium.org>
Cc: linux-kernel@vger.kernel.org, patches@lists.linux.dev,
"Andreas Noever" <andreas.noever@gmail.com>,
"Andrew Morton" <akpm@linux-foundation.org>,
"Andy Shevchenko" <andriy.shevchenko@linux.intel.com>,
"Borislav Petkov" <bp@alien8.de>,
"Catalin Marinas" <catalin.marinas@arm.com>,
"Christoph Böhmwalder" <christoph.boehmwalder@linbit.com>,
"Christoph Hellwig" <hch@lst.de>,
"Christophe Leroy" <christophe.leroy@csgroup.eu>,
"Daniel Borkmann" <daniel@iogearbox.net>,
"Dave Airlie" <airlied@redhat.com>,
"Dave Hansen" <dave.hansen@linux.intel.com>,
"David S . Miller" <davem@davemloft.net>,
"Eric Dumazet" <edumazet@google.com>,
"Florian Westphal" <fw@strlen.de>,
"Greg Kroah-Hartman" <gregkh@linuxfoundation.org>,
"H . Peter Anvin" <hpa@zytor.com>,
"Heiko Carstens" <hca@linux.ibm.com>,
"Helge Deller" <deller@gmx.de>,
"Herbert Xu" <herbert@gondor.apana.org.au>,
"Huacai Chen" <chenhuacai@kernel.org>,
"Hugh Dickins" <hughd@google.com>,
"Jakub Kicinski" <kuba@kernel.org>,
"James E . J . Bottomley" <jejb@linux.ibm.com>,
"Jan Kara" <jack@suse.com>, "Jason Gunthorpe" <jgg@ziepe.ca>,
"Jens Axboe" <axboe@kernel.dk>,
"Johannes Berg" <johannes@sipsolutions.net>,
"Jonathan Corbet" <corbet@lwn.net>,
"Jozsef Kadlecsik" <kadlec@netfilter.org>,
"KP Singh" <kpsingh@kernel.org>, "Marco Elver" <elver@google.com>,
"Mauro Carvalho Chehab" <mchehab@kernel.org>,
"Michael Ellerman" <mpe@ellerman.id.au>,
"Pablo Neira Ayuso" <pablo@netfilter.org>,
"Paolo Abeni" <pabeni@redhat.com>,
"Peter Zijlstra" <peterz@infradead.org>,
"Richard Weinberger" <richard@nod.at>,
"Russell King" <linux@armlinux.org.uk>,
"Theodore Ts'o" <tytso@mit.edu>,
"Thomas Bogendoerfer" <tsbogend@alpha.franken.de>,
"Thomas Gleixner" <tglx@linutronix.de>,
"Thomas Graf" <tgraf@suug.ch>,
"Ulf Hansson" <ulf.hansson@linaro.org>,
"Vignesh Raghavendra" <vigneshr@ti.com>,
"WANG Xuerui" <kernel@xen0n.name>,
"Will Deacon" <will@kernel.org>,
"Yury Norov" <yury.norov@gmail.com>,
dri-devel@lists.freedesktop.org, kasan-dev@googlegroups.com,
kernel-janitors@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
linux-block@vger.kernel.org, linux-crypto@vger.kernel.org,
linux-doc@vger.kernel.org, linux-fsdevel@vger.kernel.org,
linux-media@vger.kernel.org, linux-mips@vger.kernel.org,
linux-mm@kvack.org, linux-mmc@vger.kernel.org,
linux-mtd@lists.infradead.org, linux-nvme@lists.infradead.org,
linux-parisc@vger.kernel.org, linux-rdma@vger.kernel.org,
linux-s390@vger.kernel.org, linux-um@lists.infradead.org,
linux-usb@vger.kernel.org, linux-wireless@vger.kernel.org,
linuxppc-dev@lists.ozlabs.org, loongarch@lists.linux.dev,
netdev@vger.kernel.org, sparclinux@vger.kernel.org,
x86@kernel.org, "Jan Kara" <jack@suse.cz>
Subject: Re: [PATCH v4 2/6] treewide: use prandom_u32_max() when possible
Date: Fri, 7 Oct 2022 20:21:28 -0600 [thread overview]
Message-ID: <Y0DeqDC3EnA4b6ZB@zx2c4.com> (raw)
In-Reply-To: <202210071241.445289C5@keescook>
On Fri, Oct 07, 2022 at 03:47:44PM -0700, Kees Cook wrote:
> On Fri, Oct 07, 2022 at 12:01:03PM -0600, Jason A. Donenfeld wrote:
> > Rather than incurring a division or requesting too many random bytes for
> > the given range, use the prandom_u32_max() function, which only takes
> > the minimum required bytes from the RNG and avoids divisions.
>
> I actually meant splitting the by-hand stuff by subsystem, but nearly
> all of these can be done mechanically too, so it shouldn't be bad. Notes
> below...
Oh, cool, more coccinelle. You're basically giving me a class on these
recipes. Much appreciated.
> > [...]
> > diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
> > index 92bcc1768f0b..87203429f802 100644
> > --- a/arch/arm64/kernel/process.c
> > +++ b/arch/arm64/kernel/process.c
> > @@ -595,7 +595,7 @@ unsigned long __get_wchan(struct task_struct *p)
> > unsigned long arch_align_stack(unsigned long sp)
> > {
> > if (!(current->personality & ADDR_NO_RANDOMIZE) && randomize_va_space)
> > - sp -= get_random_int() & ~PAGE_MASK;
> > + sp -= prandom_u32_max(PAGE_SIZE);
> > return sp & ~0xf;
> > }
> >
>
> @mask@
> expression MASK;
> @@
>
> - (get_random_int() & ~(MASK))
> + prandom_u32_max(MASK)
Not quite! PAGE_MASK != PAGE_SIZE. In this case, things get a litttttle
more complicated where you can do:
get_random_int() & MASK == prandom_u32_max(MASK + 1)
*only if all the top bits of MASK are set* That is, if MASK one less
than a power of two. Or if MASK & (MASK + 1) == 0.
(If those top bits aren't set, you can technically do
prandom_u32_max(MASK >> n + 1) << n. That'd be a nice thing to work out.
But yeesh, maybe a bit much for the time being and probably a bit beyond
coccinelle.)
This case here, though, is a bit more special, where we can just rely on
an obvious given kernel identity. Namely, PAGE_MASK == ~(PAGE_SIZE - 1).
So ~PAGE_MASK == PAGE_SIZE - 1.
So get_random_int() & ~PAGE_MASK == prandom_u32_max(PAGE_SIZE - 1 + 1).
So get_random_int() & ~PAGE_MASK == prandom_u32_max(PAGE_SIZE).
And most importantly, this makes the code more readable, since everybody
knows what bounding by PAGE_SIZE means, where as what on earth is
happening with the &~PAGE_MASK thing. So it's a good change. I'll try to
teach coccinelle about that special case.
> > diff --git a/arch/loongarch/kernel/vdso.c b/arch/loongarch/kernel/vdso.c
> > index f32c38abd791..8c9826062652 100644
> > --- a/arch/loongarch/kernel/vdso.c
> > +++ b/arch/loongarch/kernel/vdso.c
> > @@ -78,7 +78,7 @@ static unsigned long vdso_base(void)
> > unsigned long base = STACK_TOP;
> >
> > if (current->flags & PF_RANDOMIZE) {
> > - base += get_random_int() & (VDSO_RANDOMIZE_SIZE - 1);
> > + base += prandom_u32_max(VDSO_RANDOMIZE_SIZE);
> > base = PAGE_ALIGN(base);
> > }
> >
>
> @minus_one@
> expression FULL;
> @@
>
> - (get_random_int() & ((FULL) - 1)
> + prandom_u32_max(FULL)
Ahh, well, okay, this is the example I mentioned above. Only works if
FULL is saturated. Any clever way to get coccinelle to prove that? Can
it look at the value of constants?
>
> > diff --git a/arch/parisc/kernel/vdso.c b/arch/parisc/kernel/vdso.c
> > index 63dc44c4c246..47e5960a2f96 100644
> > --- a/arch/parisc/kernel/vdso.c
> > +++ b/arch/parisc/kernel/vdso.c
> > @@ -75,7 +75,7 @@ int arch_setup_additional_pages(struct linux_binprm *bprm,
> >
> > map_base = mm->mmap_base;
> > if (current->flags & PF_RANDOMIZE)
> > - map_base -= (get_random_int() & 0x1f) * PAGE_SIZE;
> > + map_base -= prandom_u32_max(0x20) * PAGE_SIZE;
> >
> > vdso_text_start = get_unmapped_area(NULL, map_base, vdso_text_len, 0, 0);
> >
>
> These are more fun, but Coccinelle can still do them with a little
> Pythonic help:
>
> // Find a potential literal
> @literal_mask@
> expression LITERAL;
> identifier randfunc =~ "get_random_int|prandom_u32|get_random_u32";
> position p;
> @@
>
> (randfunc()@p & (LITERAL))
>
> // Add one to the literal.
> @script:python add_one@
> literal << literal_mask.LITERAL;
> RESULT;
> @@
>
> if literal.startswith('0x'):
> value = int(literal, 16) + 1
> coccinelle.RESULT = cocci.make_expr("0x%x" % (value))
> elif literal[0] in '123456789':
> value = int(literal, 10) + 1
> coccinelle.RESULT = cocci.make_expr("%d" % (value))
> else:
> print("I don't know how to handle: %s" % (literal))
>
> // Replace the literal mask with the calculated result.
> @plus_one@
> expression literal_mask.LITERAL;
> position literal_mask.p;
> expression add_one.RESULT;
> identifier FUNC;
> @@
>
> - (FUNC()@p & (LITERAL))
> + prandom_u32_max(RESULT)
Oh that's pretty cool. I can do the saturation check in python, since
`value` holds the parsed result. Neat.
> > diff --git a/fs/ext2/ialloc.c b/fs/ext2/ialloc.c
> > index 998dd2ac8008..f4944c4dee60 100644
> > --- a/fs/ext2/ialloc.c
> > +++ b/fs/ext2/ialloc.c
> > @@ -277,8 +277,7 @@ static int find_group_orlov(struct super_block *sb, struct inode *parent)
> > int best_ndir = inodes_per_group;
> > int best_group = -1;
> >
> > - group = prandom_u32();
> > - parent_group = (unsigned)group % ngroups;
> > + parent_group = prandom_u32_max(ngroups);
> > for (i = 0; i < ngroups; i++) {
> > group = (parent_group + i) % ngroups;
> > desc = ext2_get_group_desc (sb, group, NULL);
>
> Okay, that one is too much for me -- checking that group is never used
> after the assignment removal is likely possible, but beyond my cocci
> know-how. :)
Yea this is a tricky one, which I initially didn't do by hand, but Jan
seemed fine with it, and it's clear if you look at it. Trixy cocci
indeed.
> > diff --git a/lib/test_hexdump.c b/lib/test_hexdump.c
> > index 0927f44cd478..41a0321f641a 100644
> > --- a/lib/test_hexdump.c
> > +++ b/lib/test_hexdump.c
> > @@ -208,7 +208,7 @@ static void __init test_hexdump_overflow(size_t buflen, size_t len,
> > static void __init test_hexdump_overflow_set(size_t buflen, bool ascii)
> > {
> > unsigned int i = 0;
> > - int rs = (prandom_u32_max(2) + 1) * 16;
> > + int rs = prandom_u32_max(2) + 1 * 16;
> >
> > do {
> > int gs = 1 << i;
>
> This looks wrong. Cocci says:
>
> - int rs = (get_random_int() % 2 + 1) * 16;
> + int rs = (prandom_u32_max(2) + 1) * 16;
!! Nice catch.
Alright, I'll give this a try with more cocci. The big difficulty at the
moment is the power of 2 constant checking thing. If you have any
pointers on that, would be nice.
Thanks a bunch for the guidance.
Jason
_______________________________________________
linux-um mailing list
linux-um@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-um
WARNING: multiple messages have this Message-ID (diff)
From: "Jason A. Donenfeld" <Jason@zx2c4.com>
To: Kees Cook <keescook@chromium.org>
Cc: linux-wireless@vger.kernel.org,
"Ulf Hansson" <ulf.hansson@linaro.org>,
x86@kernel.org, "Jan Kara" <jack@suse.cz>,
"Vignesh Raghavendra" <vigneshr@ti.com>,
linux-doc@vger.kernel.org,
"Peter Zijlstra" <peterz@infradead.org>,
"Catalin Marinas" <catalin.marinas@arm.com>,
"Dave Hansen" <dave.hansen@linux.intel.com>,
kernel-janitors@vger.kernel.org, "KP Singh" <kpsingh@kernel.org>,
dri-devel@lists.freedesktop.org, patches@lists.linux.dev,
linux-mm@kvack.org, "Eric Dumazet" <edumazet@google.com>,
netdev@vger.kernel.org, linux-mtd@lists.infradead.org,
kasan-dev@googlegroups.com, "H . Peter Anvin" <hpa@zytor.com>,
"Andreas Noever" <andreas.noever@gmail.com>,
"WANG Xuerui" <kernel@xen0n.name>,
"Will Deacon" <will@kernel.org>, "Christoph Hellwig" <hch@lst.de>,
linux-s390@vger.kernel.org, sparclinux@vger.kernel.org,
"Mauro Carvalho Chehab" <mchehab@kernel.org>,
"Herbert Xu" <herbert@gondor.apana.org.au>,
"Daniel Borkmann" <daniel@iogearbox.net>,
"Jonathan Corbet" <corbet@lwn.net>,
linux-rdma@vger.kernel.org, "Helge Deller" <deller@gmx.de>,
"Huacai Chen" <chenhuacai@kernel.org>,
"Hugh Dickins" <hughd@google.com>,
"Russell King" <linux@armlinux.org.uk>,
"Jozsef Kadlecsik" <kadlec@netfilter.org>,
"Jason Gunthorpe" <jgg@ziepe.ca>,
"Dave Airlie" <airlied@redhat.com>,
"Paolo Abeni" <pabeni@redhat.com>,
"James E . J . Bottomley" <jejb@linux.ibm.com>,
"Pablo Neira Ayuso" <pablo@netfilter.org>,
linux-media@vger.kernel.org, "Marco Elver" <elver@google.com>,
"Yury Norov" <yury.norov@gmail.com>,
"Heiko Carstens" <hca@linux.ibm.com>,
linux-um@lists.infradead.org, linux-mips@vger.kernel.org,
linux-block@vger.kernel.org,
"Richard Weinberger" <richard@nod.at>,
"Borislav Petkov" <bp@alien8.de>,
linux-nvme@lists.infradead.org, loongarch@lists.linux.dev,
"Jakub Kicinski" <kuba@kernel.org>,
"Thomas Gleixner" <tglx@linutronix.de>,
"Andy Shevchenko" <andriy.shevchenko@linux.intel.com>,
"Johannes Berg" <johannes@sipsolutions.net>,
linux-arm-kernel@lists.infradead.org,
"Jens Axboe" <axboe@kernel.dk>,
linux-mmc@vger.kernel.org, "Thomas Bogendoerfer" <tsbogend@alp>
Subject: Re: [PATCH v4 2/6] treewide: use prandom_u32_max() when possible
Date: Fri, 7 Oct 2022 20:21:28 -0600 [thread overview]
Message-ID: <Y0DeqDC3EnA4b6ZB@zx2c4.com> (raw)
In-Reply-To: <202210071241.445289C5@keescook>
On Fri, Oct 07, 2022 at 03:47:44PM -0700, Kees Cook wrote:
> On Fri, Oct 07, 2022 at 12:01:03PM -0600, Jason A. Donenfeld wrote:
> > Rather than incurring a division or requesting too many random bytes for
> > the given range, use the prandom_u32_max() function, which only takes
> > the minimum required bytes from the RNG and avoids divisions.
>
> I actually meant splitting the by-hand stuff by subsystem, but nearly
> all of these can be done mechanically too, so it shouldn't be bad. Notes
> below...
Oh, cool, more coccinelle. You're basically giving me a class on these
recipes. Much appreciated.
> > [...]
> > diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
> > index 92bcc1768f0b..87203429f802 100644
> > --- a/arch/arm64/kernel/process.c
> > +++ b/arch/arm64/kernel/process.c
> > @@ -595,7 +595,7 @@ unsigned long __get_wchan(struct task_struct *p)
> > unsigned long arch_align_stack(unsigned long sp)
> > {
> > if (!(current->personality & ADDR_NO_RANDOMIZE) && randomize_va_space)
> > - sp -= get_random_int() & ~PAGE_MASK;
> > + sp -= prandom_u32_max(PAGE_SIZE);
> > return sp & ~0xf;
> > }
> >
>
> @mask@
> expression MASK;
> @@
>
> - (get_random_int() & ~(MASK))
> + prandom_u32_max(MASK)
Not quite! PAGE_MASK != PAGE_SIZE. In this case, things get a litttttle
more complicated where you can do:
get_random_int() & MASK == prandom_u32_max(MASK + 1)
*only if all the top bits of MASK are set* That is, if MASK one less
than a power of two. Or if MASK & (MASK + 1) == 0.
(If those top bits aren't set, you can technically do
prandom_u32_max(MASK >> n + 1) << n. That'd be a nice thing to work out.
But yeesh, maybe a bit much for the time being and probably a bit beyond
coccinelle.)
This case here, though, is a bit more special, where we can just rely on
an obvious given kernel identity. Namely, PAGE_MASK == ~(PAGE_SIZE - 1).
So ~PAGE_MASK == PAGE_SIZE - 1.
So get_random_int() & ~PAGE_MASK == prandom_u32_max(PAGE_SIZE - 1 + 1).
So get_random_int() & ~PAGE_MASK == prandom_u32_max(PAGE_SIZE).
And most importantly, this makes the code more readable, since everybody
knows what bounding by PAGE_SIZE means, where as what on earth is
happening with the &~PAGE_MASK thing. So it's a good change. I'll try to
teach coccinelle about that special case.
> > diff --git a/arch/loongarch/kernel/vdso.c b/arch/loongarch/kernel/vdso.c
> > index f32c38abd791..8c9826062652 100644
> > --- a/arch/loongarch/kernel/vdso.c
> > +++ b/arch/loongarch/kernel/vdso.c
> > @@ -78,7 +78,7 @@ static unsigned long vdso_base(void)
> > unsigned long base = STACK_TOP;
> >
> > if (current->flags & PF_RANDOMIZE) {
> > - base += get_random_int() & (VDSO_RANDOMIZE_SIZE - 1);
> > + base += prandom_u32_max(VDSO_RANDOMIZE_SIZE);
> > base = PAGE_ALIGN(base);
> > }
> >
>
> @minus_one@
> expression FULL;
> @@
>
> - (get_random_int() & ((FULL) - 1)
> + prandom_u32_max(FULL)
Ahh, well, okay, this is the example I mentioned above. Only works if
FULL is saturated. Any clever way to get coccinelle to prove that? Can
it look at the value of constants?
>
> > diff --git a/arch/parisc/kernel/vdso.c b/arch/parisc/kernel/vdso.c
> > index 63dc44c4c246..47e5960a2f96 100644
> > --- a/arch/parisc/kernel/vdso.c
> > +++ b/arch/parisc/kernel/vdso.c
> > @@ -75,7 +75,7 @@ int arch_setup_additional_pages(struct linux_binprm *bprm,
> >
> > map_base = mm->mmap_base;
> > if (current->flags & PF_RANDOMIZE)
> > - map_base -= (get_random_int() & 0x1f) * PAGE_SIZE;
> > + map_base -= prandom_u32_max(0x20) * PAGE_SIZE;
> >
> > vdso_text_start = get_unmapped_area(NULL, map_base, vdso_text_len, 0, 0);
> >
>
> These are more fun, but Coccinelle can still do them with a little
> Pythonic help:
>
> // Find a potential literal
> @literal_mask@
> expression LITERAL;
> identifier randfunc =~ "get_random_int|prandom_u32|get_random_u32";
> position p;
> @@
>
> (randfunc()@p & (LITERAL))
>
> // Add one to the literal.
> @script:python add_one@
> literal << literal_mask.LITERAL;
> RESULT;
> @@
>
> if literal.startswith('0x'):
> value = int(literal, 16) + 1
> coccinelle.RESULT = cocci.make_expr("0x%x" % (value))
> elif literal[0] in '123456789':
> value = int(literal, 10) + 1
> coccinelle.RESULT = cocci.make_expr("%d" % (value))
> else:
> print("I don't know how to handle: %s" % (literal))
>
> // Replace the literal mask with the calculated result.
> @plus_one@
> expression literal_mask.LITERAL;
> position literal_mask.p;
> expression add_one.RESULT;
> identifier FUNC;
> @@
>
> - (FUNC()@p & (LITERAL))
> + prandom_u32_max(RESULT)
Oh that's pretty cool. I can do the saturation check in python, since
`value` holds the parsed result. Neat.
> > diff --git a/fs/ext2/ialloc.c b/fs/ext2/ialloc.c
> > index 998dd2ac8008..f4944c4dee60 100644
> > --- a/fs/ext2/ialloc.c
> > +++ b/fs/ext2/ialloc.c
> > @@ -277,8 +277,7 @@ static int find_group_orlov(struct super_block *sb, struct inode *parent)
> > int best_ndir = inodes_per_group;
> > int best_group = -1;
> >
> > - group = prandom_u32();
> > - parent_group = (unsigned)group % ngroups;
> > + parent_group = prandom_u32_max(ngroups);
> > for (i = 0; i < ngroups; i++) {
> > group = (parent_group + i) % ngroups;
> > desc = ext2_get_group_desc (sb, group, NULL);
>
> Okay, that one is too much for me -- checking that group is never used
> after the assignment removal is likely possible, but beyond my cocci
> know-how. :)
Yea this is a tricky one, which I initially didn't do by hand, but Jan
seemed fine with it, and it's clear if you look at it. Trixy cocci
indeed.
> > diff --git a/lib/test_hexdump.c b/lib/test_hexdump.c
> > index 0927f44cd478..41a0321f641a 100644
> > --- a/lib/test_hexdump.c
> > +++ b/lib/test_hexdump.c
> > @@ -208,7 +208,7 @@ static void __init test_hexdump_overflow(size_t buflen, size_t len,
> > static void __init test_hexdump_overflow_set(size_t buflen, bool ascii)
> > {
> > unsigned int i = 0;
> > - int rs = (prandom_u32_max(2) + 1) * 16;
> > + int rs = prandom_u32_max(2) + 1 * 16;
> >
> > do {
> > int gs = 1 << i;
>
> This looks wrong. Cocci says:
>
> - int rs = (get_random_int() % 2 + 1) * 16;
> + int rs = (prandom_u32_max(2) + 1) * 16;
!! Nice catch.
Alright, I'll give this a try with more cocci. The big difficulty at the
moment is the power of 2 constant checking thing. If you have any
pointers on that, would be nice.
Thanks a bunch for the guidance.
Jason
WARNING: multiple messages have this Message-ID (diff)
From: "Jason A. Donenfeld" <Jason@zx2c4.com>
To: Kees Cook <keescook@chromium.org>
Cc: linux-wireless@vger.kernel.org,
"Ulf Hansson" <ulf.hansson@linaro.org>,
x86@kernel.org, "Jan Kara" <jack@suse.cz>,
"Vignesh Raghavendra" <vigneshr@ti.com>,
linux-doc@vger.kernel.org,
"Peter Zijlstra" <peterz@infradead.org>,
"Catalin Marinas" <catalin.marinas@arm.com>,
"Dave Hansen" <dave.hansen@linux.intel.com>,
kernel-janitors@vger.kernel.org, "KP Singh" <kpsingh@kernel.org>,
dri-devel@lists.freedesktop.org, patches@lists.linux.dev,
linux-mm@kvack.org, "Eric Dumazet" <edumazet@google.com>,
netdev@vger.kernel.org, linux-mtd@lists.infradead.org,
kasan-dev@googlegroups.com, "H . Peter Anvin" <hpa@zytor.com>,
"Andreas Noever" <andreas.noever@gmail.com>,
"WANG Xuerui" <kernel@xen0n.name>,
"Will Deacon" <will@kernel.org>, "Christoph Hellwig" <hch@lst.de>,
linux-s390@vger.kernel.org, sparclinux@vger.kernel.org,
"Mauro Carvalho Chehab" <mchehab@kernel.org>,
"Herbert Xu" <herbert@gondor.apana.org.au>,
"Daniel Borkmann" <daniel@iogearbox.net>,
"Jonathan Corbet" <corbet@lwn.net>,
linux-rdma@vger.kernel.org,
"Michael Ellerman" <mpe@ellerman.id.au>,
"Helge Deller" <deller@gmx.de>,
"Huacai Chen" <chenhuacai@kernel.org>,
"Hugh Dickins" <hughd@google.com>,
"Russell King" <linux@armlinux.org.uk>,
"Christophe Leroy" <christophe.leroy@csgroup.eu>,
"Jozsef Kadlecsik" <kadlec@netfilter.org>,
"Jason Gunthorpe" <jgg@ziepe.ca>,
"Dave Airlie" <airlied@redhat.com>,
"Paolo Abeni" <pabeni@redhat.com>,
"James E . J . Bottomley" <jejb@linux.ibm.com>,
"Pablo Neira Ayuso" <pablo@netfilter.org>,
linux-media@vger.kernel.org, "Marco Elver" <elver@google.com>,
"Yury Norov" <yury.norov@gmail.com>,
"Heiko Carstens" <hca@linux.ibm.com>,
linux-um@lists.infradead.org, linux-mips@vger.kernel.org,
linux-block@vger.kernel.org,
"Richard Weinberger" <richard@nod.at>,
"Borislav Petkov" <bp@alien8.de>,
linux-nvme@lists.infradead.org, loongarch@lists.linux.dev,
"Jakub Kicinski" <kuba@kernel.org>,
"Thomas Gleixner" <tglx@linutronix.de>,
"Andy Shevchenko" <andriy.shevchenko@linux.intel.com>,
"Johannes Berg" <johannes@sipsolutions.net>,
linux-arm-kernel@lists.infradead.org,
"Jens Axboe" <axboe@kernel.dk>,
linux-mmc@vger.kernel.org,
"Thomas Bogendoerfer" <tsbogend@alpha.franken.de>,
"Theodore Ts'o" <tytso@mit.edu>,
linux-parisc@vger.kernel.org,
"Greg Kroah-Hartman" <gregkh@linuxfoundation.org>,
linux-usb@vger.kernel.org, "Florian Westphal" <fw@strlen.de>,
linux-kernel@vger.kernel.org,
"Christoph Böhmwalder" <christoph.boehmwalder@linbit.com>,
linux-crypto@vger.kernel.org, "Jan Kara" <jack@suse.com>,
"Thomas Graf" <tgraf@suug.ch>,
linux-fsdevel@vger.kernel.org,
"Andrew Morton" <akpm@linux-foundation.org>,
linuxppc-dev@lists.ozlabs.org,
"David S . Miller" <davem@davemloft.net>
Subject: Re: [PATCH v4 2/6] treewide: use prandom_u32_max() when possible
Date: Fri, 7 Oct 2022 20:21:28 -0600 [thread overview]
Message-ID: <Y0DeqDC3EnA4b6ZB@zx2c4.com> (raw)
In-Reply-To: <202210071241.445289C5@keescook>
On Fri, Oct 07, 2022 at 03:47:44PM -0700, Kees Cook wrote:
> On Fri, Oct 07, 2022 at 12:01:03PM -0600, Jason A. Donenfeld wrote:
> > Rather than incurring a division or requesting too many random bytes for
> > the given range, use the prandom_u32_max() function, which only takes
> > the minimum required bytes from the RNG and avoids divisions.
>
> I actually meant splitting the by-hand stuff by subsystem, but nearly
> all of these can be done mechanically too, so it shouldn't be bad. Notes
> below...
Oh, cool, more coccinelle. You're basically giving me a class on these
recipes. Much appreciated.
> > [...]
> > diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
> > index 92bcc1768f0b..87203429f802 100644
> > --- a/arch/arm64/kernel/process.c
> > +++ b/arch/arm64/kernel/process.c
> > @@ -595,7 +595,7 @@ unsigned long __get_wchan(struct task_struct *p)
> > unsigned long arch_align_stack(unsigned long sp)
> > {
> > if (!(current->personality & ADDR_NO_RANDOMIZE) && randomize_va_space)
> > - sp -= get_random_int() & ~PAGE_MASK;
> > + sp -= prandom_u32_max(PAGE_SIZE);
> > return sp & ~0xf;
> > }
> >
>
> @mask@
> expression MASK;
> @@
>
> - (get_random_int() & ~(MASK))
> + prandom_u32_max(MASK)
Not quite! PAGE_MASK != PAGE_SIZE. In this case, things get a litttttle
more complicated where you can do:
get_random_int() & MASK == prandom_u32_max(MASK + 1)
*only if all the top bits of MASK are set* That is, if MASK one less
than a power of two. Or if MASK & (MASK + 1) == 0.
(If those top bits aren't set, you can technically do
prandom_u32_max(MASK >> n + 1) << n. That'd be a nice thing to work out.
But yeesh, maybe a bit much for the time being and probably a bit beyond
coccinelle.)
This case here, though, is a bit more special, where we can just rely on
an obvious given kernel identity. Namely, PAGE_MASK == ~(PAGE_SIZE - 1).
So ~PAGE_MASK == PAGE_SIZE - 1.
So get_random_int() & ~PAGE_MASK == prandom_u32_max(PAGE_SIZE - 1 + 1).
So get_random_int() & ~PAGE_MASK == prandom_u32_max(PAGE_SIZE).
And most importantly, this makes the code more readable, since everybody
knows what bounding by PAGE_SIZE means, where as what on earth is
happening with the &~PAGE_MASK thing. So it's a good change. I'll try to
teach coccinelle about that special case.
> > diff --git a/arch/loongarch/kernel/vdso.c b/arch/loongarch/kernel/vdso.c
> > index f32c38abd791..8c9826062652 100644
> > --- a/arch/loongarch/kernel/vdso.c
> > +++ b/arch/loongarch/kernel/vdso.c
> > @@ -78,7 +78,7 @@ static unsigned long vdso_base(void)
> > unsigned long base = STACK_TOP;
> >
> > if (current->flags & PF_RANDOMIZE) {
> > - base += get_random_int() & (VDSO_RANDOMIZE_SIZE - 1);
> > + base += prandom_u32_max(VDSO_RANDOMIZE_SIZE);
> > base = PAGE_ALIGN(base);
> > }
> >
>
> @minus_one@
> expression FULL;
> @@
>
> - (get_random_int() & ((FULL) - 1)
> + prandom_u32_max(FULL)
Ahh, well, okay, this is the example I mentioned above. Only works if
FULL is saturated. Any clever way to get coccinelle to prove that? Can
it look at the value of constants?
>
> > diff --git a/arch/parisc/kernel/vdso.c b/arch/parisc/kernel/vdso.c
> > index 63dc44c4c246..47e5960a2f96 100644
> > --- a/arch/parisc/kernel/vdso.c
> > +++ b/arch/parisc/kernel/vdso.c
> > @@ -75,7 +75,7 @@ int arch_setup_additional_pages(struct linux_binprm *bprm,
> >
> > map_base = mm->mmap_base;
> > if (current->flags & PF_RANDOMIZE)
> > - map_base -= (get_random_int() & 0x1f) * PAGE_SIZE;
> > + map_base -= prandom_u32_max(0x20) * PAGE_SIZE;
> >
> > vdso_text_start = get_unmapped_area(NULL, map_base, vdso_text_len, 0, 0);
> >
>
> These are more fun, but Coccinelle can still do them with a little
> Pythonic help:
>
> // Find a potential literal
> @literal_mask@
> expression LITERAL;
> identifier randfunc =~ "get_random_int|prandom_u32|get_random_u32";
> position p;
> @@
>
> (randfunc()@p & (LITERAL))
>
> // Add one to the literal.
> @script:python add_one@
> literal << literal_mask.LITERAL;
> RESULT;
> @@
>
> if literal.startswith('0x'):
> value = int(literal, 16) + 1
> coccinelle.RESULT = cocci.make_expr("0x%x" % (value))
> elif literal[0] in '123456789':
> value = int(literal, 10) + 1
> coccinelle.RESULT = cocci.make_expr("%d" % (value))
> else:
> print("I don't know how to handle: %s" % (literal))
>
> // Replace the literal mask with the calculated result.
> @plus_one@
> expression literal_mask.LITERAL;
> position literal_mask.p;
> expression add_one.RESULT;
> identifier FUNC;
> @@
>
> - (FUNC()@p & (LITERAL))
> + prandom_u32_max(RESULT)
Oh that's pretty cool. I can do the saturation check in python, since
`value` holds the parsed result. Neat.
> > diff --git a/fs/ext2/ialloc.c b/fs/ext2/ialloc.c
> > index 998dd2ac8008..f4944c4dee60 100644
> > --- a/fs/ext2/ialloc.c
> > +++ b/fs/ext2/ialloc.c
> > @@ -277,8 +277,7 @@ static int find_group_orlov(struct super_block *sb, struct inode *parent)
> > int best_ndir = inodes_per_group;
> > int best_group = -1;
> >
> > - group = prandom_u32();
> > - parent_group = (unsigned)group % ngroups;
> > + parent_group = prandom_u32_max(ngroups);
> > for (i = 0; i < ngroups; i++) {
> > group = (parent_group + i) % ngroups;
> > desc = ext2_get_group_desc (sb, group, NULL);
>
> Okay, that one is too much for me -- checking that group is never used
> after the assignment removal is likely possible, but beyond my cocci
> know-how. :)
Yea this is a tricky one, which I initially didn't do by hand, but Jan
seemed fine with it, and it's clear if you look at it. Trixy cocci
indeed.
> > diff --git a/lib/test_hexdump.c b/lib/test_hexdump.c
> > index 0927f44cd478..41a0321f641a 100644
> > --- a/lib/test_hexdump.c
> > +++ b/lib/test_hexdump.c
> > @@ -208,7 +208,7 @@ static void __init test_hexdump_overflow(size_t buflen, size_t len,
> > static void __init test_hexdump_overflow_set(size_t buflen, bool ascii)
> > {
> > unsigned int i = 0;
> > - int rs = (prandom_u32_max(2) + 1) * 16;
> > + int rs = prandom_u32_max(2) + 1 * 16;
> >
> > do {
> > int gs = 1 << i;
>
> This looks wrong. Cocci says:
>
> - int rs = (get_random_int() % 2 + 1) * 16;
> + int rs = (prandom_u32_max(2) + 1) * 16;
!! Nice catch.
Alright, I'll give this a try with more cocci. The big difficulty at the
moment is the power of 2 constant checking thing. If you have any
pointers on that, would be nice.
Thanks a bunch for the guidance.
Jason
next prev parent reply other threads:[~2022-10-08 2:21 UTC|newest]
Thread overview: 112+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-10-07 18:01 [PATCH v4 0/6] treewide cleanup of random integer usage Jason A. Donenfeld
2022-10-07 18:01 ` Jason A. Donenfeld
2022-10-07 18:01 ` Jason A. Donenfeld
2022-10-07 18:01 ` Jason A. Donenfeld
2022-10-07 18:01 ` Jason A. Donenfeld
2022-10-07 18:01 ` [PATCH v4 1/6] treewide: use prandom_u32_max() when possible, mechanically Jason A. Donenfeld
2022-10-07 18:01 ` Jason A. Donenfeld
2022-10-07 18:01 ` Jason A. Donenfeld
2022-10-07 18:01 ` Jason A. Donenfeld
2022-10-07 18:01 ` Jason A. Donenfeld
2022-10-07 21:14 ` Darrick J. Wong
2022-10-07 21:14 ` Darrick J. Wong
2022-10-07 21:14 ` Darrick J. Wong
2022-10-07 21:14 ` Darrick J. Wong
2022-10-07 21:14 ` Darrick J. Wong
2022-10-07 18:01 ` [PATCH v4 2/6] treewide: use prandom_u32_max() when possible Jason A. Donenfeld
2022-10-07 18:01 ` Jason A. Donenfeld
2022-10-07 18:01 ` Jason A. Donenfeld
2022-10-07 18:01 ` Jason A. Donenfeld
2022-10-07 21:17 ` Darrick J. Wong
2022-10-07 21:17 ` Darrick J. Wong
2022-10-07 21:17 ` Darrick J. Wong
2022-10-07 21:17 ` Darrick J. Wong
2022-10-07 21:17 ` Darrick J. Wong
2022-10-08 1:28 ` Jason A. Donenfeld
2022-10-08 1:28 ` Jason A. Donenfeld
2022-10-08 1:28 ` Jason A. Donenfeld
2022-10-08 1:28 ` Jason A. Donenfeld
2022-10-08 1:28 ` Jason A. Donenfeld
2022-10-07 22:47 ` Kees Cook
2022-10-07 22:47 ` Kees Cook
2022-10-07 22:47 ` Kees Cook
2022-10-07 22:47 ` Kees Cook
2022-10-08 2:21 ` Jason A. Donenfeld [this message]
2022-10-08 2:21 ` Jason A. Donenfeld
2022-10-08 2:21 ` Jason A. Donenfeld
2022-10-08 2:21 ` Jason A. Donenfeld
2022-10-08 2:21 ` Jason A. Donenfeld
2022-10-08 3:21 ` Jason A. Donenfeld
2022-10-08 3:21 ` Jason A. Donenfeld
2022-10-08 3:21 ` Jason A. Donenfeld
2022-10-08 3:21 ` Jason A. Donenfeld
2022-10-08 3:21 ` Jason A. Donenfeld
2022-10-08 22:08 ` David Laight
2022-10-08 22:08 ` David Laight
2022-10-08 22:08 ` David Laight
2022-10-08 22:08 ` David Laight
2022-10-08 22:08 ` David Laight
2022-10-08 22:19 ` Jason A. Donenfeld
2022-10-08 22:19 ` Jason A. Donenfeld
2022-10-08 22:19 ` Jason A. Donenfeld
2022-10-08 22:19 ` Jason A. Donenfeld
2022-10-08 22:19 ` Jason A. Donenfeld
2022-10-07 18:01 ` [PATCH v4 3/6] treewide: use get_random_{u8,u16}() " Jason A. Donenfeld
2022-10-07 18:01 ` Jason A. Donenfeld
2022-10-07 18:01 ` Jason A. Donenfeld
2022-10-07 18:01 ` Jason A. Donenfeld
2022-10-07 18:01 ` [PATCH v4 4/6] treewide: use get_random_u32() " Jason A. Donenfeld
2022-10-07 18:01 ` Jason A. Donenfeld
2022-10-07 18:01 ` Jason A. Donenfeld
2022-10-07 18:01 ` Jason A. Donenfeld
2022-10-07 20:34 ` Rolf Eike Beer
2022-10-07 20:34 ` Rolf Eike Beer
2022-10-07 20:34 ` Rolf Eike Beer
2022-10-07 20:34 ` Rolf Eike Beer
2022-10-08 1:40 ` Jason A. Donenfeld
2022-10-08 1:40 ` Jason A. Donenfeld
2022-10-08 1:40 ` Jason A. Donenfeld
2022-10-08 1:40 ` Jason A. Donenfeld
2022-10-08 1:40 ` Jason A. Donenfeld
2022-10-07 21:19 ` Darrick J. Wong
2022-10-07 21:19 ` Darrick J. Wong
2022-10-07 21:19 ` Darrick J. Wong
2022-10-07 21:19 ` Darrick J. Wong
2022-10-07 21:19 ` Darrick J. Wong
2022-10-08 22:18 ` David Laight
2022-10-08 22:18 ` David Laight
2022-10-08 22:18 ` David Laight
2022-10-08 22:18 ` David Laight
2022-10-08 22:18 ` David Laight
2022-10-08 22:37 ` Jason A. Donenfeld
2022-10-08 22:37 ` Jason A. Donenfeld
2022-10-08 22:37 ` Jason A. Donenfeld
2022-10-08 22:37 ` Jason A. Donenfeld
2022-10-08 22:37 ` Jason A. Donenfeld
2022-10-09 0:26 ` Jason A. Donenfeld
2022-10-09 0:26 ` Jason A. Donenfeld
2022-10-09 0:26 ` Jason A. Donenfeld
2022-10-09 0:26 ` Jason A. Donenfeld
2022-10-09 0:26 ` Jason A. Donenfeld
2022-10-07 18:01 ` [PATCH v4 5/6] treewide: use get_random_bytes " Jason A. Donenfeld
2022-10-07 18:01 ` Jason A. Donenfeld
2022-10-07 18:01 ` Jason A. Donenfeld
2022-10-07 18:01 ` Jason A. Donenfeld
2022-10-07 18:01 ` [PATCH v4 6/6] prandom: remove unused functions Jason A. Donenfeld
2022-10-07 18:01 ` Jason A. Donenfeld
2022-10-07 18:01 ` Jason A. Donenfeld
2022-10-07 18:01 ` Jason A. Donenfeld
2022-10-07 18:01 ` Jason A. Donenfeld
-- strict thread matches above, loose matches on Subject: below --
2022-10-08 3:50 [PATCH v4 2/6] treewide: use prandom_u32_max() when possible Kees Cook
2022-10-08 3:50 ` Kees Cook
2022-10-08 3:50 ` Kees Cook
2022-10-08 7:33 ` Julia Lawall
2022-10-08 7:33 ` Julia Lawall
2022-10-08 7:33 ` Julia Lawall
2022-10-08 7:33 ` Julia Lawall
2022-10-08 7:33 ` Julia Lawall
2022-10-08 18:16 ` Andy Shevchenko
2022-10-08 18:16 ` Andy Shevchenko
2022-10-08 18:16 ` Andy Shevchenko
2022-10-08 18:16 ` Andy Shevchenko
2022-10-08 18:16 ` Andy Shevchenko
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=Y0DeqDC3EnA4b6ZB@zx2c4.com \
--to=jason@zx2c4.com \
--cc=airlied@redhat.com \
--cc=akpm@linux-foundation.org \
--cc=andreas.noever@gmail.com \
--cc=andriy.shevchenko@linux.intel.com \
--cc=axboe@kernel.dk \
--cc=bp@alien8.de \
--cc=catalin.marinas@arm.com \
--cc=chenhuacai@kernel.org \
--cc=christoph.boehmwalder@linbit.com \
--cc=christophe.leroy@csgroup.eu \
--cc=corbet@lwn.net \
--cc=daniel@iogearbox.net \
--cc=dave.hansen@linux.intel.com \
--cc=davem@davemloft.net \
--cc=deller@gmx.de \
--cc=dri-devel@lists.freedesktop.org \
--cc=edumazet@google.com \
--cc=elver@google.com \
--cc=fw@strlen.de \
--cc=gregkh@linuxfoundation.org \
--cc=hca@linux.ibm.com \
--cc=hch@lst.de \
--cc=herbert@gondor.apana.org.au \
--cc=hpa@zytor.com \
--cc=hughd@google.com \
--cc=jack@suse.com \
--cc=jack@suse.cz \
--cc=jejb@linux.ibm.com \
--cc=jgg@ziepe.ca \
--cc=johannes@sipsolutions.net \
--cc=kadlec@netfilter.org \
--cc=kasan-dev@googlegroups.com \
--cc=keescook@chromium.org \
--cc=kernel-janitors@vger.kernel.org \
--cc=kernel@xen0n.name \
--cc=kpsingh@kernel.org \
--cc=kuba@kernel.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-block@vger.kernel.org \
--cc=linux-crypto@vger.kernel.org \
--cc=linux-doc@vger.kernel.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-media@vger.kernel.org \
--cc=linux-mips@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=linux-mmc@vger.kernel.org \
--cc=linux-mtd@lists.infradead.org \
--cc=linux-nvme@lists.infradead.org \
--cc=linux-parisc@vger.kernel.org \
--cc=linux-rdma@vger.kernel.org \
--cc=linux-s390@vger.kernel.org \
--cc=linux-um@lists.infradead.org \
--cc=linux-usb@vger.kernel.org \
--cc=linux-wireless@vger.kernel.org \
--cc=linux@armlinux.org.uk \
--cc=linuxppc-dev@lists.ozlabs.org \
--cc=loongarch@lists.linux.dev \
--cc=mchehab@kernel.org \
--cc=mpe@ellerman.id.au \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=pablo@netfilter.org \
--cc=patches@lists.linux.dev \
--cc=peterz@infradead.org \
--cc=richard@nod.at \
--cc=sparclinux@vger.kernel.org \
--cc=tglx@linutronix.de \
--cc=tgraf@suug.ch \
--cc=tsbogend@alpha.franken.de \
--cc=tytso@mit.edu \
--cc=ulf.hansson@linaro.org \
--cc=vigneshr@ti.com \
--cc=will@kernel.org \
--cc=x86@kernel.org \
--cc=yury.norov@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.