-
-
Notifications
You must be signed in to change notification settings - Fork 30.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
clang expects memory aligned on 16 bytes, but pymalloc aligns to 8 bytes #80799
Comments
On x86-64, clang -O3 compiles the following function: PyCArgObject *
PyCArgObject_new(void)
{
PyCArgObject *p;
p = PyObject_New(PyCArgObject, &PyCArg_Type);
if (p == NULL)
return NULL;
p->pffi_type = NULL;
p->tag = '\0';
p->obj = NULL;
memset(&p->value, 0, sizeof(p->value));
return p;
} like that: 0x00007fffe9c6acb0 <+0>: push rax The problem is that movaps requires the memory address to be aligned on 16 bytes, whereas PyObject_New() uses pymalloc allocator (the requested size is 80 bytes, pymalloc supports allocations up to 512 bytes) and pymalloc only provides alignment on 8 bytes. If PyObject_New() returns an address not aligned on 16 bytes, PyCArgObject_new() crash immediately with a segmentation fault (SIGSEGV). CPython must be compiled using -fmax-type-align=8 to avoid such alignment crash. Using this compiler flag, clag emits expected machine code: 0x00007fffe9caacb0 <+0>: push rax "movaps" instruction becomes "movups" instruction: "a" stands for "aligned" in movaps, whereas "u" stands for "unaligned" in movups. |
I merged a "workaround" in the master branch. Python 2.7 and 3.7 are also affected, but I prefer to wait to see if the change goes through buildbots. The real fix would be to modify pymalloc to use 16-byte alignement, but that's a more complex issue :-) |
Note to myself: "Sadly, the flag must be expected to CFLAGS and not just CFLAGS_NODIST, ..." It should be "Sadly, the flag must be *set* to CFLAGS and not just CFLAGS_NODIST, ..." :-( I should fix the NEWS entry. |
Oh, it seems like the change broke the FreeBSD 10 buildbot :-( https://buildbot.python.org/all/#/builders/167/builds/769 ... -- On the previous build, the output looked fine: https://buildbot.python.org/all/#/builders/167/builds/768 ... pythoninfo: CC.version: FreeBSD clang version 3.4.1 (tags/RELEASE_34/dot1-final 208032) 20140512 |
Even if you check for -fmax-type-align compiler support at configure time, there is a potential problem: Nothing guarantees that extension modules are built by the same compiler that CPython is. If CPython used an old clang without support for that flag and the extension module compiled by that CPython via pip and setup.py, etc. uses a more recent version of clang - it wouldn't specify that flag and the extension module code could be broken. I suppose this issue of conditional compiler flags is nothing new. It should not block us from going forward with a workaround like your PRs for now. |
I believe -fno-max-type-align is also an option. |
I pushed a fix quickly to unblock my PR 12796, but also because I was very scared by what I saw :-D I see my change as a "quick fix", but we really have to sit down to think about the "correct fix". Especially since we will have to do something for Python 2.7 and 3.7, and adding -fmax-type-align=8 to exported CFLAGS can cause *new* issues, as you explained. That's why I mentioned bpo-27987 and other issues, to try to see what has already been said, and try to find to identify and fix the root issue, rather than working around the issue with compiler flags. |
The issue is related to the definition of PyCArgObject: typedef struct tagPyCArgObject PyCArgObject;
struct tagPyCArgObject {
PyObject_HEAD
ffi_type *pffi_type;
char tag;
union {
char c;
char b;
short h;
int i;
long l;
long long q;
long double D;
double d;
float f;
void *p;
} value;
PyObject *obj;
Py_ssize_t size; /* for the 'V' tag */
}; This object must be allocated with suitable alignment (which is 16 on many platforms), and the default Python allocator apparently provides 8-byte alignment only on 64-bit platforms. In short, using PyObject_New with PyCArgObject results in undefined behavior. This issue potentially affects all compilers, not just Clang. |
C++ has a __alignof__ function/macro/operator (not sure what is its kind) to get the alignment of a type. C11 has <stdalign.h> header which provides an alignof() function. GCC has __alignof__(). I also found "_Alignof()" name. ... no sure which one is the most portable ... |
I also found this macro: #define ALIGNOF(type) offsetof (struct { char c; type member; }, member) |
More info on alignof(): |
@vstinner: once you have a portable version of alignof, you can deciding to *not* use the pool allocator if the required alignment is greater than 8B, or you could modify the pool allocator to take alignment information as an extra parameter? |
Python 3.8 now respects the x86-64 ABI: https://bugs.python.org/issue27987 I reverted my workaround. |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: