[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Xen-devel] [PATCH v6 1/4] xen: introduce SYMBOL

On Tuesday, January 15, 2019 3:21 AM, Jan Beulich wrote:
> >>> On 14.01.19 at 22:18, <sstabellini@xxxxxxxxxx> wrote:
> > Hi Jan,
> >
> > One question below to make a decision on the way forward.
> >
> > On Mon, 14 Jan 2019, Jan Beulich wrote:
> >> >>> On 14.01.19 at 04:45, <Stewart.Hildebrand@xxxxxxxxxxxxxxx> wrote:
> >> > The difference would be whether we want to rely on implementation-defined
> >> > behavior or not.
> >>
> >> Why not? Simply specify that compilers with implementation defined
> >> behavior not matching our expectations are unsuitable. And btw, I
> >> suppose this is just the tiny tip of the iceberg of our reliance on
> >> implementation defined behavior.
> >
> > The reason is that relying on undefined behavior is not reliable, it is
> > not C compliant, it is not allowed by MISRA-C, and not guaranteed to
> > work with any compiler.
> "undefined behavior" != "implementation defined behavior"

The C standard gives definitions for unspecified, implementation defined,
and undefined behavior.
To paraphrase:
- Unspecified behavior: the C standard intentionally leaves a choice for
  the implementation to make.
- Implementation defined behavior: the implementation's choice of the
  unspecified behavior.
- Undefined behavior: the C standard does not impose any requirements.

Annex J in the C99 standard lists cases of unspecified, implementation
defined, and undefined behavior.

Two relevant examples are:
- The width of unsigned long is implementation-defined (i.e. ULONG_MAX is
  implementation-defined). The example given in Annex E in the C standard
  is "#define ULONG_MAX 4294967295", but that is to be replaced by the
  implementation's choice.
- Performing subtraction on pointers to different objects is undefined

> > Yes, this instance is only the tip of the
> > iceberg, we have a long road ahead, but we shouldn't really give up
> > because it is going to be difficult :-) Stewart's approach would
> > actually be compliant and help toward reducing reliance on undefined
> > behavior.
> >
> > Would you be OK if I rework the series to follow his approach using
> > intermediate variables? See the attached patch as a reference, it only
> > "converts" _start and _end as an example. Fortunately, it will be
> > textually similar to the previous SYMBOL returning unsigned long version
> > of the series.
> Well, I've given reasons why I dislike that, and why (I think) it was
> done without such intermediate variables. Nevertheless, if this is
> _the only way_ to achieve compliance, I don't think I could
> reasonably NAK it.
> The thing that I don't understand though is how the undefined
> behavior (if there really is any) goes away: Even if you compare
> the contents of the variables instead of the original (perhaps
> casted) pointers, in the end you still compare what C would
> consider pointers to different objects. It's merely a different
> way of hiding that fact from C. Undefined behavior would imo
> go away only if those comparisons/subtractions didn't happen
> in C anymore. IOW - see my .startof.() / .sizeof.() proposal.

No, the C standard provides us a guarantee.

To quote the ISO/IEC 9899 C99 standard regarding the subtract operator:
> For subtraction, one of the following shall hold:
> - both operands have arithmetic type;
> - both operands are pointers to qualified or unqualified versions of
>   compatible object types; or
> - the left operand is a pointer to an object type and the right operand
>   has integer type.
> If both operands have arithmetic type, the usual arithmetic conversions
> are performed on them.
> When an expression that has integer type is added to or subtracted from
> a pointer ... If both the pointer operand and the result point to
> elements of the same array object, or one past the last element of the
> array object, the evaluation shall not produce an overflow; otherwise,
> the behavior is undefined.

Here, "arithmetic type" is either integer type or floating point type (but
NOT pointer type).

There is similar language for the equality comparison operator that
Stefano quoted earlier in the thread.

My interpretation of the standard is:
Subtract/compare operations where one or both operands are pointer types
are potentially subject to the "pointers to different objects" issue, and
the compiler is free to make that determination by any means available.
Subtract/compare operations where both operands are integer types are well
defined in the C standard, and, per the C standard, are NOT subject to the
"pointers to different objects" issue. If the compiler starts to consider
integer types being "pointers to different objects" then the compiler
clearly does not adhere to the C standard. The compiler may look through
*pointer type* casts, but if it started to look through *integer type*
casts, we would have good reason to complain to the GCC mailing list.

You could achieve both operands being integer types either by changing the
type of _start (using intermediate variables) or by casting (using
SYMBOL_HIDE with an integer return type). I would argue that changing the
type of _start would be less prone to human error, since developers
wouldn't have to remember to explicitly wrap each reference to _start and
friends in a macro. That's probably not an issue for the patches submitted
to xen-devel that are subject to informed review, but in potential
downstream/forks of Xen, it would be easy for somebody to miss the
requirement of having to use SYMBOL_HIDE every time the variable is
referenced. Somebody wrote the code this way in the first place, and the
potentially undefined behavior has been in upstream for years without any

With the SYMBOL_HIDE approach, we are probably violating a few MISRA rules
with all the tricks going on inside SYMBOL_HIDE, so we'd have to write up
deviations with justification for that and track each occurrence. With the
approach of changing the type of _start, I believe there will be fewer
MISRA rule violations.

Just to reiterate, MISRA C says: don't subtract/compare *pointer types*
pointing to different objects, otherwise it's "undefined behavior" except
in one irrelevant corner case (I'm paraphrasing since the actual text is
copyrighted). If the operands are both integer types (not pointer types),
we don't risk violating the MISRA rules pertaining to pointer types. For
completeness, the corner case is pointer A pointing to one element past
the end of object A, and pointer B pointing to the beginning of object B.

> > If you are OK with it, do you have any suggestions on how would you like
> > the intermediate variables to be called? I went with _start/start_ and
> > _end/end_ but I am open to suggestions. Also to which assembly file you
> > would like the new variables being added -- I created a new one for the
> > purpose named var.S in the attached example.
> First of all we should explore whether the variables could also be
> linker generated, in particular to avoid the current symbols to be
> global (thus making it impossible to access them from C files in the
> first place).

Interesting idea. That certainly would be ideal if the linker will allow

> Failing that, I don't think it matters much where these
> helper symbols live, and hence your choice is probably fine (I'd
> prefer though if, just like on Arm, the x86 file didn't live in the
> boot/ subdirectory; in the end it might even be possible to have
> some of them in xen/common/var.S).
> Jan

(if you're tired of reading my walls of text, you can stop here)

Lastly, please let me take a moment to reiterate why MISRA C exists and
how safety certification relates to the Xen project. Here is a quick
definition two distinct concepts:
Safety: preservation of human life and avoiding harmful behavior.
Security: locking up your valuables.

As embedded devices gain more connectivity and functionality, it is
becoming more economical to consolidate functions (both safety and
non-safety critical) on to a single hardware platform, hence the need for
a hypervisor. One of the draws of potentially using Xen in a safety
critical system is that it already has an extremely large user base that
cares a lot about security. Although safety and security are two distinct
concepts, there is still a lot of overlap. A coding error that allows a
hacker to access a private database could just as well cause unintended
acceleration in a drive-by-wire system. In the safety critical world, it
is not enough to say that something works, we also have to do our due
diligence to ensure that it won't exhibit unintended behavior and that it
keeps working reliably. The rigor and assurance involved in a safety
critical process is a pretty powerful benefit that I think carries over to
those who care about security.

From a MISRA perspective, we have to document and ensure developers
understand implementation defined behavior (MISRA C:2012 Directive 1.1).
We also can't use any undefined behavior (Rule 1.3). Where it is
unavoidable to violate a rule, we have to write up deviations for MISRA
rules that are violated, and justify/maintain each violation of a MISRA
rule. It's much more maintainable and justifiable from a MISRA perspective
to not violate the rule in the first place. The longer our list of
violations becomes, the larger the burden imposed on the community that
cares about safety certification. This is not something that would be
necessary for the entire codebase, only a safety certified subset. For
MISRA, we have no choice but to pick apart the entire safety certified
subset of the iceberg.

Let's assume, for example, that we would have to document why "inspecting
the text of an asm() is something that should never happen", thus
guaranteeing that the compiler won't make the assumption that the value
passed through that inline assembly could be considered a "pointer to a
different object". Documenting this could range anywhere from simply
referencing compiler documentation to inspecting compiler source code and
potentially imposing certain restrictions on what optimization flags we're
allowed to use, and then likely pinning the compiler version.

Once the safety critical subset has been fully defined, I wouldn't rule
out the possibility that somebody will try to use a compiler built for
safety critical applications. GCC has too many unregulated moving pieces
to really be suitable for higher levels of safety assurance (unless you
painstakingly perform manual object code verification - which we obviously
try to avoid if we can due to the effort involved. And even with the right
compiler, sometimes this still has to be done for certain software on
commercial airliners - but Xen has quite a way to go before that would
reasonably happen).

At the end of the day, I have a harder time justifying more and more
implementation defined behavior and rule violations when there is a
proposed solution that avoids violating certain MISRA rules in the first
place. I live in a world where *should never* isn't something I would
trust my life to.

Xen-devel mailing list



Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.