Skip to content

Commit 83d82a5

Browse files
authored
swift_slowAlloc() assumes malloc(0) returns non-NULL. (swiftlang#86922)
`swift_slowAlloc()` and related functions assume that `malloc(0)` and `aligned_alloc(0)` return non-`NULL` pointers. The C standards allow implementations to return `NULL` when the allocation size is `0`. This PR implements a check for `0` that instead allocates `1`. The cost of the check is negligible next to the cost of actually allocating, but we'll mark it `SWIFT_UNLIKELY` just in case. Resolves rdar://169304909.
1 parent 4d89b9b commit 83d82a5

File tree

2 files changed

+35
-3
lines changed

2 files changed

+35
-3
lines changed

stdlib/public/runtime/Heap.cpp

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@ static size_t computeAlignment(size_t alignMask) {
8383
// The runtime may use either malloc or AlignedAlloc, and the standard library
8484
// must deallocate using an identical alignment.
8585
void *swift::swift_slowAlloc(size_t size, size_t alignMask) {
86+
if (SWIFT_UNLIKELY(size == 0)) {
87+
return swift_slowAlloc(1, alignMask);
88+
}
89+
8690
void *p;
8791
// This check also forces "default" alignment to use AlignedAlloc.
8892
if (alignMask <= MALLOC_ALIGN_MASK) {
@@ -91,12 +95,18 @@ void *swift::swift_slowAlloc(size_t size, size_t alignMask) {
9195
size_t alignment = computeAlignment(alignMask);
9296
p = AlignedAlloc(size, alignment);
9397
}
94-
if (!p) swift::swift_abortAllocationFailure(size, alignMask);
98+
if (SWIFT_UNLIKELY(!p)) {
99+
swift::swift_abortAllocationFailure(size, alignMask);
100+
}
95101
return p;
96102
}
97103

98104
void *swift::swift_slowAllocTyped(size_t size, size_t alignMask,
99105
MallocTypeId typeId) {
106+
if (SWIFT_UNLIKELY(size == 0)) {
107+
return swift_slowAllocTyped(1, alignMask, typeId);
108+
}
109+
100110
#if SWIFT_STDLIB_HAS_MALLOC_TYPE
101111
if (__builtin_available(macOS 15, iOS 17, tvOS 17, watchOS 10, *)) {
102112
void *p;
@@ -113,7 +123,9 @@ void *swift::swift_slowAllocTyped(size_t size, size_t alignMask,
113123
if (err != 0)
114124
p = nullptr;
115125
}
116-
if (!p) swift::swift_abortAllocationFailure(size, alignMask);
126+
if (SWIFT_UNLIKELY(!p)) {
127+
swift::swift_abortAllocationFailure(size, alignMask);
128+
}
117129
return p;
118130
}
119131
#endif
@@ -122,10 +134,16 @@ void *swift::swift_slowAllocTyped(size_t size, size_t alignMask,
122134

123135
void *swift::swift_coroFrameAlloc(size_t size,
124136
MallocTypeId typeId) {
137+
if (SWIFT_UNLIKELY(size == 0)) {
138+
return swift_coroFrameAlloc(1, typeId);
139+
}
140+
125141
#if SWIFT_STDLIB_HAS_MALLOC_TYPE
126142
if (__builtin_available(macOS 15, iOS 17, tvOS 17, watchOS 10, *)) {
127143
void *p = malloc_type_malloc(size, typeId);
128-
if (!p) swift::swift_abortAllocationFailure(size, 0);
144+
if (SWIFT_UNLIKELY(!p)) {
145+
swift::swift_abortAllocationFailure(size, 0);
146+
}
129147
return p;
130148
}
131149
#endif

test/stdlib/UnsafeRawPointer.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,4 +394,18 @@ UnsafeMutableRawPointerExtraTestSuite.test("pointer-comparisons") {
394394
expectTrue(a < b.assumingMemoryBound(to: Double.self))
395395
}
396396

397+
UnsafeMutableRawPointerExtraTestSuite.test("zero-allocation") {
398+
let a = UnsafeMutableRawPointer.allocate(byteCount: 0, alignment: 1)
399+
let b = UnsafeMutableRawPointer.allocate(byteCount: 0, alignment: 16)
400+
let c = UnsafeMutableRawPointer.allocate(byteCount: 0, alignment: 1024)
401+
defer {
402+
a.deallocate()
403+
b.deallocate()
404+
c.deallocate()
405+
}
406+
expectNotEqual(Int(bitPattern: a), 0x0)
407+
expectNotEqual(Int(bitPattern: b), 0x0)
408+
expectNotEqual(Int(bitPattern: c), 0x0)
409+
}
410+
397411
runAllTests()

0 commit comments

Comments
 (0)