[PATCH 1/2] powerpc/pkeys: preallocate execute_only key only if the key is available.
Thiago Jung Bauermann
bauerman at linux.ibm.com
Sat Jun 30 10:58:37 AEST 2018
Gabriel Paubert <paubert at iram.es> writes:
> On Thu, Jun 28, 2018 at 11:56:34PM -0300, Thiago Jung Bauermann wrote:
>>
>> Hello,
>>
>> Ram Pai <linuxram at us.ibm.com> writes:
>>
>> > Key 2 is preallocated and reserved for execute-only key. In rare
>> > cases if key-2 is unavailable, mprotect(PROT_EXEC) will behave
>> > incorrectly. NOTE: mprotect(PROT_EXEC) uses execute-only key.
>> >
>> > Ensure key 2 is available for preallocation before reserving it for
>> > execute_only purpose. Problem noticed by Michael Ellermen.
>>
>> Since "powerpc/pkeys: Preallocate execute-only key" isn't upstream yet,
>> this patch could be squashed into it.
>>
>> > Signed-off-by: Ram Pai <linuxram at us.ibm.com>
>> > ---
>> > arch/powerpc/mm/pkeys.c | 14 +++++++++-----
>> > 1 files changed, 9 insertions(+), 5 deletions(-)
>> >
>> > diff --git a/arch/powerpc/mm/pkeys.c b/arch/powerpc/mm/pkeys.c
>> > index cec990c..0b03914 100644
>> > --- a/arch/powerpc/mm/pkeys.c
>> > +++ b/arch/powerpc/mm/pkeys.c
>> > @@ -19,6 +19,7 @@
>> > u64 pkey_amr_mask; /* Bits in AMR not to be touched */
>> > u64 pkey_iamr_mask; /* Bits in AMR not to be touched */
>> > u64 pkey_uamor_mask; /* Bits in UMOR not to be touched */
>> > +int execute_only_key = 2;
>> >
>> > #define AMR_BITS_PER_PKEY 2
>> > #define AMR_RD_BIT 0x1UL
>> > @@ -26,7 +27,6 @@
>> > #define IAMR_EX_BIT 0x1UL
>> > #define PKEY_REG_BITS (sizeof(u64)*8)
>> > #define pkeyshift(pkey) (PKEY_REG_BITS - ((pkey+1) * AMR_BITS_PER_PKEY))
>> > -#define EXECUTE_ONLY_KEY 2
>> >
>> > static void scan_pkey_feature(void)
>> > {
>> > @@ -122,8 +122,12 @@ int pkey_initialize(void)
>> > #else
>> > os_reserved = 0;
>> > #endif
>> > +
>> > + if ((pkeys_total - os_reserved) <= execute_only_key)
>> > + execute_only_key = -1;
>> > +
>> > /* Bits are in LE format. */
>> > - reserved_allocation_mask = (0x1 << 1) | (0x1 << EXECUTE_ONLY_KEY);
>> > + reserved_allocation_mask = (0x1 << 1) | (0x1 << execute_only_key);
>>
>> My understanding is that left-shifting by a negative amount is undefined
>> behavior in C. A quick test tells me that at least on the couple of
>> machines I tested, 1 < -1 = 0. Does GCC guarantee that behavior?
>
> Not in general. It probably always works on Power because of the definition
> of the machine instruction for shifts with variable amount (consider the
> shift amount unsigned and take it modulo twice the width of the operand),
Ok, thanks for confirming.
> but for example it fails on x86 (1<<-1 gives 0x80000000).
Strange, this works on my laptop with an Intel(R) Core(TM) i5-7300U CPU:
$ cat blah.c
#include <stdio.h>
int main(int argc, char *argv[])
{
printf("1 << -1 = %llx\n", (unsigned long long) 1 << -1);
return 0;
}
$ make blah
cc blah.c -o blah
blah.c: In function 'main':
blah.c:5:52: warning: left shift count is negative [-Wshift-count-negative]
printf("1 << -1 = %llx\n", (unsigned long long) 1 << -1);
^~
$ ./blah
1 << -1 = 0
Even if I change the cast and printf format to int, the result is the
same. Or am I doing it wrong?
>> If so, a comment pointing this out would make this less confusing.
>
> Unless I miss something, this code is run once at boot, so its
> performance is irrelevant.
>
> In this case simply rewrite it as:
>
> reserved_allocation_mask = 0x1 << 1;
> if ( (pkeys_total - os_reserved) <= execute_only_key) {
> execute_only_key = -1;
> } else {
> reserved_allocation_mask = (0x1 << 1) | (0x1 << execute_only_key);
> }
I agree it's clearer and more robust code (except that the first
line should be inside the if block).
>
> Caveat, I have assumed that the code will either:
> - only run once,
> - pkeys_total and os_reserved are int, not unsigned
Both of the above are true.
--
Thiago Jung Bauermann
IBM Linux Technology Center
More information about the Linuxppc-dev
mailing list