Skip to content

obmalloc's 8-byte alignment causes undefined behavior #72174

Closed

Description

@benjaminp
BPO 27987
Nosy @Yhg1s, @nascheme, @gpshead, @pitrou, @vstinner, @benjaminp, @methane, @skrah, @miss-islington
PRs
  • bpo-27987: pymalloc: align by 16bytes on 64bit platform #12850
  • [3.7] bpo-27987: pymalloc: align by 16bytes on 64bit platform (GH-12850) #13318
  • [2.7] bpo-27987: pymalloc: align by 16bytes on 64bit platform (GH-12850) #13319
  • [3.7] bpo-27987: align PyGC_Head to alignof(long double) #13335
  • bpo-27987: align PyGC_Head to alignof(long double) explicitly #13336
  • [3.7] bpo-27987: Revert "align PyGC_Head to alignof(long double)" (GH-13335) #13569
  • [3.7] bpo-27987: align PyGC_Head to alignof(long double) (GH-13335) #13581
  • Files
  • alignment.patch
  • 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:

    assignee = None
    closed_at = <Date 2019-05-25.18:25:09.602>
    created_at = <Date 2016-09-07.00:17:09.669>
    labels = ['interpreter-core', '3.8', 'type-bug', '3.7']
    title = "obmalloc's 8-byte alignment causes undefined behavior"
    updated_at = <Date 2019-06-03.02:02:46.814>
    user = 'https://github.com/benjaminp'

    bugs.python.org fields:

    activity = <Date 2019-06-03.02:02:46.814>
    actor = 'vstinner'
    assignee = 'none'
    closed = True
    closed_date = <Date 2019-05-25.18:25:09.602>
    closer = 'gregory.p.smith'
    components = ['Interpreter Core']
    creation = <Date 2016-09-07.00:17:09.669>
    creator = 'benjamin.peterson'
    dependencies = []
    files = ['44413']
    hgrepos = []
    issue_num = 27987
    keywords = ['patch']
    message_count = 41.0
    messages = ['274688', '304873', '304874', '304912', '304917', '304918', '304919', '304922', '304925', '304959', '305847', '305939', '305985', '311323', '340120', '340179', '340254', '340264', '340265', '340266', '340330', '340331', '342440', '342441', '342443', '342445', '342447', '342575', '343133', '343134', '343218', '343219', '343471', '343489', '343492', '343493', '343495', '343498', '343499', '343500', '344364']
    nosy_count = 11.0
    nosy_names = ['twouters', 'nascheme', 'gregory.p.smith', 'pitrou', 'vstinner', 'benjamin.peterson', 'methane', 'skrah', 'fweimer', 'miss-islington', 'tgrigg']
    pr_nums = ['12850', '13318', '13319', '13335', '13336', '13569', '13581']
    priority = 'normal'
    resolution = 'fixed'
    stage = 'resolved'
    status = 'closed'
    superseder = None
    type = 'behavior'
    url = 'https://bugs.python.org/issue27987'
    versions = ['Python 3.7', 'Python 3.8']

    Activity

    1. benjaminp commented on Sep 7, 2016

      @benjaminp
      ContributorAuthor

      ubsan complains about unaligned access when structs include "long double". An example error:
      runtime error: member access within misaligned address 0x7f77dbba9798 for type 'struct CDataObject', which requires 16 byte alignment

      This is because (on x86 anyway), long double is 16-bytes long and requires that alignment, but obmalloc only gives a 8-byte alignment. (glibc malloc() gives 16-byte alignment.)

      I'm attaching a POC patch. I don't know what the impact of increasing the alignment is on obmalloc's performance or memory usage. It's also unfortunate that this patch increases the size of PyGC_Head to 32 bytes from 24 bytes. One can imagine a more middle-ground solution to this by allowing types to specify their required alignment.

    2. added
      interpreter-core(Objects, Python, Grammar, and Parser dirs)
      type-bugAn unexpected behavior, bug, or error
      on Sep 7, 2016
    3. pitrou commented on Oct 24, 2017

      @pitrou
      Member

      What do we do if at some point a C type requires a larger alignment (for example a vector type readable using AVX512 instructions)?

    4. vstinner commented on Oct 24, 2017

      @vstinner
      Member

      ubsan complains about unaligned access when structs include "long double". An example error:
      runtime error: member access within misaligned address 0x7f77dbba9798 for type 'struct CDataObject', which requires 16 byte alignment

      Can we use memcpy() to prevent such issue?

    5. benjaminp commented on Oct 24, 2017

      @benjaminp
      ContributorAuthor

      My suggestion would be to pass alignof(type) into the allocator via macro. Then the allocator could at least assert it's providing good enough alignment if not provide the correct alignment.

      I believe 16-byte alignment is special because it's glibc's malloc's default. So "normal" code shouldn't really be expecting anything better than 16-byte alignment. Code with higher alignment requirements will have to use APIs like the one proposed in bpo-18835.

    6. pitrou commented on Oct 24, 2017

      @pitrou
      Member

      My suggestion would be to pass alignof(type) into the allocator via macro.

      Do you mean using some new PyMem_ function? Or as as new tp_ field on the type declaration?

    7. vstinner commented on Oct 24, 2017

      @vstinner
      Member

      What matters when a Python object is allocated? The start of the PyObject structure, or the start of the PyGC_Head structure? Would it be possible to align the PyObject start?

      The simplest option is to store data which needs to be aligned in a second memory block allocated by PyMem_AlignedAlloc().

    8. vstinner commented on Oct 24, 2017

      @vstinner
      Member

      Change by Antoine Pitrou: "versions: -Python 2.7, Python 3.3, Python 3.4, Python 3.5, Python 3.6"

      The undefined behaviour exists and should be fixed in Python 2.7 and 3.6, no? Can we use memcpy()?

    9. pitrou commented on Oct 24, 2017

      @pitrou
      Member

      Can we use memcpy()?

      Hmm, perhaps. Do you want to try it out (and measure any performance degradation)?

    10. skrah commented on Oct 24, 2017

      skrahmannequin
      Mannequin

      Since we have "#define PYMEM_FUNCS PYOBJ_FUNCS", I think extensions that
      use PyMem_Malloc() also won't get the glibc max_align_t alignment.

      But guess technically they should.

    11. benjaminp commented on Oct 25, 2017

      @benjaminp
      ContributorAuthor

      Yes, we could memcpy things around to obtain the desired alignment. It would be nicer to have a builtin solution, though.

    12. vstinner commented on Nov 8, 2017

      @vstinner
      Member

      alignment.patch: + long double dummy; /* force worst-case alignment */

      Would it be possible to use max_align_t mentioned by Stefan, at least when this type is available?

      What is the impact of the patch on objects size?

    13. benjaminp commented on Nov 9, 2017

      @benjaminp
      ContributorAuthor

      On Wed, Nov 8, 2017, at 06:14, STINNER Victor wrote:

      STINNER Victor <victor.stinner@gmail.com> added the comment:

      alignment.patch: + long double dummy; /* force worst-case alignment
      */

      Would it be possible to use max_align_t mentioned by Stefan, at least
      when this type is available?

      Yes, that would be the correct thing to do. I was looking for the quick
      hack.

      What is the impact of the patch on objects size?

      On 64-bit platforms, I believe it wastes a word for GC objects.

    14. 25 remaining items

    15. gpshead commented on May 25, 2019

      @gpshead
      Member

      commit reverted in 2156fec

    16. miss-islington commented on May 25, 2019

      @miss-islington
      Contributor

      New changeset 1b85f4e by Miss Islington (bot) in branch '3.7':
      bpo-27987: pymalloc: align by 16bytes on 64bit platform (GH-12850)
      1b85f4e

    17. gpshead commented on May 25, 2019

      @gpshead
      Member

      I'm not marking this bug as "Fixed" as the original complaint about obmalloc'd structs with a long double not being aligned is still going to be true on 32-bit platforms for 2.7 - 3.7. We've merely increased the obmalloc alignment to 16-bytes on 64-bit platforms.

      So the problem should only remain for 32-bit users which at this point are likely only arm (rpi and similar low end friends not using a 64-bit OS).

    18. pitrou commented on May 25, 2019

      @pitrou
      Member

      Here is what I've found for (32-bit) ARM:

      And on (32-bit) x86, it looks like the standard alignment for "long double" is 4 bytes:
      https://www.codesynthesis.com/~boris/blog/2009/04/06/cxx-data-alignment-portability/

      So I don't think there's anything to change on 32-bit Python builds *if* we only really care about ARM and x86 (which is restrictive, but using "long double" in C extension types is a bit of an exotic issue).

    19. pitrou commented on May 25, 2019

      @pitrou
      Member

      And of course, someone who has this issue can at worse recompile Python without pymalloc.

    20. gpshead commented on May 25, 2019

      @gpshead
      Member

      if someone runs into an actual need for this on 32-bit builds, please provide details and feel free to reopen the issue. closing as i don't believe there is any more for us to do.

    21. gpshead commented on Jun 3, 2019

      @gpshead
      Member

      New changeset 8766cb7 by Gregory P. Smith (Inada Naoki) in branch '3.7':
      [3.7] bpo-27987: align PyGC_Head to alignof(long double) (GH-13335) (GH-13581)
      8766cb7

    22. transferred this issue fromon Apr 10, 2022
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Metadata

    Metadata

    Assignees

    No one assigned

      Labels

      3.7 (EOL)end of life3.8 (EOL)end of lifeinterpreter-core(Objects, Python, Grammar, and Parser dirs)type-bugAn unexpected behavior, bug, or error

      Projects

      No projects

        Milestone

        No milestone

        Relationships

        None yet

        Development

        No branches or pull requests

        Issue actions