2018-02-15 13:29:16

by Thomas Gleixner

[permalink] [raw]
Subject: [PATCH] posix-timers: Protect posix clock array access against speculation

The (clock) id argument of clockid_to_kclock() comes straight from user
space via various syscalls and is used as index into the posix_clocks
array.

Protect it against spectre v1 array out of bounds speculation.

Signed-off-by: Thomas Gleixner <[email protected]>
Cc: [email protected]
---
kernel/time/posix-timers.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)

--- a/kernel/time/posix-timers.c
+++ b/kernel/time/posix-timers.c
@@ -50,6 +50,7 @@
#include <linux/export.h>
#include <linux/hashtable.h>
#include <linux/compat.h>
+#include <linux/nospec.h>

#include "timekeeping.h"
#include "posix-timers.h"
@@ -1346,11 +1347,14 @@ static const struct k_clock * const posi

static const struct k_clock *clockid_to_kclock(const clockid_t id)
{
+ clockid_t idx = id;
+
if (id < 0)
return (id & CLOCKFD_MASK) == CLOCKFD ?
&clock_posix_dynamic : &clock_posix_cpu;

if (id >= ARRAY_SIZE(posix_clocks) || !posix_clocks[id])
return NULL;
- return posix_clocks[id];
+
+ return posix_clocks[array_index_nospec(idx, ARRAY_SIZE(posix_clocks))];
}


2018-02-15 14:07:05

by Rasmus Villemoes

[permalink] [raw]
Subject: Re: [PATCH] posix-timers: Protect posix clock array access against speculation

On 2018-02-15 14:27, Thomas Gleixner wrote:
> The (clock) id argument of clockid_to_kclock() comes straight from user
> space via various syscalls and is used as index into the posix_clocks
> array.
>
> Protect it against spectre v1 array out of bounds speculation.
>
> Signed-off-by: Thomas Gleixner <[email protected]>
> Cc: [email protected]
> ---
> kernel/time/posix-timers.c | 6 +++++-
> 1 file changed, 5 insertions(+), 1 deletion(-)
>
> --- a/kernel/time/posix-timers.c
> +++ b/kernel/time/posix-timers.c
> @@ -50,6 +50,7 @@
> #include <linux/export.h>
> #include <linux/hashtable.h>
> #include <linux/compat.h>
> +#include <linux/nospec.h>
>
> #include "timekeeping.h"
> #include "posix-timers.h"
> @@ -1346,11 +1347,14 @@ static const struct k_clock * const posi
>
> static const struct k_clock *clockid_to_kclock(const clockid_t id)
> {
> + clockid_t idx = id;
> +
> if (id < 0)
> return (id & CLOCKFD_MASK) == CLOCKFD ?
> &clock_posix_dynamic : &clock_posix_cpu;
>
> if (id >= ARRAY_SIZE(posix_clocks) || !posix_clocks[id])
> return NULL;
> - return posix_clocks[id];
> +
> + return posix_clocks[array_index_nospec(idx, ARRAY_SIZE(posix_clocks))];
> }
>

Stupid questions from someone trying to learn what the rules for when
and how to apply these _nospec macros:

(1) why introduce the idx var? There's no assignment to it other than
the initialization. Is it some magic in array_index_nospec that prevents
the use of a const-qualified expression?

(2) The line "if (id >= ARRAY_SIZE(posix_clocks) || !posix_clocks[id])"
still seems to allow speculatively accessing posix_clocks[id]. Is that
ok, and even if so, wouldn't it be cleaner to elide the
!posix_clocks[id] check and just return the NULL safely fetched from the
array in the following line?

Rasmus

2018-02-15 14:47:17

by Dan Williams

[permalink] [raw]
Subject: Re: [PATCH] posix-timers: Protect posix clock array access against speculation

On Thu, Feb 15, 2018 at 6:05 AM, Rasmus Villemoes
<[email protected]> wrote:
> On 2018-02-15 14:27, Thomas Gleixner wrote:
>> The (clock) id argument of clockid_to_kclock() comes straight from user
>> space via various syscalls and is used as index into the posix_clocks
>> array.
>>
>> Protect it against spectre v1 array out of bounds speculation.
>>
>> Signed-off-by: Thomas Gleixner <[email protected]>
>> Cc: [email protected]
>> ---
>> kernel/time/posix-timers.c | 6 +++++-
>> 1 file changed, 5 insertions(+), 1 deletion(-)
>>
>> --- a/kernel/time/posix-timers.c
>> +++ b/kernel/time/posix-timers.c
>> @@ -50,6 +50,7 @@
>> #include <linux/export.h>
>> #include <linux/hashtable.h>
>> #include <linux/compat.h>
>> +#include <linux/nospec.h>
>>
>> #include "timekeeping.h"
>> #include "posix-timers.h"
>> @@ -1346,11 +1347,14 @@ static const struct k_clock * const posi
>>
>> static const struct k_clock *clockid_to_kclock(const clockid_t id)
>> {
>> + clockid_t idx = id;
>> +
>> if (id < 0)
>> return (id & CLOCKFD_MASK) == CLOCKFD ?
>> &clock_posix_dynamic : &clock_posix_cpu;
>>
>> if (id >= ARRAY_SIZE(posix_clocks) || !posix_clocks[id])
>> return NULL;
>> - return posix_clocks[id];
>> +
>> + return posix_clocks[array_index_nospec(idx, ARRAY_SIZE(posix_clocks))];
>> }
>>
>
> Stupid questions from someone trying to learn what the rules for when
> and how to apply these _nospec macros:
>
> (1) why introduce the idx var? There's no assignment to it other than
> the initialization. Is it some magic in array_index_nospec that prevents
> the use of a const-qualified expression?

It does currently, but perhaps it can be fixed.

>
> (2) The line "if (id >= ARRAY_SIZE(posix_clocks) || !posix_clocks[id])"
> still seems to allow speculatively accessing posix_clocks[id]. Is that
> ok, and even if so, wouldn't it be cleaner to elide the
> !posix_clocks[id] check and just return the NULL safely fetched from the
> array in the following line?

Right, this looks broken. I would expect:

if (id >= ARRAY_SIZE(posix_clocks))
return NULL;
idx = array_index_nospec(idx, ARRAY_SIZE(posix_clocks));
if (!posix_clocks[idx])
return NULL;
return posix_clocks[idx];

2018-02-15 14:56:32

by Thomas Gleixner

[permalink] [raw]
Subject: Re: [PATCH] posix-timers: Protect posix clock array access against speculation

On Thu, 15 Feb 2018, Dan Williams wrote:
> On Thu, Feb 15, 2018 at 6:05 AM, Rasmus Villemoes
> <[email protected]> wrote:
> > (2) The line "if (id >= ARRAY_SIZE(posix_clocks) || !posix_clocks[id])"
> > still seems to allow speculatively accessing posix_clocks[id]. Is that
> > ok, and even if so, wouldn't it be cleaner to elide the
> > !posix_clocks[id] check and just return the NULL safely fetched from the
> > array in the following line?
>
> Right, this looks broken. I would expect:

Indeed. Missed that.

> if (id >= ARRAY_SIZE(posix_clocks))
> return NULL;
> idx = array_index_nospec(idx, ARRAY_SIZE(posix_clocks));
> if (!posix_clocks[idx])
> return NULL;
> return posix_clocks[idx];

The !posix_clocks[idx] check is pointless and always was.

if (id >= ARRAY_SIZE(posix_clocks))
return NULL;

idx = array_index_nospec(idx, ARRAY_SIZE(posix_clocks));
return posix_clocks[idx];

is sufficient. It returns NULL for !posix_clocks[idx] anyway.

Thanks,

tglx

2018-02-15 16:24:18

by Thomas Gleixner

[permalink] [raw]
Subject: [PATCH V2] posix-timers: Protect posix clock array access against speculation

The clockid argument of clockid_to_kclock() comes straight from user space
via various syscalls and is used as index into the posix_clocks array.

Protect it against spectre v1 array out of bounds speculation. Remove the
redundant check for !posix_clock[id] as this is another source for
speculation and does not provide any advantage over the return
posix_clock[id] path which returns NULL in that case anyway.

Signed-off-by: Thomas Gleixner <[email protected]>
Cc: [email protected]
---

V2: Remove the redundant !posix_clocks[id] check.

kernel/time/posix-timers.c | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)

--- a/kernel/time/posix-timers.c
+++ b/kernel/time/posix-timers.c
@@ -50,6 +50,7 @@
#include <linux/export.h>
#include <linux/hashtable.h>
#include <linux/compat.h>
+#include <linux/nospec.h>

#include "timekeeping.h"
#include "posix-timers.h"
@@ -1346,11 +1347,15 @@ static const struct k_clock * const posi

static const struct k_clock *clockid_to_kclock(const clockid_t id)
{
- if (id < 0)
+ clockid_t idx = id;
+
+ if (id < 0) {
return (id & CLOCKFD_MASK) == CLOCKFD ?
&clock_posix_dynamic : &clock_posix_cpu;
+ }

- if (id >= ARRAY_SIZE(posix_clocks) || !posix_clocks[id])
+ if (id >= ARRAY_SIZE(posix_clocks))
return NULL;
- return posix_clocks[id];
+
+ return posix_clocks[array_index_nospec(idx, ARRAY_SIZE(posix_clocks))];
}

2018-02-16 10:26:02

by Peter Zijlstra

[permalink] [raw]
Subject: Re: [PATCH V2] posix-timers: Protect posix clock array access against speculation

On Thu, Feb 15, 2018 at 05:21:55PM +0100, Thomas Gleixner wrote:
> The clockid argument of clockid_to_kclock() comes straight from user space
> via various syscalls and is used as index into the posix_clocks array.
>
> Protect it against spectre v1 array out of bounds speculation. Remove the
> redundant check for !posix_clock[id] as this is another source for
> speculation and does not provide any advantage over the return
> posix_clock[id] path which returns NULL in that case anyway.
>
> Signed-off-by: Thomas Gleixner <[email protected]>
> Cc: [email protected]

Acked-by: Peter Zijlstra (Intel) <[email protected]>

It might also be useful to figure out why the automation didn't flag
this one, its about as trivial as it gets.

2018-02-16 14:50:24

by Rasmus Villemoes

[permalink] [raw]
Subject: [PATCH] linux/nospec.h: allow index argument to have const-qualified type

The last expression in a statement expression need not be a bare
variable, quoting gcc docs

The last thing in the compound statement should be an expression
followed by a semicolon; the value of this subexpression serves as the
value of the entire construct.

and we already use that in e.g. the min/max macros which end with a
ternary expression.

This way, we can allow index to have const-qualified type, which will in
some cases avoid the need for introducing a local copy of index of
non-const qualified type. That, in turn, can prevent readers not
familiar with the internals of array_index_nospec from wondering about
the seemingly redundant extra variable, and I think that's worthwhile
considering how confusing the whole _nospec business is.

The expression _i&_mask has type unsigned long (since that is the type
of _mask, and the BUILD_BUG_ONs guarantee that _i will get promoted to
that), so in order not to change the type of the whole expression, add
a cast back to typeof(_i).

Cc: [email protected]
Signed-off-by: Rasmus Villemoes <[email protected]>
---
cc stable because if this is ok, there will probably be future users
relying on this which also get cc'ed to -stable.

include/linux/nospec.h | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/include/linux/nospec.h b/include/linux/nospec.h
index fbc98e2c8228..132e3f5a2e0d 100644
--- a/include/linux/nospec.h
+++ b/include/linux/nospec.h
@@ -72,7 +72,6 @@ static inline unsigned long array_index_mask_nospec(unsigned long index,
BUILD_BUG_ON(sizeof(_i) > sizeof(long)); \
BUILD_BUG_ON(sizeof(_s) > sizeof(long)); \
\
- _i &= _mask; \
- _i; \
+ (typeof(_i)) (_i & _mask); \
})
#endif /* _LINUX_NOSPEC_H */
--
2.15.1


2018-02-16 15:15:48

by Linus Torvalds

[permalink] [raw]
Subject: Re: [PATCH] linux/nospec.h: allow index argument to have const-qualified type

On Thu, Feb 15, 2018 at 11:52 AM, Rasmus Villemoes
<[email protected]> wrote:
>
> This way, we can allow index to have const-qualified type, which will in
> some cases avoid the need for introducing a local copy of index of
> non-const qualified type.

Ack.

That said, looking at this header file, I find a couple of of other issues..

(a) we should just remove the array_index_mask_nospec_check() thing.

(b) once fixed, there's no reason for that extra "_s" variable in
array_index_nospec()

That (a) thing causes horrible code generation, and is pointless and wrong.

The "wrong" part is because it wants about "index" being larger than
LONG_MAX, and that's really stupid and wrong, because by *definition*
we don't trust index and it came from user space. The whole point was
that array_index_nospec() would limit those things!

Yes, it's true that the compiler may optimize that warning away if the
type of 'index' is such that it cannot happen, but that doesn't make
the warning any more valid.

It is only the sign of *size* that can matter and be an issue.
HOWEVER, even then it's wrong, because if "size" is of a signed type,
the check in WARN_ONCE is pure garbage.

To make matters worse, that warning means that
array_index_mask_nospec_check() currently uses it's arguments twice.
It so happens that the only current use of that macro is ok with that,
because it's being extra careful, but it's *WRONG*. Macros that look
like functions should not use arguments twice.

So (a) is just wrong right now. It's better to just remove it.

A valid warning *might* be

WARN_ONCE((long)(size) < 0, "array_index_mask only works for sizes
that fit in a positive long");

but honestly, it's just not worth the code generation pain.

Linus

2018-02-16 15:31:15

by Dan Williams

[permalink] [raw]
Subject: Re: [PATCH] linux/nospec.h: allow index argument to have const-qualified type

On Thu, Feb 15, 2018 at 12:59 PM, Linus Torvalds
<[email protected]> wrote:
> On Thu, Feb 15, 2018 at 11:52 AM, Rasmus Villemoes
> <[email protected]> wrote:
>>
>> This way, we can allow index to have const-qualified type, which will in
>> some cases avoid the need for introducing a local copy of index of
>> non-const qualified type.
>
> Ack.
>
> That said, looking at this header file, I find a couple of of other issues..
>
> (a) we should just remove the array_index_mask_nospec_check() thing.
>
> (b) once fixed, there's no reason for that extra "_s" variable in
> array_index_nospec()
>
> That (a) thing causes horrible code generation, and is pointless and wrong.
>
> The "wrong" part is because it wants about "index" being larger than
> LONG_MAX, and that's really stupid and wrong, because by *definition*
> we don't trust index and it came from user space. The whole point was
> that array_index_nospec() would limit those things!
>
> Yes, it's true that the compiler may optimize that warning away if the
> type of 'index' is such that it cannot happen, but that doesn't make
> the warning any more valid.
>
> It is only the sign of *size* that can matter and be an issue.
> HOWEVER, even then it's wrong, because if "size" is of a signed type,
> the check in WARN_ONCE is pure garbage.
>
> To make matters worse, that warning means that
> array_index_mask_nospec_check() currently uses it's arguments twice.
> It so happens that the only current use of that macro is ok with that,
> because it's being extra careful, but it's *WRONG*. Macros that look
> like functions should not use arguments twice.

Yes, that piece is new and I should have noticed that breakage when I
reviewed that patch from Will.

>
> So (a) is just wrong right now. It's better to just remove it.
>
> A valid warning *might* be
>
> WARN_ONCE((long)(size) < 0, "array_index_mask only works for sizes
> that fit in a positive long");
>
> but honestly, it's just not worth the code generation pain.

So I don't mind removing it, but I don't think it is garbage. It's
there purely as a notification to the odd kernel developer that wants
to pass "insane" index values, It compiles away in most cases because
all current users are sane and have indexes that are right sized. It
also used to be the case that it was only used when the arch did not
provide a custom array_index_mask_nospec(), but now that it is "on all
the time" I do think it is in the way.

2018-02-16 15:32:29

by Dan Williams

[permalink] [raw]
Subject: Re: [PATCH] linux/nospec.h: allow index argument to have const-qualified type

On Thu, Feb 15, 2018 at 2:03 PM, Linus Torvalds
<[email protected]> wrote:
> On Thu, Feb 15, 2018 at 1:56 PM, Dan Williams <[email protected]> wrote:
>>
>> So I don't mind removing it, but I don't think it is garbage. It's
>> there purely as a notification to the odd kernel developer that wants
>> to pass "insane" index values,
>
> But the thing is, the "index" value isn't even kernel-supplied.
>
> Here's a test: run a 32-bit kernel, and then do an ioctl() or
> something with a negative fd.
>
> What I think will happen is:
>
> - the negative fd will be seen as a big 'unsigned int' here:
>
> fcheck_files(struct files_struct *files, unsigned int fd)
>
> which then does
>
> fd = array_index_nospec(fd, fdt->max_fds);
>
> and that existing *STUPID* and *WRONG* WARN_ON() will trigger.
>
> Sure, you can't trigger it on 64-bit kernels because there the
> "unsigned int" will be small compared to LONG_MAX, but..
>
> It is simply is *wrong* to check the "index". It really fundamentally
> is complete garbage.
>
> Because the whole - and ONLY - *point* of this is that you have an
> untrusted index. So checking it and giving a warning when it's out of
> range is pure garbage.
>
> Really. That warning must go away. Stop arguing for it, it's stupid and wrong.

True, I had been myopically focused on the 64-bit case.

> Checking _size_ is one thing, but honestly, that's questionable too.

Nah, I'm not going to argue for that.

2018-02-16 15:33:20

by Linus Torvalds

[permalink] [raw]
Subject: Re: [PATCH] linux/nospec.h: allow index argument to have const-qualified type

On Thu, Feb 15, 2018 at 1:56 PM, Dan Williams <[email protected]> wrote:
>
> So I don't mind removing it, but I don't think it is garbage. It's
> there purely as a notification to the odd kernel developer that wants
> to pass "insane" index values,

But the thing is, the "index" value isn't even kernel-supplied.

Here's a test: run a 32-bit kernel, and then do an ioctl() or
something with a negative fd.

What I think will happen is:

- the negative fd will be seen as a big 'unsigned int' here:

fcheck_files(struct files_struct *files, unsigned int fd)

which then does

fd = array_index_nospec(fd, fdt->max_fds);

and that existing *STUPID* and *WRONG* WARN_ON() will trigger.

Sure, you can't trigger it on 64-bit kernels because there the
"unsigned int" will be small compared to LONG_MAX, but..

It is simply is *wrong* to check the "index". It really fundamentally
is complete garbage.

Because the whole - and ONLY - *point* of this is that you have an
untrusted index. So checking it and giving a warning when it's out of
range is pure garbage.

Really. That warning must go away. Stop arguing for it, it's stupid and wrong.

Checking _size_ is one thing, but honestly, that's questionable too.

Linus

2018-03-07 18:15:02

by Dan Williams

[permalink] [raw]
Subject: Re: [PATCH V2] posix-timers: Protect posix clock array access against speculation

On Thu, Feb 15, 2018 at 9:01 AM, Peter Zijlstra <[email protected]> wrote:
> On Thu, Feb 15, 2018 at 05:21:55PM +0100, Thomas Gleixner wrote:
>> The clockid argument of clockid_to_kclock() comes straight from user space
>> via various syscalls and is used as index into the posix_clocks array.
>>
>> Protect it against spectre v1 array out of bounds speculation. Remove the
>> redundant check for !posix_clock[id] as this is another source for
>> speculation and does not provide any advantage over the return
>> posix_clock[id] path which returns NULL in that case anyway.
>>
>> Signed-off-by: Thomas Gleixner <[email protected]>
>> Cc: [email protected]
>
> Acked-by: Peter Zijlstra (Intel) <[email protected]>
>

Curious where this ended up, I don't see it on tip/master. In any event:

Acked-by: Dan Williams <[email protected]>

Subject: [tip:timers/urgent] posix-timers: Protect posix clock array access against speculation

Commit-ID: 19b558db12f9f4e45a22012bae7b4783e62224da
Gitweb: https://git.kernel.org/tip/19b558db12f9f4e45a22012bae7b4783e62224da
Author: Thomas Gleixner <[email protected]>
AuthorDate: Thu, 15 Feb 2018 17:21:55 +0100
Committer: Thomas Gleixner <[email protected]>
CommitDate: Thu, 22 Mar 2018 12:29:27 +0100

posix-timers: Protect posix clock array access against speculation

The clockid argument of clockid_to_kclock() comes straight from user space
via various syscalls and is used as index into the posix_clocks array.

Protect it against spectre v1 array out of bounds speculation. Remove the
redundant check for !posix_clock[id] as this is another source for
speculation and does not provide any advantage over the return
posix_clock[id] path which returns NULL in that case anyway.

Signed-off-by: Thomas Gleixner <[email protected]>
Acked-by: Peter Zijlstra (Intel) <[email protected]>
Acked-by: Dan Williams <[email protected]>
Cc: Rasmus Villemoes <[email protected]>
Cc: Greg KH <[email protected]>
Cc: [email protected]
Cc: Linus Torvalds <[email protected]>
Cc: David Woodhouse <[email protected]>
Link: https://lkml.kernel.org/r/[email protected]

---
kernel/time/posix-timers.c | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/kernel/time/posix-timers.c b/kernel/time/posix-timers.c
index 75043046914e..10b7186d0638 100644
--- a/kernel/time/posix-timers.c
+++ b/kernel/time/posix-timers.c
@@ -50,6 +50,7 @@
#include <linux/export.h>
#include <linux/hashtable.h>
#include <linux/compat.h>
+#include <linux/nospec.h>

#include "timekeeping.h"
#include "posix-timers.h"
@@ -1346,11 +1347,15 @@ static const struct k_clock * const posix_clocks[] = {

static const struct k_clock *clockid_to_kclock(const clockid_t id)
{
- if (id < 0)
+ clockid_t idx = id;
+
+ if (id < 0) {
return (id & CLOCKFD_MASK) == CLOCKFD ?
&clock_posix_dynamic : &clock_posix_cpu;
+ }

- if (id >= ARRAY_SIZE(posix_clocks) || !posix_clocks[id])
+ if (id >= ARRAY_SIZE(posix_clocks))
return NULL;
- return posix_clocks[id];
+
+ return posix_clocks[array_index_nospec(idx, ARRAY_SIZE(posix_clocks))];
}