| author | |
| committer | |
| log | 7ca86936caf7d206248ca6862f23ac25e4b1d327 |
| tree | 633573fb3d758a7c58c7e00b5731847e5c0e5f64 |
| parent | 5c765b2ec4921e64ddc78f8011bd716e73fb7802 |
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.
Revert "tests: disable i386-linux-gnu -lc target due to CI failures"
This reverts commit 97a2f4e7ae9c52c595841347bb0b26572b180dcf.3 files changed, 83 insertions(+), 19 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+64| ... | ... | @@ -2323,6 +2323,70 @@ 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, or `null` if one of the following | |
| 2328 | /// conditions is met: | |
| 2329 | /// - The aligned pointer would not fit the address space, | |
| 2330 | /// - The delta required to align the pointer is not a multiple of the pointee's | |
| 2331 | /// type. | |
| 2332 | pub fn alignPointerOffset(ptr: anytype, align_to: u29) ?usize { | |
| 2333 | assert(align_to != 0 and @popCount(u29, align_to) == 1); | |
| 2334 | ||
| 2335 | const T = @TypeOf(ptr); | |
| 2336 | const info = @typeInfo(T); | |
| 2337 | if (info != .Pointer or info.Pointer.size != .Many) | |
| 2338 | @compileError("expected many item pointer, got " ++ @typeName(T)); | |
| 2339 | ||
| 2340 | // Do nothing if the pointer is already well-aligned. | |
| 2341 | if (align_to <= info.Pointer.alignment) | |
| 2342 | return 0; | |
| 2343 | ||
| 2344 | // Calculate the aligned base address with an eye out for overflow. | |
| 2345 | const addr = @ptrToInt(ptr); | |
| 2346 | var new_addr: usize = undefined; | |
| 2347 | if (@addWithOverflow(usize, addr, align_to - 1, &new_addr)) return null; | |
| 2348 | new_addr &= ~@as(usize, align_to - 1); | |
| 2349 | ||
| 2350 | // The delta is expressed in terms of bytes, turn it into a number of child | |
| 2351 | // type elements. | |
| 2352 | const delta = new_addr - addr; | |
| 2353 | const pointee_size = @sizeOf(info.Pointer.child); | |
| 2354 | if (delta % pointee_size != 0) return null; | |
| 2355 | return delta / pointee_size; | |
| 2356 | } | |
| 2357 | ||
| 2358 | /// Aligns a given pointer value to a specified alignment factor. | |
| 2359 | /// Returns an aligned pointer or null if one of the following conditions is | |
| 2360 | /// met: | |
| 2361 | /// - The aligned pointer would not fit the address space, | |
| 2362 | /// - The delta required to align the pointer is not a multiple of the pointee's | |
| 2363 | /// type. | |
| 2364 | pub fn alignPointer(ptr: anytype, align_to: u29) ?@TypeOf(ptr) { | |
| 2365 | const adjust_off = alignPointerOffset(ptr, align_to) orelse return null; | |
| 2366 | const T = @TypeOf(ptr); | |
| 2367 | // Avoid the use of intToPtr to avoid losing the pointer provenance info. | |
| 2368 | return @alignCast(@typeInfo(T).Pointer.alignment, ptr + adjust_off); | |
| 2369 | } | |
| 2370 | ||
| 2371 | test "alignPointer" { | |
| 2372 | const S = struct { | |
| 2373 | fn checkAlign(comptime T: type, base: usize, align_to: u29, expected: usize) !void { | |
| 2374 | var ptr = @intToPtr(T, base); | |
| 2375 | var aligned = alignPointer(ptr, align_to); | |
| 2376 | try testing.expectEqual(expected, @ptrToInt(aligned)); | |
| 2377 | } | |
| 2378 | }; | |
| 2379 | ||
| 2380 | try S.checkAlign([*]u8, 0x123, 0x200, 0x200); | |
| 2381 | try S.checkAlign([*]align(4) u8, 0x10, 2, 0x10); | |
| 2382 | try S.checkAlign([*]u32, 0x10, 2, 0x10); | |
| 2383 | try S.checkAlign([*]u32, 0x4, 16, 0x10); | |
| 2384 | // Misaligned. | |
| 2385 | try S.checkAlign([*]align(1) u32, 0x3, 2, 0); | |
| 2386 | // Overflow. | |
| 2387 | try S.checkAlign([*]u32, math.maxInt(usize) - 3, 8, 0); | |
| 2388 | } | |
| 2389 | ||
| 2326 | 2390 | fn CopyPtrAttrs(comptime source: type, comptime size: std.builtin.TypeInfo.Pointer.Size, comptime child: type) type { |
| 2327 | 2391 | const info = @typeInfo(source).Pointer; |
| 2328 | 2392 | return @Type(.{ |
test/tests.zig+8-9| ... | ... | @@ -98,15 +98,14 @@ const test_targets = blk: { |
| 98 | 98 | }, |
| 99 | 99 | .link_libc = true, |
| 100 | 100 | }, |
| 101 | // https://github.com/ziglang/zig/issues/8930 | |
| 102 | //TestTarget{ | |
| 103 | // .target = .{ | |
| 104 | // .cpu_arch = .i386, | |
| 105 | // .os_tag = .linux, | |
| 106 | // .abi = .gnu, | |
| 107 | // }, | |
| 108 | // .link_libc = true, | |
| 109 | //}, | |
| 101 | TestTarget{ | |
| 102 | .target = .{ | |
| 103 | .cpu_arch = .i386, | |
| 104 | .os_tag = .linux, | |
| 105 | .abi = .gnu, | |
| 106 | }, | |
| 107 | .link_libc = true, | |
| 108 | }, | |
| 110 | 109 | |
| 111 | 110 | TestTarget{ |
| 112 | 111 | .target = .{ |