linux-4.4-rc8/arch/arm64/kernel/module.c:78: 32/64 bit problem ?

Ard Biesheuvel ard.biesheuvel at linaro.org
Mon Jan 4 07:24:49 PST 2016


On 4 January 2016 at 15:32, Ard Biesheuvel <ard.biesheuvel at linaro.org> wrote:
> On 4 January 2016 at 15:16, Will Deacon <will.deacon at arm.com> wrote:
>> On Mon, Jan 04, 2016 at 08:25:30AM +0000, David Binderman wrote:
>>> Hello there,
>>
>> Hi David,
>>
>>> [linux-4.4-rc8/arch/arm64/kernel/module.c:78] ->
>>> [linux-4.4-rc8/arch/arm64/kernel/module.c:88]: (warning) Shifting 32-bit
>>> value by 64 bits is undefined behaviour. See condition at line 88.
>>
>> Curious, but how are you seeing this warning? GCC is silent for me...
>>
>>> Source code is
>>>
>>>     u64 imm_mask = (1 << len) - 1;
>>>     s64 sval = do_reloc(op, place, val);
>>>
>>>     switch (len) {
>>>     case 16:
>>>         *(s16 *)place = sval;
>>>         break;
>>>     case 32:
>>>         *(s32 *)place = sval;
>>>         break;
>>>     case 64:
>>>
>>> So it seems that len can be 64. Suggest new code
>>>
>>>     u64 imm_mask = (1UL << len) - 1;
>>
>> That still ends up shifting by the width of the type when len == 64,
>> which is potentially still broken. We're better off using GENMASK.
>>
>
> Can't we simply return from the function rather than break from the
> switch statement if len == 64?
> The range check does not make any sense in that case anyway.
>

Or perhaps:

------------8<-----------------
diff --git a/arch/arm64/kernel/module.c b/arch/arm64/kernel/module.c
index f4bc779e62e8..fd1f4e678655 100644
--- a/arch/arm64/kernel/module.c
+++ b/arch/arm64/kernel/module.c
@@ -75,14 +75,17 @@ static u64 do_reloc(enum aarch64_reloc_op
reloc_op, void *place, u64 val)

 static int reloc_data(enum aarch64_reloc_op op, void *place, u64 val, int len)
 {
-       u64 imm_mask = (1 << len) - 1;
        s64 sval = do_reloc(op, place, val);

        switch (len) {
        case 16:
+               if (sval < S16_MIN || sval > U16_MAX)
+                       return -ERANGE;
                *(s16 *)place = sval;
                break;
        case 32:
+               if (sval < S32_MIN || sval > U32_MAX)
+                       return -ERANGE;
                *(s32 *)place = sval;
                break;
        case 64:
@@ -92,21 +95,6 @@ static int reloc_data(enum aarch64_reloc_op op,
void *place, u64 val, int len)
                pr_err("Invalid length (%d) for data relocation\n", len);
                return 0;
        }
-
-       /*
-        * Extract the upper value bits (including the sign bit) and
-        * shift them to bit 0.
-        */
-       sval = (s64)(sval & ~(imm_mask >> 1)) >> (len - 1);
-
-       /*
-        * Overflow has occurred if the value is not representable in
-        * len bits (i.e the bottom len bits are not sign-extended and
-        * the top bits are not all zero).
-        */
-       if ((u64)(sval + 1) > 2)
-               return -ERANGE;
-
        return 0;
 }



More information about the linux-arm-kernel mailing list