Skip to content

Commit

Permalink
Fix p{read,write}v{,v2}'s encoding of the offset argument on Linux.
Browse files Browse the repository at this point in the history
Unlike with `p{read,write}`, Linux's `p{read,write}v` syscall's offset
argument is not passed in an endian-specific order. And, the expectation is
for syscall wrappers to always pass both the high and low halves of the
offset as separate arguments, even though on 64-bit architectures the low
half is passed throgh as a 64-bit value containing the full offset and the
kernel doesn't mask it.

And `p{read,write}v2` follow the behavior of `p{read,write}`.
  • Loading branch information
sunfishcode committed Oct 25, 2023
1 parent 0b7ccf4 commit f749983
Show file tree
Hide file tree
Showing 4 changed files with 248 additions and 227 deletions.
269 changes: 98 additions & 171 deletions src/backend/libc/c.rs
Expand Up @@ -224,25 +224,6 @@ pub(super) unsafe fn prlimit(
prlimit64(pid, resource, new_limit, old_limit)
}

// 64-bit offsets on 32-bit platforms are passed in endianness-specific
// lo/hi pairs. See src/backend/linux_raw/conv.rs for details.
#[cfg(all(linux_kernel, target_endian = "little", target_pointer_width = "32"))]
fn lo(x: i64) -> usize {
(x >> 32) as usize
}
#[cfg(all(linux_kernel, target_endian = "little", target_pointer_width = "32"))]
fn hi(x: i64) -> usize {
x as usize
}
#[cfg(all(linux_kernel, target_endian = "big", target_pointer_width = "32"))]
fn lo(x: i64) -> usize {
x as usize
}
#[cfg(all(linux_kernel, target_endian = "big", target_pointer_width = "32"))]
fn hi(x: i64) -> usize {
(x >> 32) as usize
}

#[cfg(target_os = "android")]
mod readwrite_pv64 {
use super::*;
Expand All @@ -263,31 +244,18 @@ mod readwrite_pv64 {
if let Some(fun) = preadv64.get() {
fun(fd, iov, iovcnt, offset)
} else {
#[cfg(target_pointer_width = "32")]
{
syscall! {
fn preadv(
fd: libc::c_int,
iov: *const libc::iovec,
iovcnt: libc::c_int,
offset_hi: usize,
offset_lo: usize
) via SYS_preadv -> libc::ssize_t
}
preadv(fd, iov, iovcnt, hi(offset), lo(offset))
}
#[cfg(target_pointer_width = "64")]
{
syscall! {
fn preadv(
fd: libc::c_int,
iov: *const libc::iovec,
iovcnt: libc::c_int,
offset: libc::off_t
) via SYS_preadv -> libc::ssize_t
}
preadv(fd, iov, iovcnt, offset)
// Unlike the plain "p" functions, the "pv" functions pass their
// offset in an endian-independent way, and always in two registers.
syscall! {
fn preadv(
fd: libc::c_int,
iov: *const libc::iovec,
iovcnt: libc::c_int,
offset_lo: usize,
offset_hi: usize
) via SYS_preadv -> libc::ssize_t
}
preadv(fd, iov, iovcnt, offset as usize, (offset >> 32) as usize)
}
}
pub(in super::super) unsafe fn pwritev64(
Expand All @@ -303,31 +271,18 @@ mod readwrite_pv64 {
if let Some(fun) = pwritev64.get() {
fun(fd, iov, iovcnt, offset)
} else {
#[cfg(target_pointer_width = "32")]
{
syscall! {
fn pwritev(
fd: libc::c_int,
iov: *const libc::iovec,
iovcnt: libc::c_int,
offset_hi: usize,
offset_lo: usize
) via SYS_pwritev -> libc::ssize_t
}
pwritev(fd, iov, iovcnt, hi(offset), lo(offset))
}
#[cfg(target_pointer_width = "64")]
{
syscall! {
fn pwritev(
fd: libc::c_int,
iov: *const libc::iovec,
iovcnt: libc::c_int,
offset: libc::off_t
) via SYS_pwritev -> libc::ssize_t
}
pwritev(fd, iov, iovcnt, offset)
// Unlike the plain "p" functions, the "pv" functions pass their
// offset in an endian-independent way, and always in two registers.
syscall! {
fn pwritev(
fd: libc::c_int,
iov: *const libc::iovec,
iovcnt: libc::c_int,
offset_lo: usize,
offset_hi: usize
) via SYS_pwritev -> libc::ssize_t
}
pwritev(fd, iov, iovcnt, offset as usize, (offset >> 32) as usize)
}
}
}
Expand Down Expand Up @@ -378,33 +333,26 @@ mod readwrite_pv64v2 {
if let Some(fun) = preadv64v2.get() {
fun(fd, iov, iovcnt, offset, flags)
} else {
#[cfg(target_pointer_width = "32")]
{
syscall! {
fn preadv2(
fd: libc::c_int,
iov: *const libc::iovec,
iovcnt: libc::c_int,
offset_hi: usize,
offset_lo: usize,
flags: libc::c_int
) via SYS_preadv2 -> libc::ssize_t
}
preadv2(fd, iov, iovcnt, hi(offset), lo(offset), flags)
}
#[cfg(target_pointer_width = "64")]
{
syscall! {
fn preadv2(
fd: libc::c_int,
iov: *const libc::iovec,
iovcnt: libc::c_int,
offset: libc::off_t,
flags: libc::c_int
) via SYS_preadv2 -> libc::ssize_t
}
preadv2(fd, iov, iovcnt, offset, flags)
// Unlike the plain "p" functions, the "pv" functions pass their
// offset in an endian-independent way, and always in two registers.
syscall! {
fn preadv2(
fd: libc::c_int,
iov: *const libc::iovec,
iovcnt: libc::c_int,
offset_lo: usize,
offset_hi: usize,
flags: libc::c_int
) via SYS_preadv2 -> libc::ssize_t
}
preadv2(
fd,
iov,
iovcnt,
offset as usize,
(offset >> 32) as usize,
flags,
)
}
}
pub(in super::super) unsafe fn pwritev64v2(
Expand All @@ -421,33 +369,26 @@ mod readwrite_pv64v2 {
if let Some(fun) = pwritev64v2.get() {
fun(fd, iov, iovcnt, offset, flags)
} else {
#[cfg(target_pointer_width = "32")]
{
syscall! {
fn pwritev2(
fd: libc::c_int,
iov: *const libc::iovec,
iovec: libc::c_int,
offset_hi: usize,
offset_lo: usize,
flags: libc::c_int
) via SYS_pwritev2 -> libc::ssize_t
}
pwritev2(fd, iov, iovcnt, hi(offset), lo(offset), flags)
}
#[cfg(target_pointer_width = "64")]
{
syscall! {
fn pwritev2(
fd: libc::c_int,
iov:*const libc::iovec,
iovcnt: libc::c_int,
offset: libc::off_t,
flags: libc::c_int
) via SYS_pwritev2 -> libc::ssize_t
}
pwritev2(fd, iov, iovcnt, offset, flags)
// Unlike the plain "p" functions, the "pv" functions pass their
// offset in an endian-independent way, and always in two registers.
syscall! {
fn pwritev2(
fd: libc::c_int,
iov: *const libc::iovec,
iovec: libc::c_int,
offset_lo: usize,
offset_hi: usize,
flags: libc::c_int
) via SYS_pwritev2 -> libc::ssize_t
}
pwritev2(
fd,
iov,
iovcnt,
offset as usize,
(offset >> 32) as usize,
flags,
)
}
}
}
Expand All @@ -470,33 +411,26 @@ mod readwrite_pv64v2 {
offset: libc::off64_t,
flags: libc::c_int,
) -> libc::ssize_t {
#[cfg(target_pointer_width = "32")]
{
syscall! {
fn preadv2(
fd: libc::c_int,
iov: *const libc::iovec,
iovcnt: libc::c_int,
offset_hi: usize,
offset_lo: usize,
flags: libc::c_int
) via SYS_preadv2 -> libc::ssize_t
}
preadv2(fd, iov, iovcnt, hi(offset), lo(offset), flags)
}
#[cfg(target_pointer_width = "64")]
{
syscall! {
fn preadv2(
fd: libc::c_int,
iov: *const libc::iovec,
iovcnt: libc::c_int,
offset: libc::off_t,
flags: libc::c_int
) via SYS_preadv2 -> libc::ssize_t
}
preadv2(fd, iov, iovcnt, offset, flags)
// Unlike the plain "p" functions, the "pv" functions pass their offset
// in an endian-independent way, and always in two registers.
syscall! {
fn preadv2(
fd: libc::c_int,
iov: *const libc::iovec,
iovcnt: libc::c_int,
offset_lo: usize,
offset_hi: usize,
flags: libc::c_int
) via SYS_preadv2 -> libc::ssize_t
}
preadv2(
fd,
iov,
iovcnt,
offset as usize,
(offset >> 32) as usize,
flags,
)
}
pub(in super::super) unsafe fn pwritev64v2(
fd: libc::c_int,
Expand All @@ -505,33 +439,26 @@ mod readwrite_pv64v2 {
offset: libc::off64_t,
flags: libc::c_int,
) -> libc::ssize_t {
#[cfg(target_pointer_width = "32")]
{
syscall! {
fn pwritev2(
fd: libc::c_int,
iov: *const libc::iovec,
iovcnt: libc::c_int,
offset_hi: usize,
offset_lo: usize,
flags: libc::c_int
) via SYS_pwritev2 -> libc::ssize_t
}
pwritev2(fd, iov, iovcnt, hi(offset), lo(offset), flags)
}
#[cfg(target_pointer_width = "64")]
{
syscall! {
fn pwritev2(
fd: libc::c_int,
iov:*const libc::iovec,
iovcnt: libc::c_int,
offset: libc::off_t,
flags: libc::c_int
) via SYS_pwritev2 -> libc::ssize_t
}
pwritev2(fd, iov, iovcnt, offset, flags)
// Unlike the plain "p" functions, the "pv" functions pass their offset
// in an endian-independent way, and always in two registers.
syscall! {
fn pwritev2(
fd: libc::c_int,
iov: *const libc::iovec,
iovcnt: libc::c_int,
offset_lo: usize,
offset_hi: usize,
flags: libc::c_int
) via SYS_pwritev2 -> libc::ssize_t
}
pwritev2(
fd,
iov,
iovcnt,
offset as usize,
(offset >> 32) as usize,
flags,
)
}
}
#[cfg(any(
Expand Down

0 comments on commit f749983

Please sign in to comment.