authorgravatar for thatlemon@gmail.comLemonBoy <thatlemon@gmail.com> 2021-06-06 11:12:53+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-06-06 18:04:16-07:00
log0c091f8b096051687a6d7f563d743387f6b39a86
treea7ac1b80a1ccb22915c1a99cd389b31596c060d9
parent616c82e446625478179b873b37b05fddfc53ed01

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.

2 files changed, 76 insertions(+), 10 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+65
......@@ -2323,6 +2323,71 @@ 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.
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.
2333pub 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.
2365pub 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
2372test "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
23262391fn CopyPtrAttrs(comptime source: type, comptime size: std.builtin.TypeInfo.Pointer.Size, comptime child: type) type {
23272392 const info = @typeInfo(source).Pointer;
23282393 return @Type(.{