4pie0
4pie0

Reputation: 29724

Safely cast void* to int

If application is compiled to yield a x32 image then depending on architecture integer type may be 16 bits wide, 32s bit wide or anything more than 2 bytes. Size of void* will be 4 (on x32 always 4???). This would mean that passing int to void* is fine, but if it turns out that on a given architecture void* is wider than int (which is only guaranteed to be at least 2 bytes by the Standard) than in the face of

C Standard n1124 § 6.3.2.3 Pointers

5 An integer may be converted to any pointer type. Except as previously specified, the result is implementation-defined, might not be correctly aligned, might not point to an entity of the referenced type, and might be a trap representation.56)

6 Any pointer type may be converted to an integer type. Except as previously specified, the result is implementation-defined. If the result cannot be represented in the integer type, the behavior is undefined. The result need not be in the range of values of any integer type.

casting void* to int may produce undefined behavior in the following snippet.

typedef enum tagENUM
{
    WSO_1,
    WSO_2,
    //...
    WSO_COUNT
} ENUM;

/* I cannot change handler signature because this is callback. I have to cast void*
 * to ENUM however inside */
void handler( int i, int j, void *user_data)
{
    ENUM mOperation;
    mOperation = (ENUM)reinterpret_cast<int>(user_data);
}

// somewhere
handler( 1, 2, (void*)WSO_1);  // UB? We can imagine that someone passes to handler
                               // (void*)WSO_131072 which don't fit into 16 bits
                               // So is there a place opened for UB?

If this is correct that possibility for nasal deamons is opened - how do I then write thiss cast safely? Can I use intptr_t to make sure the result will fit?

void handler( int i, int j, void *user_data)
{
    ENUM mOperation;

uintptr_t p_mOperation = reinterpret_cast<uintptr_t>( user_data);

if ( p_mOperation > WSO_COUNT ) {
    send_error(conn, 500, http_500_error, "Error: %s", strerror(ERRNO));
    return;
}

mOperation = static_cast<ENUM_WS_OPERATION>( p_mOperation);  // now safe?

Upvotes: 0

Views: 5614

Answers (2)

Tom Tanner
Tom Tanner

Reputation: 9354

This bit:

mOperation = (ENUM)reinterpret_cast<int>(user_data);

suggests you've lost control of what you're doing already. You're doing a double cast which is always indicative of bad things going on (as if the reinterpret_cast wasn't already indicating that).

It's not clear from this what you're trying to do. Certainly (void*) is an inappropriate cast for passing values that aren't data. It's for passing pointers to arbitrary buffers of data where one hopes the thing at the other end can work out from the data what it needs to do with it.

So this:

handler( 1, 2, (void*)WSO_1); 

is just plain wrong.

However, given that WSO_1 is in fact an enum, and I don't know of any platform where a void * is actually smaller than an enum, you should be OK to do

mOperation = reinterpret_cast<ENUM>(user_data);

The client has already gone into dubious areas when he's cast the enum into a void *, and that cast isn't going to make it any worse.

Upvotes: 0

M.M
M.M

Reputation: 141544

Yes, it can cause undefined behaviour. If you use intptr_t instead then there is no undefined behaviour.

However, usually you can rewrite your code so that the pointer points to the intended integer, rather than being intended to be cast to it.

In your second example you have a mishmash. You want to use either use intptr_t, or void * that points to int. Not intptr_t * or uintptr_t *.

My preferred solution is that user_data always points to the data; and the type of the data being pointed to is determined by the handler being called or another parameter.

Upvotes: 5

Related Questions