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: stable@vger.kernel.org Signed-off-by: Rasmus Villemoes linux@rasmusvillemoes.dk --- 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 */
On Thu, Feb 15, 2018 at 11:52 AM, Rasmus Villemoes linux@rasmusvillemoes.dk 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
On Thu, Feb 15, 2018 at 12:59 PM, Linus Torvalds torvalds@linux-foundation.org wrote:
On Thu, Feb 15, 2018 at 11:52 AM, Rasmus Villemoes linux@rasmusvillemoes.dk 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.
On Thu, Feb 15, 2018 at 1:56 PM, Dan Williams dan.j.williams@intel.com 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
On Thu, Feb 15, 2018 at 2:03 PM, Linus Torvalds torvalds@linux-foundation.org wrote:
On Thu, Feb 15, 2018 at 1:56 PM, Dan Williams dan.j.williams@intel.com 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.
linux-stable-mirror@lists.linaro.org