| author | |
| committer | |
| log | 0c091f8b096051687a6d7f563d743387f6b39a86 |
| tree | a7ac1b80a1ccb22915c1a99cd389b31596c060d9 |
| parent | 616c82e446625478179b873b37b05fddfc53ed01 |
Add two helpers to ensure people won't ignore some edge cases such as
pointers overflowing the address space.
Also fix #8924 to some degree, the amount of unchecked alignForward is
still scary.2 files changed, 76 insertions(+), 10 deletions(-)
lib/std/heap.zig+11-10| ... | ... | @@ -247,7 +247,7 @@ const PageAllocator = struct { |
| 247 | 247 | ) catch return error.OutOfMemory; |
| 248 | 248 | |
| 249 | 249 | // If the allocation is sufficiently aligned, use it. |
| 250 | if (@ptrToInt(addr) & (alignment - 1) == 0) { | |
| 250 | if (mem.isAligned(@ptrToInt(addr), alignment)) { | |
| 251 | 251 | return @ptrCast([*]u8, addr)[0..alignPageAllocLen(aligned_len, n, len_align)]; |
| 252 | 252 | } |
| 253 | 253 | |
| ... | ... | @@ -300,13 +300,13 @@ const PageAllocator = struct { |
| 300 | 300 | ) catch return error.OutOfMemory; |
| 301 | 301 | assert(mem.isAligned(@ptrToInt(slice.ptr), mem.page_size)); |
| 302 | 302 | |
| 303 | const aligned_addr = mem.alignForward(@ptrToInt(slice.ptr), alignment); | |
| 304 | const result_ptr = @alignCast(mem.page_size, @intToPtr([*]u8, aligned_addr)); | |
| 303 | const result_ptr = mem.alignPointer(slice.ptr, alignment) orelse | |
| 304 | return error.OutOfMemory; | |
| 305 | 305 | |
| 306 | 306 | // Unmap the extra bytes that were only requested in order to guarantee |
| 307 | 307 | // that the range of memory we were provided had a proper alignment in |
| 308 | 308 | // it somewhere. The extra bytes could be at the beginning, or end, or both. |
| 309 | const drop_len = aligned_addr - @ptrToInt(slice.ptr); | |
| 309 | const drop_len = @ptrToInt(result_ptr) - @ptrToInt(slice.ptr); | |
| 310 | 310 | if (drop_len != 0) { |
| 311 | 311 | os.munmap(slice[0..drop_len]); |
| 312 | 312 | } |
| ... | ... | @@ -372,7 +372,7 @@ const PageAllocator = struct { |
| 372 | 372 | return alignPageAllocLen(new_size_aligned, new_size, len_align); |
| 373 | 373 | |
| 374 | 374 | if (new_size_aligned < buf_aligned_len) { |
| 375 | const ptr = @intToPtr([*]align(mem.page_size) u8, @ptrToInt(buf_unaligned.ptr) + new_size_aligned); | |
| 375 | const ptr = @alignCast(mem.page_size, buf_unaligned.ptr + new_size_aligned); | |
| 376 | 376 | // TODO: if the next_mmap_addr_hint is within the unmapped range, update it |
| 377 | 377 | os.munmap(ptr[0 .. buf_aligned_len - new_size_aligned]); |
| 378 | 378 | if (new_size_aligned == 0) |
| ... | ... | @@ -692,8 +692,9 @@ pub const FixedBufferAllocator = struct { |
| 692 | 692 | |
| 693 | 693 | fn alloc(allocator: *Allocator, n: usize, ptr_align: u29, len_align: u29, ra: usize) ![]u8 { |
| 694 | 694 | const self = @fieldParentPtr(FixedBufferAllocator, "allocator", allocator); |
| 695 | const aligned_addr = mem.alignForward(@ptrToInt(self.buffer.ptr) + self.end_index, ptr_align); | |
| 696 | const adjusted_index = aligned_addr - @ptrToInt(self.buffer.ptr); | |
| 695 | const adjust_off = mem.alignPointerOffset(self.buffer.ptr + self.end_index, ptr_align) orelse | |
| 696 | return error.OutOfMemory; | |
| 697 | const adjusted_index = self.end_index + adjust_off; | |
| 697 | 698 | const new_end_index = adjusted_index + n; |
| 698 | 699 | if (new_end_index > self.buffer.len) { |
| 699 | 700 | return error.OutOfMemory; |
| ... | ... | @@ -765,9 +766,9 @@ pub const ThreadSafeFixedBufferAllocator = blk: { |
| 765 | 766 | const self = @fieldParentPtr(ThreadSafeFixedBufferAllocator, "allocator", allocator); |
| 766 | 767 | var end_index = @atomicLoad(usize, &self.end_index, builtin.AtomicOrder.SeqCst); |
| 767 | 768 | while (true) { |
| 768 | const addr = @ptrToInt(self.buffer.ptr) + end_index; | |
| 769 | const adjusted_addr = mem.alignForward(addr, ptr_align); | |
| 770 | const adjusted_index = end_index + (adjusted_addr - addr); | |
| 769 | const adjust_off = mem.alignPointerOffset(self.buffer.ptr + end_index, ptr_align) orelse | |
| 770 | return error.OutOfMemory; | |
| 771 | const adjusted_index = end_index + adjust_off; | |
| 771 | 772 | const new_end_index = adjusted_index + n; |
| 772 | 773 | if (new_end_index > self.buffer.len) { |
| 773 | 774 | return error.OutOfMemory; |
lib/std/mem.zig+65| ... | ... | @@ -2323,6 +2323,71 @@ pub fn nativeToBig(comptime T: type, x: T) T { |
| 2323 | 2323 | }; |
| 2324 | 2324 | } |
| 2325 | 2325 | |
| 2326 | /// Returns the number of elements that, if added to the given pointer, align it | |
| 2327 | /// to a multiple of the given quantity. | |
| 2328 | /// Returns an aligned pointer or null if one of the following conditions is | |
| 2329 | /// met: | |
| 2330 | /// - The aligned pointer would not fit the address space, | |
| 2331 | /// - The delta required to align the pointer is not a multiple of the pointee's | |
| 2332 | /// type. | |
| 2333 | pub fn alignPointerOffset(ptr: anytype, align_to: u29) ?usize { | |
| 2334 | assert(align_to != 0 and @popCount(u29, align_to) == 1); | |
| 2335 | ||
| 2336 | const T = @TypeOf(ptr); | |
| 2337 | const info = @typeInfo(T); | |
| 2338 | if (info != .Pointer or info.Pointer.size != .Many) | |
| 2339 | @compileError("expected many item pointer, got " ++ @typeName(T)); | |
| 2340 | ||
| 2341 | // Do nothing if the pointer is already well-aligned. | |
| 2342 | if (align_to <= info.Pointer.alignment) | |
| 2343 | return 0; | |
| 2344 | ||
| 2345 | // Calculate the aligned base address with an eye out for overflow. | |
| 2346 | const addr = @ptrToInt(ptr); | |
| 2347 | var new_addr: usize = undefined; | |
| 2348 | if (@addWithOverflow(usize, addr, align_to - 1, &new_addr)) return null; | |
| 2349 | new_addr &= ~@as(usize, align_to - 1); | |
| 2350 | ||
| 2351 | // The delta is expressed in terms of bytes, turn it into a number of child | |
| 2352 | // type elements. | |
| 2353 | const delta = new_addr - addr; | |
| 2354 | const pointee_size = @sizeOf(info.Pointer.child); | |
| 2355 | if (delta % pointee_size != 0) return null; | |
| 2356 | return delta / pointee_size; | |
| 2357 | } | |
| 2358 | ||
| 2359 | /// Aligns a given pointer value to a specified alignment factor. | |
| 2360 | /// Returns an aligned pointer or null if one of the following conditions is | |
| 2361 | /// met: | |
| 2362 | /// - The aligned pointer would not fit the address space, | |
| 2363 | /// - The delta required to align the pointer is not a multiple of the pointee's | |
| 2364 | /// type. | |
| 2365 | pub fn alignPointer(ptr: anytype, align_to: u29) ?@TypeOf(ptr) { | |
| 2366 | const adjust_off = alignPointerOffset(ptr, align_to) orelse return null; | |
| 2367 | const T = @TypeOf(ptr); | |
| 2368 | // Avoid the use of intToPtr to avoid losing the pointer provenance info. | |
| 2369 | return @alignCast(@typeInfo(T).Pointer.alignment, ptr + adjust_off); | |
| 2370 | } | |
| 2371 | ||
| 2372 | test "alignPointer" { | |
| 2373 | const S = struct { | |
| 2374 | fn checkAlign(comptime T: type, base: usize, align_to: u29, expected: usize) !void { | |
| 2375 | var ptr = @intToPtr(T, base); | |
| 2376 | var aligned = alignPointer(ptr, align_to); | |
| 2377 | try testing.expectEqual(expected, @ptrToInt(aligned)); | |
| 2378 | } | |
| 2379 | }; | |
| 2380 | ||
| 2381 | try S.checkAlign([*]u8, 0x123, 0x200, 0x200); | |
| 2382 | try S.checkAlign([*]align(4) u8, 0x10, 2, 0x10); | |
| 2383 | try S.checkAlign([*]u32, 0x10, 2, 0x10); | |
| 2384 | try S.checkAlign([*]u32, 0x4, 16, 0x10); | |
| 2385 | // Misaligned. | |
| 2386 | try S.checkAlign([*]align(1) u32, 0x3, 2, 0); | |
| 2387 | // Overflow. | |
| 2388 | try S.checkAlign([*]u32, math.maxInt(usize) - 3, 8, 0); | |
| 2389 | } | |
| 2390 | ||
| 2326 | 2391 | fn CopyPtrAttrs(comptime source: type, comptime size: std.builtin.TypeInfo.Pointer.Size, comptime child: type) type { |
| 2327 | 2392 | const info = @typeInfo(source).Pointer; |
| 2328 | 2393 | return @Type(.{ |