[C-safe-secure-studygroup] MISRA C:2012 Rule 11.6 - can undefined behaviour on conversion from intptr_t/uintptr_t to void * occur?

Martin Sebor msebor at gmail.com
Thu May 31 20:56:26 BST 2018


On 05/31/2018 11:38 AM, Aaron Ballman wrote:
> On Thu, May 31, 2018 at 11:38 AM, Martin Sebor <msebor at gmail.com> wrote:
>> On 05/31/2018 08:24 AM, Aaron Ballman wrote:
>>>
>>> On Thu, May 31, 2018 at 8:54 AM, Fulvio Baccaglini
>>> <fulvio_baccaglini at prqa.com> wrote:
>>>>
>>>> During the last meeting Aaron raised an interesting query about this
>>>> first line in the rationale of Rule 11.6:
>>>> "Conversion of an integer into a pointer to void may result in a pointer
>>>> that is not correctly aligned, resulting in undefined behaviour."
>>>>
>>>> Tentative argument:
>>>>
>>>> Looking at the list of undefined behaviours in the C99 standard, this
>>>> entry looks applicable:
>>>> [C99-J.2.22] "Conversion between two pointer types produces a result
>>>> that is incorrectly aligned (6.3.2.3)."
>>>
>>>
>>> I don't think that's applicable to the MISRA rule at all. The MISRA
>>> rule is about converting an integer into a pointer (to void or to
>>> object). C99 6.3.2.3p5 makes this explicitly implementation-defined
>>> behavior, not undefined behavior. The bullet you found is about
>>> converting between two pointers and is undefined (though I am not
>>> certain *why* it's UB to merely form the conversion).
>>
>>
>> Blame it on trap representations :-)
>>
>> It's undefined because the source pointer value need not be
>> representable in the destination pointer type.  For example,
>> take an implementation where the three least significant bits
>> in long* are padding bits and only the two least significant
>> bits in short* are.  On such an implementation directly
>> converting long* to short* might lose value.
>
> Agreed.
>
>> If those bits
>> were also trapping bits, the conversion would trap.
>
> Wait, what? I thought you had to do something with a trap
> representations in order to perform the trap and that a value with a
> trap representation itself was fine. Otherwise, declaring an automatic
> variable without explicitly initializing it could trap without ever
> actually mentioning the variable beyond its declaration (because an
> indeterminate value can either be unspecified or a trap
> representation). In my mental model, the conversion from long * to
> short * might create a trap representation in the short *, but there's
> no *trap* until you go to use the value  (such as performing
> arithmetic on it, dereferencing it, etc). e.g.,

Trapping is typically a property of a hardware register so what
needs to happen for it to trigger is for something to set
the trapping bit in it.  Allocating an auto variable doesn't
store anything in a register (or even allocate one) so it doesn't
trap.  But assigning a value to a variable usually does eventually
end up storing it in a register (unless the result is unused, or
unless the store is directly to memory), and that's what could
trap.  Even without assigning the value to a variable, if it
ends up in a register along the way it can trap.

> void f(long *lp) {
>   short *sp = (short *)lp; // May form a trap representation in sp,
> does not trap unless lp is a trap representation

(I assume you meant for the function to take a short* and the local
to be a long*.)

The conclusion in your comment doesn't necessarily follow.  If/when
the conversion is undefined it can trap (or do other "evil" things) 
regardless of trap representations.  The compiler could, for example,
insert a runtime test for lp's alignment followed by a break if
the test fails (like the Address Sanitizer does).

>   sp = sp + 2; // May trap when reading the value of sp to form the addition
>   *sp = 12; // May trap when reading the value of sp to form the dereference
>
>   int i; // May form a trap representation in i; does not trap
>   i = i + 2; // May trap when reading the value of i to form the addition
> }
>
> If forming a trap representation always performs a trap, why is there
> a distinction between the two terms of art in the standard? C17
> helpfully defines a term of art "perform a trap" in 3.19.5 but it
> never goes on to USE that definition anywhere normatively, so I could
> be totally wrong and if you can point to some formulation on this in
> the standard, I'd appreciate it.

A trap is a manifestation of undefined behavior, and so not
something that can be relied on by strictly conforming programs
(and in many cases not by any programs).

But the example we're talking about (pointer conversion) is not
described as performing a trap.  All the spec says is that it's
undefined.  The only realistic example of such undefined behavior
I can give is that of a trap, but there are other examples of what
might happen (e.g., it could corrupt memory).

Martin

>
> ~Aaron
>
>>  IMO, even
>> though implementations with trap representations are exceedingly
>> rare, I think the undefined behavior here is more useful than
>> if it were unspecified.  It makes it possible to pinpoint
>> the source of a misaligned pointer before it is dereferenced
>> (and either causes a trap then or yields a bogus value).
>>
>> Martin
>>
>>
>> _______________________________________________
>> C-safe-secure-studygroup mailing list
>> C-safe-secure-studygroup at lists.trustable.io
>> https://lists.trustable.io/cgi-bin/mailman/listinfo/c-safe-secure-studygroup
>
> _______________________________________________
> C-safe-secure-studygroup mailing list
> C-safe-secure-studygroup at lists.trustable.io
> https://lists.trustable.io/cgi-bin/mailman/listinfo/c-safe-secure-studygroup
>




More information about the C-safe-secure-studygroup mailing list