Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can Aliasing Problems be Avoided with const Variables

My company uses a messaging server which gets a message into a const char* and then casts it to the message type.

I've become concerned about this after asking this question. I'm not aware of any bad behavior in the messaging server. Is it possible that const variables do not incur aliasing problems?

For example say that foo is defined in MessageServer in one of these ways:

  1. As a parameter: void MessageServer(const char* foo)
  2. Or as const variable at the top of MessageServer: const char* foo = PopMessage();

Now MessageServer is a huge function, but it never assigns anything to foo, however at 1 point in MessageServer's logic foo will be cast to the selected message type.

auto bar = reinterpret_cast<const MessageJ*>(foo);

bar will only be read from subsequently, but will be used extensively for object setup.

Is an aliasing problem possible here, or does the fact that foo is only initialized, and never modified save me?

EDIT:

Jarod42's answer finds no problem with casting from a const char* to a MessageJ*, but I'm not sure this makes sense.

We know this is illegal:

MessageX* foo = new MessageX;
const auto bar = reinterpret_cast<MessageJ*>(foo);

Are we saying this somehow makes it legal?

MessageX* foo = new MessageX;
const auto temp = reinterpret_cast<char*>(foo);
auto bar = reinterpret_cast<const MessageJ*>(temp);

My understanding of Jarod42's answer is that the cast to temp makes it legal.

EDIT:

I've gotten some comments with relation to serialization, alignment, network passing, and so on. That's not what this question is about.

This is a question about strict aliasing.

Strict aliasing is an assumption, made by the C (or C++) compiler, that dereferencing pointers to objects of different types will never refer to the same memory location (i.e. alias eachother.)

What I'm asking is: Will the initialization of a const object, by casting from a char*, ever be optimized below where that object is cast to another type of object, such that I am casting from uninitialized data?

like image 755
Jonathan Mee Avatar asked Mar 18 '15 11:03

Jonathan Mee


1 Answers

First of all, casting pointers does not cause any aliasing violations (although it might cause alignment violations).

Aliasing refers to the process of reading or writing an object through a glvalue of different type than the object.

If an object has type T, and we read/write it via a X& and a Y& then the questions are:

  • Can X alias T?
  • Can Y alias T?

It does not directly matter whether X can alias Y or vice versa, as you seem to focus on in your question. But, the compiler can infer if X and Y are completely incompatible that there is no such type T that can be aliased by both X and Y, therefore it can assume that the two references refer to different objects.

So, to answer your question, it all hinges on what PopMessage does. If the code is something like:

const char *PopMessage()
{
     static MessageJ foo = .....;
     return reinterpret_cast<const char *>(&foo);
}

then it is fine to write:

const char *ptr = PopMessage();
auto bar = reinterpret_cast<const MessageJ*>(foo);

auto baz = *bar;    // OK, accessing a `MessageJ` via glvalue of type `MessageJ`
auto ch = ptr[4];   // OK, accessing a `MessageJ` via glvalue of type `char`

and so on. The const has nothing to do with it. In fact if you did not use const here (or you cast it away) then you could also write through bar and ptr with no problem.

On the other hand, if PopMessage was something like:

const char *PopMessage()
{
    static char buf[200];
    return buf;
}

then the line auto baz = *bar; would cause UB because char cannot be aliased by MessageJ. Note that you can use placement-new to change the dynamic type of an object (in that case, char buf[200] is said to have stopped existing, and the new object created by placement-new exists and its type is T).

like image 89
M.M Avatar answered Sep 22 '22 13:09

M.M