authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-10-01 14:30:00-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-10-29 06:20:48-07:00
log3b80fde6f42f104278d4102562dd2b714aafe877
tree59787851007bd08b6d16bde028e667e6a67c4d91
parentcde5a51d0ca26b8274f0208cfae88e548385fe3b

std.os.linux: remove sendmmsg workaround

This "fix" is too opinionated to belong here. Better instead to document the pitfalls.

1 files changed, 9 insertions(+), 42 deletions(-)

lib/std/os/linux.zig+9-42
...@@ -1,10 +1,8 @@...@@ -1,10 +1,8 @@
1//! This file provides the system interface functions for Linux matching those1//! This file provides the system interface functions for Linux matching those
2//! that are provided by libc, whether or not libc is linked. The following2//! that are provided by libc, whether or not libc is linked. The following
3//! abstractions are made:3//! abstractions are made:
4//! * Work around kernel bugs and limitations. For example, see sendmmsg.
5//! * Implement all the syscalls in the same way that libc functions will4//! * Implement all the syscalls in the same way that libc functions will
6//! provide `rename` when only the `renameat` syscall exists.5//! provide `rename` when only the `renameat` syscall exists.
7//! * Does not support POSIX thread cancellation.
8const std = @import("../std.zig");6const std = @import("../std.zig");
9const builtin = @import("builtin");7const builtin = @import("builtin");
10const assert = std.debug.assert;8const assert = std.debug.assert;
...@@ -1836,7 +1834,7 @@ pub fn seteuid(euid: uid_t) usize {...@@ -1836,7 +1834,7 @@ pub fn seteuid(euid: uid_t) usize {
1836 // id will not be changed. Since uid_t is unsigned, this wraps around to the1834 // id will not be changed. Since uid_t is unsigned, this wraps around to the
1837 // max value in C.1835 // max value in C.
1838 comptime assert(@typeInfo(uid_t) == .int and @typeInfo(uid_t).int.signedness == .unsigned);1836 comptime assert(@typeInfo(uid_t) == .int and @typeInfo(uid_t).int.signedness == .unsigned);
1839 return setresuid(std.math.maxInt(uid_t), euid, std.math.maxInt(uid_t));1837 return setresuid(maxInt(uid_t), euid, maxInt(uid_t));
1840}1838}
18411839
1842pub fn setegid(egid: gid_t) usize {1840pub fn setegid(egid: gid_t) usize {
...@@ -1847,7 +1845,7 @@ pub fn setegid(egid: gid_t) usize {...@@ -1847,7 +1845,7 @@ pub fn setegid(egid: gid_t) usize {
1847 // id will not be changed. Since gid_t is unsigned, this wraps around to the1845 // id will not be changed. Since gid_t is unsigned, this wraps around to the
1848 // max value in C.1846 // max value in C.
1849 comptime assert(@typeInfo(uid_t) == .int and @typeInfo(uid_t).int.signedness == .unsigned);1847 comptime assert(@typeInfo(uid_t) == .int and @typeInfo(uid_t).int.signedness == .unsigned);
1850 return setresgid(std.math.maxInt(gid_t), egid, std.math.maxInt(gid_t));1848 return setresgid(maxInt(gid_t), egid, maxInt(gid_t));
1851}1849}
18521850
1853pub fn getresuid(ruid: *uid_t, euid: *uid_t, suid: *uid_t) usize {1851pub fn getresuid(ruid: *uid_t, euid: *uid_t, suid: *uid_t) usize {
...@@ -2081,44 +2079,13 @@ pub fn sendmsg(fd: i32, msg: *const msghdr_const, flags: u32) usize {...@@ -2081,44 +2079,13 @@ pub fn sendmsg(fd: i32, msg: *const msghdr_const, flags: u32) usize {
2081 }2079 }
2082}2080}
20832081
2082/// Warning: libc is defined to have incompatible integer types with the
2083/// corresponding kernel data structures for this syscall.
2084///
2085/// Warning: on 64-bit systems, if any message length would exceed `maxInt(i32)`,
2086/// number of bytes sent cannot be determined, because the kernel uses `ssize_t`
2087/// for `sendmsg` return value but `int` for the corresponding values here.
2084pub fn sendmmsg(fd: i32, msgvec: [*]mmsghdr_const, vlen: u32, flags: u32) usize {2088pub fn sendmmsg(fd: i32, msgvec: [*]mmsghdr_const, vlen: u32, flags: u32) usize {
2085 if (@typeInfo(usize).int.bits > @typeInfo(@typeInfo(mmsghdr).@"struct".fields[1].type).int.bits) {
2086 // workaround kernel brokenness:
2087 // if adding up all iov_len overflows a i32 then split into multiple calls
2088 // see https://www.openwall.com/lists/musl/2014/06/07/5
2089 const kvlen = if (vlen > IOV_MAX) IOV_MAX else vlen; // matches kernel
2090 var next_unsent: usize = 0;
2091 for (msgvec[0..kvlen], 0..) |*msg, i| {
2092 var size: i32 = 0;
2093 const msg_iovlen = @as(usize, @intCast(msg.hdr.iovlen)); // kernel side this is treated as unsigned
2094 for (msg.hdr.iov[0..msg_iovlen]) |iov| {
2095 if (iov.len > std.math.maxInt(i32) or @addWithOverflow(size, @as(i32, @intCast(iov.len)))[1] != 0) {
2096 // batch-send all messages up to the current message
2097 if (next_unsent < i) {
2098 const batch_size = i - next_unsent;
2099 const r = syscall4(.sendmmsg, @as(usize, @bitCast(@as(isize, fd))), @intFromPtr(&msgvec[next_unsent]), batch_size, flags);
2100 if (E.init(r) != .SUCCESS) return next_unsent;
2101 if (r < batch_size) return next_unsent + r;
2102 }
2103 // send current message as own packet
2104 const r = sendmsg(fd, &msg.hdr, flags);
2105 if (E.init(r) != .SUCCESS) return r;
2106 // Linux limits the total bytes sent by sendmsg to INT_MAX, so this cast is safe.
2107 msg.len = @as(u32, @intCast(r));
2108 next_unsent = i + 1;
2109 break;
2110 }
2111 size += @intCast(iov.len);
2112 }
2113 }
2114 if (next_unsent < kvlen or next_unsent == 0) { // want to make sure at least one syscall occurs (e.g. to trigger MSG.EOR)
2115 const batch_size = kvlen - next_unsent;
2116 const r = syscall4(.sendmmsg, @as(usize, @bitCast(@as(isize, fd))), @intFromPtr(&msgvec[next_unsent]), batch_size, flags);
2117 if (E.init(r) != .SUCCESS) return r;
2118 return next_unsent + r;
2119 }
2120 return kvlen;
2121 }
2122 return syscall4(.sendmmsg, @as(usize, @bitCast(@as(isize, fd))), @intFromPtr(msgvec), vlen, flags);2089 return syscall4(.sendmmsg, @as(usize, @bitCast(@as(isize, fd))), @intFromPtr(msgvec), vlen, flags);
2123}2090}
21242091
...@@ -8700,7 +8667,7 @@ pub const PR = enum(i32) {...@@ -8700,7 +8667,7 @@ pub const PR = enum(i32) {
8700 pub const SET_MM_MAP = 14;8667 pub const SET_MM_MAP = 14;
8701 pub const SET_MM_MAP_SIZE = 15;8668 pub const SET_MM_MAP_SIZE = 15;
87028669
8703 pub const SET_PTRACER_ANY = std.math.maxInt(c_ulong);8670 pub const SET_PTRACER_ANY = maxInt(c_ulong);
87048671
8705 pub const FP_MODE_FR = 1 << 0;8672 pub const FP_MODE_FR = 1 << 0;
8706 pub const FP_MODE_FRE = 1 << 1;8673 pub const FP_MODE_FRE = 1 << 1;