Pelles C forum

Pelles C => Bug reports => Topic started by: aaaaaa123456789 on July 13, 2022, 11:32:58 PM

Title: Misalignment of buffers allocated with malloc
Post by: aaaaaa123456789 on July 13, 2022, 11:32:58 PM
Disclaimer: as this is a libc bug, I don't know if it belongs in the compiler or some underlying layer. If this is not the right place to post about this bug, please let me know and I'll report it where appropriate.

malloc is supposed to return a pointer that "is suitably aligned so that it may be assigned to a pointer to any type of object with a fundamental alignment requirement and then used to access such an object or an array of such objects in the space allocated" (C17, section 7.22.3, paragraph 1). The standard defines "fundamental alignment" as "a valid alignment less than or equal to _Alignof (max_align_t)" (C17, section 6.2.8, paragraph 2).
In other words, a non-null result of malloc must be aligned to max_align_t alignment.

However, on my system, alignof(max_align_t) is 16, but malloc only returns pointers aligned to 8. This causes problems with code that assumes 16-byte alignment for those pointers. (The practical example I encountered was with an allocated jmp_buf — the implementation of setjmp uses the movdqa instruction to save SSE state, and that happens to be one of the few x86 instructions that cares about alignment; the misaligned pointer causes a fault.)

Pelles C version: 11.00.2, running on 64-bit wine 5.0 (reported as "Microsoft Windows 7 Ultimate N Edition, 64-bit, Service Pack 1 (Build 7601)" in the IDE about box).

Sample code (allocating and showing pointers):
Code: [Select]
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <stdalign.h>

int main (void) {
  printf("max_align_t alignment: %zu\n", alignof(max_align_t));
  for (unsigned p = 0; p < 20; p ++) {
    // allocate alignof(max_align_t) bytes in both cases for a fair comparison
    void * mallocptr = malloc(alignof(max_align_t));
    void * alignedptr = aligned_alloc(alignof(max_align_t), alignof(max_align_t));
    printf("malloc: %p, aligned_alloc: %p\n", mallocptr, alignedptr);
  }
  return 0;
}

Sample output (showing pointers aligned to 8, as opposed to aligned_alloc properly aligning to 16):
Code: [Select]
[18:03:00] ax6@n3 ~/.wine/drive_c/users/ax6/C/test $ wine64 test.exe
max_align_t alignment: 16
malloc: 0000000000340058, aligned_alloc: 0000000000340080
malloc: 00000000003400a8, aligned_alloc: 00000000003400d0
malloc: 00000000003400f8, aligned_alloc: 0000000000340120
malloc: 0000000000340148, aligned_alloc: 0000000000340170
malloc: 0000000000340198, aligned_alloc: 00000000003401c0
malloc: 00000000003401e8, aligned_alloc: 0000000000340210
malloc: 0000000000340238, aligned_alloc: 0000000000340260
malloc: 0000000000340288, aligned_alloc: 00000000003402b0
malloc: 00000000003402d8, aligned_alloc: 0000000000340300
malloc: 0000000000340328, aligned_alloc: 0000000000340350
malloc: 0000000000340378, aligned_alloc: 00000000003403a0
malloc: 00000000003403c8, aligned_alloc: 00000000003403f0
malloc: 0000000000340418, aligned_alloc: 0000000000340440
malloc: 0000000000340468, aligned_alloc: 0000000000340490
malloc: 00000000003404b8, aligned_alloc: 00000000003404e0
malloc: 0000000000340508, aligned_alloc: 0000000000340530
malloc: 0000000000340558, aligned_alloc: 0000000000340580
malloc: 00000000003405a8, aligned_alloc: 00000000003405d0
malloc: 00000000003405f8, aligned_alloc: 0000000000340620
malloc: 0000000000340648, aligned_alloc: 0000000000340670
Title: Re: Misalignment of buffers allocated with malloc
Post by: John Z on July 14, 2022, 12:56:37 PM
Output of your program run under 64 bit Windows 11 Home 10.0.22 build 22000,
Pelles C v11.002

Don't know if this answers your question but here are the results.
Build and run as a 64 bit console program

Code: [Select]
C:\Program Files\PellesC\Files\align>align
max_align_t alignment: 16
malloc: 000002a5998d0040, aligned_alloc: 000002a5998d0060
malloc: 000002a5998d0090, aligned_alloc: 000002a5998d00b0
malloc: 000002a5998d00e0, aligned_alloc: 000002a5998d0100
malloc: 000002a5998d0130, aligned_alloc: 000002a5998d0150
malloc: 000002a5998d0180, aligned_alloc: 000002a5998d01a0
malloc: 000002a5998d01d0, aligned_alloc: 000002a5998d01f0
malloc: 000002a5998d0220, aligned_alloc: 000002a5998d0240
malloc: 000002a5998d0270, aligned_alloc: 000002a5998d0290
malloc: 000002a5998d02c0, aligned_alloc: 000002a5998d02e0
malloc: 000002a5998d0310, aligned_alloc: 000002a5998d0330
malloc: 000002a5998d0360, aligned_alloc: 000002a5998d0380
malloc: 000002a5998d03b0, aligned_alloc: 000002a5998d03d0
malloc: 000002a5998d0400, aligned_alloc: 000002a5998d0420
malloc: 000002a5998d0450, aligned_alloc: 000002a5998d0470
malloc: 000002a5998d04a0, aligned_alloc: 000002a5998d04c0
malloc: 000002a5998d04f0, aligned_alloc: 000002a5998d0510
malloc: 000002a5998d0540, aligned_alloc: 000002a5998d0560
malloc: 000002a5998d0590, aligned_alloc: 000002a5998d05b0
malloc: 000002a5998d05e0, aligned_alloc: 000002a5998d0600
malloc: 000002a5998d0630, aligned_alloc: 000002a5998d0650

C:\Program Files\PellesC\Files\align>

Build and run as as 32 bit console program
Code: [Select]
C:\Program Files\PellesC\Files\align>align
max_align_t alignment: 16
malloc: 01990348, aligned_alloc: 01990370
malloc: 01990398, aligned_alloc: 019903c0
malloc: 019903e8, aligned_alloc: 01990410
malloc: 01990438, aligned_alloc: 01990460
malloc: 01990488, aligned_alloc: 019904b0
malloc: 019904d8, aligned_alloc: 01990500
malloc: 01990528, aligned_alloc: 01990550
malloc: 01990578, aligned_alloc: 019905a0
malloc: 019905c8, aligned_alloc: 019905f0
malloc: 01990618, aligned_alloc: 01990640
malloc: 01990668, aligned_alloc: 01990690
malloc: 019906b8, aligned_alloc: 019906e0
malloc: 01990708, aligned_alloc: 01990730
malloc: 01990758, aligned_alloc: 01990780
malloc: 019907a8, aligned_alloc: 019907d0
malloc: 019907f8, aligned_alloc: 01990820
malloc: 01990848, aligned_alloc: 01990870
malloc: 01990898, aligned_alloc: 019908c0
malloc: 019908e8, aligned_alloc: 01990910
malloc: 01990938, aligned_alloc: 01990960

C:\Program Files\PellesC\Files\align>

So did you build a 32 bit or 64 bit exe?

John Z
Title: Re: Misalignment of buffers allocated with malloc
Post by: frankie on July 14, 2022, 02:21:04 PM
Following are the outputs of your program adapted and compiled as C++ under MSVC for comparison purpose.
Code: [Select]
#include <stdio.h>
#include <stdlib.h>
#include <cstdlib>
#include <stddef.h>
#include <cstddef>
#define align_16 16

int main(void) {
    printf("max_align_t alignment: %zu\n", alignof(std::max_align_t));
    for (unsigned p = 0; p < 20; p++) {
        // allocate alignof(max_align_t) bytes in both cases for a fair comparison
        void* mallocptr = malloc(align_16);
        void* alignedptr = _aligned_malloc(align_16, align_16);
        printf("malloc: %p, aligned_alloc: %p\n", mallocptr, alignedptr);
    }
    return 0;
}

Quote
32 bits version:
Code: [Select]
max_align_t alignment: 8
malloc: 00BE6308, aligned_alloc: 00BE63E0
malloc: 00BE0598, aligned_alloc: 00BE6410
malloc: 00BE5458, aligned_alloc: 00BE6440
malloc: 00BE6468, aligned_alloc: 00BEB340
malloc: 00BEB368, aligned_alloc: 00BEB390
malloc: 00BEB3D8, aligned_alloc: 00BEBCC0
malloc: 00BEB4E0, aligned_alloc: 00BEBC00
malloc: 00BEB4C8, aligned_alloc: 00BEB9F0
malloc: 00BEB480, aligned_alloc: 00BEBBA0
malloc: 00BEB4F8, aligned_alloc: 00BEBB70
malloc: 00BEB558, aligned_alloc: 00BEBC60
malloc: 00BEB510, aligned_alloc: 00BEBD80
malloc: 00BEB438, aligned_alloc: 00BEBA20
malloc: 00BEB588, aligned_alloc: 00BEBBD0
malloc: 00BEB5A0, aligned_alloc: 00BEBC30
malloc: 00BEB3F0, aligned_alloc: 00BEBCF0
malloc: 00BEB498, aligned_alloc: 00BEBC90
malloc: 00BEB528, aligned_alloc: 00BEBD50
malloc: 00BEB540, aligned_alloc: 00BEBA50
malloc: 00BEB4B0, aligned_alloc: 00BEBA80

Quote
64 bits version:
Code: [Select]
max_align_t alignment: 8
malloc: 000001951D07A6F0, aligned_alloc: 000001951D079FB0
malloc: 000001951D07A9F0, aligned_alloc: 000001951D079F20
malloc: 000001951D07AA30, aligned_alloc: 000001951D07A0D0
malloc: 000001951D07A7F0, aligned_alloc: 000001951D07A100
malloc: 000001951D07A850, aligned_alloc: 000001951D07A1C0
malloc: 000001951D07AA50, aligned_alloc: 000001951D07A190
malloc: 000001951D07A810, aligned_alloc: 000001951D07A130
malloc: 000001951D07A870, aligned_alloc: 000001951D07A160
malloc: 000001951D07A970, aligned_alloc: 000001951D07A1F0
malloc: 000001951D07A9B0, aligned_alloc: 000001951D07A010
malloc: 000001951D07A8D0, aligned_alloc: 000001951D07A220
malloc: 000001951D07AA70, aligned_alloc: 000001951D079F50
malloc: 000001951D07A9D0, aligned_alloc: 000001951D079FE0
malloc: 000001951D07A770, aligned_alloc: 000001951D07A250
malloc: 000001951D07A710, aligned_alloc: 000001951D079EF0
malloc: 000001951D07A830, aligned_alloc: 000001951D080740
malloc: 000001951D07A7B0, aligned_alloc: 000001951D0803E0
malloc: 000001951D07A8F0, aligned_alloc: 000001951D0805C0
malloc: 000001951D07A750, aligned_alloc: 000001951D0802F0
malloc: 000001951D07A790, aligned_alloc: 000001951D080680

max_align_t is always 8 for both WIN32 and WIN64 compilations.
malloc alligns on 8 bytes for WIN32 and 16bytes for WIN64 exactly as PellesC.
With reference to the "fundamental alignment" (as defined in C17, section 7.22.3, paragraph 1), I think it should be considered that 128bits and 256bits objects aren't actually defined, so the 8byte alignment is suitable for all types defined in the standard and should be the effective one (at least until larger types, 128-256bits, will be introduced).
At least this is my personal interpretation, maybe Pelle could add points...
Title: Re: Misalignment of buffers allocated with malloc
Post by: John Z on July 14, 2022, 03:51:03 PM
The standard defines "fundamental alignment" as "a valid alignment less than or equal to _Alignof (max_align_t)" (C17, section 6.2.8, paragraph 2).
In other words, a non-null result of malloc must be aligned to max_align_t alignment.

The standard mentions above does include the words "less than OR equal to"  isn't that dispositive?

John Z
Title: Re: Misalignment of buffers allocated with malloc
Post by: aaaaaa123456789 on July 18, 2022, 08:09:03 AM
So did you build a 32 bit or 64 bit exe?

As 64-bit. (That's why the output to the %p format specifier is 16 characters long.)

With reference to the "fundamental alignment" (as defined in C17, section 7.22.3, paragraph 1), I think it should be considered that 128bits and 256bits objects aren't actually defined, so the 8byte alignment is suitable for all types defined in the standard and should be the effective one (at least until larger types, 128-256bits, will be introduced).
At least this is my personal interpretation, maybe Pelle could add points...

If this is the intention, then max_align_t should have an alignment requirement of 8. (And none of the types required to have a fundamental alignment should have a greater alignment requirement.) What is improper is for max_align_t to have a greater alignment requirement than what the implementation considers fundamental.

The standard mentions above does include the words "less than OR equal to"  isn't that dispositive?

That quote is defining what a fundamental alignment is: any alignment that meets that requirement.
This is the full paragraph:

Quote
A fundamental alignment is a valid alignment less than or equal to _Alignof (max_align_t) . Fundamental alignments shall be supported by the implementation for objects of all storage durations. The alignment requirements of the following types shall be fundamental alignments:

  • all atomic, qualified or unqualified basic types;
  • all atomic, qualified or unqualified enumerated types;
  • all atomic, qualified or unqualified pointer types;
  • all array types whose element type has a fundamental alignment requirement;
  • all types specified in clause 7 as complete object types;
  • all structure or union types all of whose elements have types with fundamental alignment requirements and none of whose elements have an alignment specifier specifying an alignment that is not a fundamental alignment.

This interpretation (where max_align_t has the greatest alignment considered fundamental) is made clear in the definition of the type (section 7.19, paragraph 2), which says that max_align_t is "an object type whose alignment is the greatest fundamental alignment".



EDIT: note that, in frankie's example above, there is no bug, as the alignment for max_align_t is reported to be 8. Therefore, it's valid for malloc to return pointers aligned to 8 bytes. Likewise in the first example from John Z, where max_align_t has an alignment of 16 and malloc returns pointers aligned to 16 bytes. The second example from that post seems to exhibit the same bug as my test case on wine.
Title: Re: Misalignment of buffers allocated with malloc
Post by: frankie on July 18, 2022, 10:08:20 AM
With reference to the "fundamental alignment" (as defined in C17, section 7.22.3, paragraph 1), I think it should be considered that 128bits and 256bits objects aren't actually defined, so the 8byte alignment is suitable for all types defined in the standard and should be the effective one (at least until larger types, 128-256bits, will be introduced).
At least this is my personal interpretation, maybe Pelle could add points...

If this is the intention, then max_align_t should have an alignment requirement of 8. (And none of the types required to have a fundamental alignment should have a greater alignment requirement.) What is improper is for max_align_t to have a greater alignment requirement than what the implementation considers fundamental.
I understand your point. You're right.
This behavior differs from MS that keeps 8bytes alignment. But MS stay to C standards like a fish to its bike.
I can't say if it is correct or not. In this case Pelle's comment would be appropriate.
Title: Re: Misalignment of buffers allocated with malloc
Post by: TimoVJL on July 18, 2022, 06:56:54 PM
Win 7 OS system msvcrt.dll
32-bit
Code: [Select]
max_align_t alignment: 16
malloc: 00290E78
malloc: 00290E90
malloc: 00290EA8
malloc: 00290EC0
malloc: 00290ED8
malloc: 00290EF0
malloc: 00290F08
malloc: 00290F20
malloc: 00290F38
malloc: 00290F50
malloc: 00290F68
malloc: 00290F80
malloc: 00290F98
malloc: 00290FB0
malloc: 00290FC8
malloc: 00290FE0
malloc: 00290FF8
malloc: 00291010
malloc: 00291040
malloc: 00291058
64-bit
Code: [Select]
max_align_t alignment: 16
malloc: 000000000033FC90
malloc: 000000000033FCB0
malloc: 000000000033FCD0
malloc: 000000000033FCF0
malloc: 000000000033FD10
malloc: 000000000033FD30
malloc: 000000000033FD50
malloc: 000000000033FD70
malloc: 000000000033FD90
malloc: 000000000033FDB0
malloc: 000000000033FDD0
malloc: 000000000033FDF0
malloc: 000000000033FE10
malloc: 000000000033FE30
malloc: 000000000033FE50
malloc: 000000000033FE70
malloc: 000000000033FE90
malloc: 0000000000466EC0
malloc: 0000000000466EE0
malloc: 0000000000466F00