Skip to content

Commit df9ee49

Browse files
committed
fix: freebsd's xattr returns the number of bytes read
On linux, the xattr API returns the ERANGE if the result doesn't fit. On FreeBSD, it returns the number of bytes read. Previously, this wasn't an obvious issue because we always checked the size before we read the attribute. It was still incorrect, but would have required a race to trigger a bug. Now, it's a more pressing issue because we try reading once with a 4KiB buffer before we check the size. The fix is simple: make the target buffer 1 byte larger than it has to be and make sure we always read less than the size of the buffer we passed in.
1 parent 99b48d8 commit df9ee49

File tree

2 files changed

+17
-7
lines changed

2 files changed

+17
-7
lines changed

src/sys/bsd.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,18 @@ fn allocate_loop<F: Fn(*mut c_void, size_t) -> libc::ssize_t>(f: F) -> io::Resul
5656
|buf| unsafe {
5757
let (ptr, len) = slice_parts(buf);
5858
let new_len = cvt(f(ptr, len))?;
59-
assert!(
60-
new_len <= len,
61-
"OS returned length {new_len} greater than buffer size {len}"
62-
);
63-
Ok(slice::from_raw_parts_mut(ptr.cast(), new_len))
59+
if new_len < len {
60+
Ok(slice::from_raw_parts_mut(ptr.cast(), new_len))
61+
} else {
62+
// If the length of the value isn't strictly smaller than the length of the value
63+
// read, there may be more to read. Fake an ERANGE error so we can try again with a
64+
// bigger buffer.
65+
Err(io::Error::from_raw_os_error(crate::sys::ERANGE))
66+
}
6467
},
65-
|| cvt(f(ptr::null_mut(), 0)),
68+
// Estimate size + 1 because, on freebsd, the only way to tell if we've read the entire
69+
// value is read a value smaller than the buffer we passed.
70+
|| Ok(cvt(f(ptr::null_mut(), 0))? + 1),
6671
)
6772
}
6873

src/util.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,12 @@ where
4242
);
4343
vec.set_len(len);
4444
}
45-
vec.shrink_to_fit();
45+
// Only shrink to fit if we've over-allocated by MORE than one byte. Unfortunately,
46+
// on FreeBSD, we have to over-allocate by one byte to determine if we've read all
47+
// the attributes.
48+
if vec.capacity() > vec.len() + 1 {
49+
vec.shrink_to_fit();
50+
}
4651
return Ok(vec);
4752
}
4853
Err(e) if e.raw_os_error() != Some(crate::sys::ERANGE) => return Err(e),

0 commit comments

Comments
 (0)