<table border="1" cellspacing="0" cellpadding="8">
<tr>
<th>Issue</th>
<td>
<a href=https://github.com/llvm/llvm-project/issues/193139>193139</a>
</td>
</tr>
<tr>
<th>Summary</th>
<td>
[libc] broken C++ object model across posix interface
</td>
</tr>
<tr>
<th>Labels</th>
<td>
libc
</td>
</tr>
<tr>
<th>Assignees</th>
<td>
</td>
</tr>
<tr>
<th>Reporter</th>
<td>
SchrodingerZhu
</td>
</tr>
</table>
<pre>
According to https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf,
> If a program attempts to access the stored value of an object through a glvalue of other than one of the
> following types the behavior is undefined:52
> — the dynamic type of the object,
> — a cv-qualified version of the dynamic type of the object,
> — a type similar (as defined in 4.4) to the dynamic type of the object,
> — a type that is the signed or unsigned type corresponding to the dynamic type of the object,
> — a type that is the signed or unsigned type corresponding to a cv-qualified version of the dynamic type
> of the object,
> — an aggregate or union type that includes one of the aforementioned types among its elements or non-
> static data members (including, recursively, an element or non-static data member of a subaggregate
> or contained union),
> — a type that is a (possibly cv-qualified) base class type of the dynamic type of the object,
> — a char or unsigned char type.
However it is a common pattern for llvm-libc to use different public interface type and internal implementation type.
For example, we may have code similar to the following in things like `pthread_mutex_t/pthread_cond_t`, and effectively rejected by `clang -fsanitize=type`:
```c++
#include <type_traits>
typedef struct {
int __internal;
} lock_t;
struct Lock {
float x;
void init() {
this->x = 0;
}
void lock() {
this->x += 1;
}
bool is_locked() {
return this->x == 1;
}
};
static void do_init(lock_t* m) {
auto* ptr = reinterpret_cast<Lock*>(m);
ptr->init();
}
static void do_lock(lock_t* m) {
auto* ptr = reinterpret_cast<Lock*>(m);
ptr->lock();
}
#define LOCK_INIT {}
static_assert(sizeof(lock_t) == sizeof(Lock), "size mismatch");
static_assert(alignof(lock_t) == alignof(Lock), "alignof mismatch");
static_assert(std::is_trivial<Lock>(), "Lock is not trivial");
// this program should be rejected by type sanitizer???
int main() {
lock_t f = LOCK_INIT;
do_lock(&f);
f = LOCK_INIT;
do_lock(&f);
return 0;
}
```
The problem is that, the location of `&f` is actually never initialized as `Lock` but we always access it as if it is.
I think there are two ways to workaround this:
- For C11 API like `mtx_init`, where the object is always explicitly initialized via API, we can use placement new during initialization, so that the compiler can understand that we really want to start lifetime of the internal object at position.
- For API like in the above example, where POSIX allows constant initializer, we may need to abuse `__builtin_start_lifetime_as` and ensure that internal implementation is trivially copy constructible. Then, according to the standard, the object representation will appear as if it is converted via `std::bit_cast<U>(E)` inplace.
</pre>
<img width="1" height="1" alt="" src="http://email.email.llvm.org/o/eJy8V1GP4jgS_jXmpdQoODQ0Dzww3YOudaPblXZOOt1LVHEqxNuOnbMdaObXn8oJAWZ6dXMr3aFIgGN__uqrclUZQ9AHS7QVj5_E48sM-9g4v_1NNd5V2h7I_7PpZ6WrztudUs7zGEQHTYxdEPlOyL2Q-9PpNHcd2YcQq7nzByH3v0e1EHIflJQ84SD5X-VUEHLfYUeef8hswW9tnufreVfVQj6DyHb85J_htQaEzruDxxYwRmq7GHhzVIpCgNgQhOg8VXBE0xO4GtCCK38nFSE23vWHBhAOZnrtYkMeYsPTbBqJDY3b1c4Yd0r2nTsa4Etq8KidBx2gtxXV2lIl8t2jHBeB-CzFUyY2yzS_OltstUoII_rIR8jnH5cgqOPDv3o0utZsBfmgnb0s_K_B0sSgW23Qg5BPGGCkDNrCcr4UcsP6_Xnw2GBkLZL0HDgVOA-9HX-nOcp5T6Fz9hIr_-ftfl7Uy77p8xOkLODh4OmAkQYejHtD1CrTVxRuQguwdp5aslG7C-MA2Dp7AB0DkEkvA8NZZx_uGIWIUSuoMCK01JbkA3t12EbbA58WT6r3QR_JnPkv2gvkBfFHkHRMIPTlZMy9Dh6UsxFT2CQThdxc9fhDHyFz61wIujTnOxdw1JUYCJRBPrY3IfAzYXF3XBr0dyGQBnj1fMgbf3EnOpIHPXJSrm2dhY7Th7dQOw_GHNsHo0vFwdIHgkrXNXnWrOtLoxVoG8nXqGjghbYahiwa0G03CIzx4n3eeu880DvyS_bDiaDFMzR45ACtrqdyPA7XXKMtxEbbQwCj3wjEKuti4wmrou0jvReR8-U4opytiihW2eDqCqiuScXkffDEslEF5ZlRlEF7gIc6oNVRfyORv6SQX2WctbMd_0iPEvITP9lOyHwMYRD5M88uokcdg8g_i2zHAxXVEKLvVQSx5jUcMdpGKIqLQiJPWOsXME69FXH8n55x6Ren3m7W18ZhhPdpIo8dnWbRdRTyiePnOps_sdHhQeSf30HkL5ANK_mFWL98h8Ek_hOG_MQwiz-AKZ0zoEPBSBzMH2B5ir2397Q-hly_TGaOJzOxrFwxGjuKJnfQ3u-DfXQ83EWfrPaUFO88xUJhiCJ__pJs5UMj5BMvv27fRc_MropObvqQzKja_5bM1TXfkREyH-oWfPnl-a_F699ev6a978kWGAJ5Nifob-TqG76biwOmNwObTWowpORhaHVoMapGcIcyUvgeGY0-2I-hr6_uscfxn4IPkZsJke90KKLXR83HZ1AuyXZFTUdGB7AuwmXmDfDQhqUAnDqm0LjeVFDSXWoYeoQxKXiR78cn2_ExblHbHyJ8MB3q5OnJI1d3XuNFyFV95-k_s2Y8TNldUFyy1RAAXxtiM0tD7dAeINeLlFmNU0NmdjWkTLmqxSpLxUDFHo05gx0qBEuARn-jCjDw3KT7KoOyj5zA0ZzwHC7Npo48S9dDZRmrzWvK3W-8sSdATxBPDtKy6ODk_Bt619sqOWbMu9nuAbhaPC8WsPv1dUr7bXwfcsCQ3k8J8loPkwUDI3rvjFY6mvOdEUeNDDiWH4U2FbfOoBoaAksnqHo_FJ1xWZKKVwQ3FHLekD_KtZ025AccW5EPEZMhmMTxlKQ8oY1saojoIxhdU9TtVMmnojmagBE6FzTvOZ90mDRIlZAAS3eku1qalPj1l99e_wHIZTNwg8J04o39_qbsWuJWywGWrIBYZUVR9tpEbYtEtLgQLTCww1MptaH3UyP3cbHnUBvOHnc4rjsPRLim6dLQHL42lNTE26vScE1BW6GvLlE6CuKp8xQm_JM2BrDrCP1tsPEuR_Jx9LFYZVPeKPWUb_8-pIzPfJY44G3y_HxWbfNqk29wRtvFer1aLx-Xi6dZs92Uq2VdyzU-ZbhYlKusWizXWbaWq0X1KGs101uZyVW2lItsKfP8ab4pUZJ8wk1Z5zLbKLHMqEVt5txR8bVvpkPoabvY5It8MzNYkgnpYikl91ucsB5fZn6bOrCyPwSxzIwOMVwRoo4mXUbTgscXKL17IwvPQ5dyEa51FRlA5V0IKaber03brPdme38_PejY9OVcuVbIPe81fj103g3N5j5R5zvpyP64lf8OAAD__1IJ1Fg">