authorgravatar for thatlemon@gmail.comLemonBoy <thatlemon@gmail.com> 2021-06-06 11:12:41+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-06-06 18:07:47-07:00
log7ca86936caf7d206248ca6862f23ac25e4b1d327
tree633573fb3d758a7c58c7e00b5731847e5c0e5f64
parent5c765b2ec4921e64ddc78f8011bd716e73fb7802

std: Add helpers to safely align pointers

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 {
247247 ) catch return error.OutOfMemory;
248248
249249 // If the allocation is sufficiently aligned, use it.
250 if (@ptrToInt(addr) & (alignment - 1) == 0) {
250 if (mem.isAligned(@ptrToInt(addr), alignment)) {
251251 return @ptrCast([*]u8, addr)[0..alignPageAllocLen(aligned_len, n, len_align)];
252252 }
253253
......@@ -300,13 +300,13 @@ const PageAllocator = struct {
300300 ) catch return error.OutOfMemory;
301301 assert(mem.isAligned(@ptrToInt(slice.ptr), mem.page_size));
302302
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;
305305
306306 // Unmap the extra bytes that were only requested in order to guarantee
307307 // that the range of memory we were provided had a proper alignment in
308308 // 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);
310310 if (drop_len != 0) {
311311 os.munmap(slice[0..drop_len]);
312312 }
......@@ -372,7 +372,7 @@ const PageAllocator = struct {
372372 return alignPageAllocLen(new_size_aligned, new_size, len_align);
373373
374374 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);
376376 // TODO: if the next_mmap_addr_hint is within the unmapped range, update it
377377 os.munmap(ptr[0 .. buf_aligned_len - new_size_aligned]);
378378 if (new_size_aligned == 0)
......@@ -692,8 +692,9 @@ pub const FixedBufferAllocator = struct {
692692
693693 fn alloc(allocator: *Allocator, n: usize, ptr_align: u29, len_align: u29, ra: usize) ![]u8 {
694694 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;
697698 const new_end_index = adjusted_index + n;
698699 if (new_end_index > self.buffer.len) {
699700 return error.OutOfMemory;
......@@ -765,9 +766,9 @@ pub const ThreadSafeFixedBufferAllocator = blk: {
765766 const self = @fieldParentPtr(ThreadSafeFixedBufferAllocator, "allocator", allocator);
766767 var end_index = @atomicLoad(usize, &self.end_index, builtin.AtomicOrder.SeqCst);
767768 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;
771772 const new_end_index = adjusted_index + n;
772773 if (new_end_index > self.buffer.len) {
773774 return error.OutOfMemory;
lib/std/mem.zig+64
......@@ -2323,6 +2323,70 @@ pub fn nativeToBig(comptime T: type, x: T) T {
23232323 };
23242324}
23252325
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.
2332pub 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.
2364pub 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
2371test "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
23262390fn CopyPtrAttrs(comptime source: type, comptime size: std.builtin.TypeInfo.Pointer.Size, comptime child: type) type {
23272391 const info = @typeInfo(source).Pointer;
23282392 return @Type(.{
test/tests.zig+8-9
......@@ -98,15 +98,14 @@ const test_targets = blk: {
9898 },
9999 .link_libc = true,
100100 },
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 },
110109
111110 TestTarget{
112111 .target = .{