axeld + bonefish:
* Implemented automatic syscall restarts:
- A syscall can indicate that it has been interrupted and can be
restarted by setting a respective bit in thread::flags. It can
store parameters it wants to be preserved for the restart in
thread::syscall_restart::parameters. Another thread::flags bit
indicates whether it has been restarted.
- handle_signals() clears the restart flag, if the handled signal
has a handler function installed and SA_RESTART is not set. Another
thread flag (THREAD_FLAGS_DONT_RESTART_SYSCALL) can prevent syscalls
from being restarted, even if they could be (not used yet, but we
might want to use it in resume_thread(), so that we stay
behaviorally compatible with BeOS).
- The architecture specific syscall handler restarts the syscall, if
the restart flag is set. Implemented for x86 only.
- Added some support functions in the private <syscall_restart.h> to
simplify the syscall restart code in the syscalls.
- Adjusted all syscalls that can potentially be restarted accordingly.
- _user_ioctl() sets new thread flag THREAD_FLAGS_IOCTL_SYSCALL while
calling the underlying FS's/driver's hook, so that syscall restarts
can also be supported there.
* thread_at_kernel_exit() invokes handle_signals() in a loop now, as
long as the latter indicates that the thread shall be suspended, so
that after waking up signals received in the meantime will be handled
before the thread returns to userland. Adjusted handle_signals()
accordingly -- when encountering a suspending signal we don't check
for further signals.
* Fixed sigsuspend(): Suspending the thread and rescheduling doesn't
result in the correct behavior. Instead we employ a temporary
condition variable and interruptably wait on it. The POSIX test
suite test passes, now.
* Made the switch_sem[_etc]() behavior on interruption consistent.
Depending on when the signal arrived (before the call or when already
waiting) the first semaphore would or wouldn't be released. Now we
consistently release it.
* Refactored _user_{read,write}[v]() syscalls. Use a common function for
either pair. The iovec version doesn't fail anymore, if anything could
be read/written at all. It also checks whether a complete vector
could be read/written, so that we won't skip data, if the underlying
FS/driver couldn't read/write more ATM.
* Some refactoring in the x86 syscall handler: The int 99 and sysenter
handlers use a common subroutine to avoid code duplication.
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@23983 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
+160
-228
@@ -12,7 +12,10 @@
|
||||
|
||||
#include <OS.h>
|
||||
|
||||
#include <AutoDeleter.h>
|
||||
|
||||
#include <syscalls.h>
|
||||
#include <syscall_restart.h>
|
||||
#include <util/AutoLock.h>
|
||||
#include <vfs.h>
|
||||
#include <wait_for_objects.h>
|
||||
@@ -66,6 +69,12 @@ public:
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
inline file_descriptor* SetTo(int fd, bool kernel,
|
||||
bool contextLocked = false)
|
||||
{
|
||||
return SetTo(get_current_io_context(kernel), fd, contextLocked);
|
||||
}
|
||||
|
||||
inline file_descriptor* FD() const
|
||||
{
|
||||
return fLockable;
|
||||
@@ -345,12 +354,12 @@ dup_fd(int fd, bool kernel)
|
||||
}
|
||||
|
||||
|
||||
/** POSIX says this should be the same as:
|
||||
* close(newfd);
|
||||
* fcntl(oldfd, F_DUPFD, newfd);
|
||||
*
|
||||
* We do dup2() directly to be thread-safe.
|
||||
*/
|
||||
/*! POSIX says this should be the same as:
|
||||
close(newfd);
|
||||
fcntl(oldfd, F_DUPFD, newfd);
|
||||
|
||||
We do dup2() directly to be thread-safe.
|
||||
*/
|
||||
static int
|
||||
dup2_fd(int oldfd, int newfd, bool kernel)
|
||||
{
|
||||
@@ -612,6 +621,131 @@ common_close(int fd, bool kernel)
|
||||
}
|
||||
|
||||
|
||||
static ssize_t
|
||||
common_user_io(int fd, off_t pos, void *buffer, size_t length, bool write)
|
||||
{
|
||||
if (IS_KERNEL_ADDRESS(buffer))
|
||||
return B_BAD_ADDRESS;
|
||||
|
||||
if (pos < -1)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
FDGetter fdGetter;
|
||||
struct file_descriptor* descriptor = fdGetter.SetTo(fd, false);
|
||||
if (!descriptor)
|
||||
return B_FILE_ERROR;
|
||||
|
||||
if (write ? (descriptor->open_mode & O_RWMASK) == O_RDONLY
|
||||
: (descriptor->open_mode & O_RWMASK) == O_WRONLY) {
|
||||
return B_FILE_ERROR;
|
||||
}
|
||||
|
||||
bool movePosition = false;
|
||||
if (pos == -1) {
|
||||
pos = descriptor->pos;
|
||||
movePosition = true;
|
||||
}
|
||||
|
||||
if (write ? descriptor->ops->fd_write == NULL
|
||||
: descriptor->ops->fd_read == NULL) {
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
status_t status;
|
||||
if (write)
|
||||
status = descriptor->ops->fd_write(descriptor, pos, buffer, &length);
|
||||
else
|
||||
status = descriptor->ops->fd_read(descriptor, pos, buffer, &length);
|
||||
|
||||
if (status < B_OK)
|
||||
return syscall_restart_handle_post(status);
|
||||
|
||||
if (movePosition)
|
||||
descriptor->pos = pos + length;
|
||||
|
||||
return length <= SSIZE_MAX ? (ssize_t)length : SSIZE_MAX;
|
||||
}
|
||||
|
||||
|
||||
static ssize_t
|
||||
common_user_vector_io(int fd, off_t pos, const iovec *userVecs, size_t count,
|
||||
bool write)
|
||||
{
|
||||
if (!IS_USER_ADDRESS(userVecs))
|
||||
return B_BAD_ADDRESS;
|
||||
|
||||
if (pos < -1)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
/* prevent integer overflow exploit in malloc() */
|
||||
if (count > IOV_MAX)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
FDGetter fdGetter;
|
||||
struct file_descriptor* descriptor = fdGetter.SetTo(fd, false);
|
||||
if (!descriptor)
|
||||
return B_FILE_ERROR;
|
||||
|
||||
if (write ? (descriptor->open_mode & O_RWMASK) == O_RDONLY
|
||||
: (descriptor->open_mode & O_RWMASK) == O_WRONLY) {
|
||||
return B_FILE_ERROR;
|
||||
}
|
||||
|
||||
iovec* vecs = (iovec*)malloc(sizeof(iovec) * count);
|
||||
if (vecs == NULL)
|
||||
return B_NO_MEMORY;
|
||||
MemoryDeleter _(vecs);
|
||||
|
||||
if (user_memcpy(vecs, userVecs, sizeof(iovec) * count) < B_OK)
|
||||
return B_BAD_ADDRESS;
|
||||
|
||||
bool movePosition = false;
|
||||
if (pos == -1) {
|
||||
pos = descriptor->pos;
|
||||
movePosition = true;
|
||||
}
|
||||
|
||||
if (write ? descriptor->ops->fd_write == NULL
|
||||
: descriptor->ops->fd_read == NULL) {
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
ssize_t bytesTransferred = 0;
|
||||
for (uint32 i = 0; i < count; i++) {
|
||||
size_t length = vecs[i].iov_len;
|
||||
status_t status;
|
||||
if (write) {
|
||||
status = descriptor->ops->fd_write(descriptor, pos,
|
||||
vecs[i].iov_base, &length);
|
||||
} else {
|
||||
status = descriptor->ops->fd_read(descriptor, pos, vecs[i].iov_base,
|
||||
&length);
|
||||
}
|
||||
|
||||
if (status < B_OK) {
|
||||
if (bytesTransferred == 0)
|
||||
return syscall_restart_handle_post(status);
|
||||
break;
|
||||
}
|
||||
|
||||
if ((uint64)bytesTransferred + length > SSIZE_MAX)
|
||||
bytesTransferred = SSIZE_MAX;
|
||||
else
|
||||
bytesTransferred += (ssize_t)length;
|
||||
|
||||
pos += length;
|
||||
|
||||
if (length < vecs[i].iov_len)
|
||||
break;
|
||||
}
|
||||
|
||||
if (movePosition)
|
||||
descriptor->pos = pos;
|
||||
|
||||
return bytesTransferred;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
user_fd_kernel_ioctl(int fd, ulong op, void *buffer, size_t length)
|
||||
{
|
||||
@@ -627,243 +761,28 @@ user_fd_kernel_ioctl(int fd, ulong op, void *buffer, size_t length)
|
||||
ssize_t
|
||||
_user_read(int fd, off_t pos, void *buffer, size_t length)
|
||||
{
|
||||
struct file_descriptor *descriptor;
|
||||
ssize_t bytesRead;
|
||||
|
||||
/* This is a user_function, so abort if we have a kernel address */
|
||||
if (!IS_USER_ADDRESS(buffer))
|
||||
return B_BAD_ADDRESS;
|
||||
|
||||
if (pos < -1)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
descriptor = get_fd(get_current_io_context(false), fd);
|
||||
if (!descriptor)
|
||||
return B_FILE_ERROR;
|
||||
if ((descriptor->open_mode & O_RWMASK) == O_WRONLY) {
|
||||
put_fd(descriptor);
|
||||
return B_FILE_ERROR;
|
||||
}
|
||||
|
||||
bool movePosition = false;
|
||||
if (pos == -1) {
|
||||
pos = descriptor->pos;
|
||||
movePosition = true;
|
||||
}
|
||||
|
||||
if (descriptor->ops->fd_read) {
|
||||
bytesRead = descriptor->ops->fd_read(descriptor, pos, buffer, &length);
|
||||
if (bytesRead >= B_OK) {
|
||||
if (length > SSIZE_MAX)
|
||||
bytesRead = SSIZE_MAX;
|
||||
else
|
||||
bytesRead = (ssize_t)length;
|
||||
|
||||
if (movePosition)
|
||||
descriptor->pos = pos + length;
|
||||
}
|
||||
} else
|
||||
bytesRead = B_BAD_VALUE;
|
||||
|
||||
put_fd(descriptor);
|
||||
return bytesRead;
|
||||
return common_user_io(fd, pos, buffer, length, false);
|
||||
}
|
||||
|
||||
|
||||
ssize_t
|
||||
_user_readv(int fd, off_t pos, const iovec *userVecs, size_t count)
|
||||
{
|
||||
struct file_descriptor *descriptor;
|
||||
bool movePosition = false;
|
||||
ssize_t bytesRead = 0;
|
||||
status_t status;
|
||||
iovec *vecs;
|
||||
uint32 i;
|
||||
|
||||
/* This is a user_function, so abort if we have a kernel address */
|
||||
if (!IS_USER_ADDRESS(userVecs))
|
||||
return B_BAD_ADDRESS;
|
||||
|
||||
if (pos < -1)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
/* prevent integer overflow exploit in malloc() */
|
||||
if (count > IOV_MAX)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
descriptor = get_fd(get_current_io_context(false), fd);
|
||||
if (!descriptor)
|
||||
return B_FILE_ERROR;
|
||||
if ((descriptor->open_mode & O_RWMASK) == O_WRONLY) {
|
||||
status = B_FILE_ERROR;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
vecs = (iovec*)malloc(sizeof(iovec) * count);
|
||||
if (vecs == NULL) {
|
||||
status = B_NO_MEMORY;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
if (user_memcpy(vecs, userVecs, sizeof(iovec) * count) < B_OK) {
|
||||
status = B_BAD_ADDRESS;
|
||||
goto err2;
|
||||
}
|
||||
|
||||
if (pos == -1) {
|
||||
pos = descriptor->pos;
|
||||
movePosition = true;
|
||||
}
|
||||
|
||||
if (descriptor->ops->fd_read) {
|
||||
for (i = 0; i < count; i++) {
|
||||
size_t length = vecs[i].iov_len;
|
||||
status = descriptor->ops->fd_read(descriptor, pos, vecs[i].iov_base, &length);
|
||||
if (status < B_OK) {
|
||||
bytesRead = status;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((uint64)bytesRead + length > SSIZE_MAX)
|
||||
bytesRead = SSIZE_MAX;
|
||||
else
|
||||
bytesRead += (ssize_t)length;
|
||||
|
||||
pos += vecs[i].iov_len;
|
||||
}
|
||||
} else
|
||||
bytesRead = B_BAD_VALUE;
|
||||
|
||||
status = bytesRead;
|
||||
if (movePosition)
|
||||
descriptor->pos = pos;
|
||||
|
||||
err2:
|
||||
free(vecs);
|
||||
err1:
|
||||
put_fd(descriptor);
|
||||
return status;
|
||||
return common_user_vector_io(fd, pos, userVecs, count, false);
|
||||
}
|
||||
|
||||
|
||||
ssize_t
|
||||
_user_write(int fd, off_t pos, const void *buffer, size_t length)
|
||||
{
|
||||
struct file_descriptor *descriptor;
|
||||
ssize_t bytesWritten = 0;
|
||||
|
||||
if (IS_KERNEL_ADDRESS(buffer))
|
||||
return B_BAD_ADDRESS;
|
||||
|
||||
if (pos < -1)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
descriptor = get_fd(get_current_io_context(false), fd);
|
||||
if (!descriptor)
|
||||
return B_FILE_ERROR;
|
||||
if ((descriptor->open_mode & O_RWMASK) == O_RDONLY) {
|
||||
put_fd(descriptor);
|
||||
return B_FILE_ERROR;
|
||||
}
|
||||
|
||||
bool movePosition = false;
|
||||
if (pos == -1) {
|
||||
pos = descriptor->pos;
|
||||
movePosition = true;
|
||||
}
|
||||
|
||||
if (descriptor->ops->fd_write) {
|
||||
bytesWritten = descriptor->ops->fd_write(descriptor, pos, buffer, &length);
|
||||
if (bytesWritten >= B_OK) {
|
||||
if (length > SSIZE_MAX)
|
||||
bytesWritten = SSIZE_MAX;
|
||||
else
|
||||
bytesWritten = (ssize_t)length;
|
||||
|
||||
if (movePosition)
|
||||
descriptor->pos = pos + length;
|
||||
}
|
||||
} else
|
||||
bytesWritten = B_BAD_VALUE;
|
||||
|
||||
put_fd(descriptor);
|
||||
return bytesWritten;
|
||||
return common_user_io(fd, pos, (void*)buffer, length, true);
|
||||
}
|
||||
|
||||
|
||||
ssize_t
|
||||
_user_writev(int fd, off_t pos, const iovec *userVecs, size_t count)
|
||||
{
|
||||
struct file_descriptor *descriptor;
|
||||
bool movePosition = false;
|
||||
ssize_t bytesWritten = 0;
|
||||
status_t status;
|
||||
iovec *vecs;
|
||||
uint32 i;
|
||||
|
||||
/* This is a user_function, so abort if we have a kernel address */
|
||||
if (!IS_USER_ADDRESS(userVecs))
|
||||
return B_BAD_ADDRESS;
|
||||
|
||||
if (pos < -1)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
/* prevent integer overflow exploit in malloc() */
|
||||
if (count > IOV_MAX)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
descriptor = get_fd(get_current_io_context(false), fd);
|
||||
if (!descriptor)
|
||||
return B_FILE_ERROR;
|
||||
if ((descriptor->open_mode & O_RWMASK) == O_RDONLY) {
|
||||
status = B_FILE_ERROR;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
vecs = (iovec*)malloc(sizeof(iovec) * count);
|
||||
if (vecs == NULL) {
|
||||
status = B_NO_MEMORY;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
if (user_memcpy(vecs, userVecs, sizeof(iovec) * count) < B_OK) {
|
||||
status = B_BAD_ADDRESS;
|
||||
goto err2;
|
||||
}
|
||||
|
||||
if (pos == -1) {
|
||||
pos = descriptor->pos;
|
||||
movePosition = true;
|
||||
}
|
||||
|
||||
if (descriptor->ops->fd_write) {
|
||||
for (i = 0; i < count; i++) {
|
||||
size_t length = vecs[i].iov_len;
|
||||
status = descriptor->ops->fd_write(descriptor, pos, vecs[i].iov_base, &length);
|
||||
if (status < B_OK) {
|
||||
bytesWritten = status;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((uint64)bytesWritten + length > SSIZE_MAX)
|
||||
bytesWritten = SSIZE_MAX;
|
||||
else
|
||||
bytesWritten += (ssize_t)length;
|
||||
|
||||
pos += vecs[i].iov_len;
|
||||
}
|
||||
} else
|
||||
bytesWritten = B_BAD_VALUE;
|
||||
|
||||
status = bytesWritten;
|
||||
if (movePosition)
|
||||
descriptor->pos = pos;
|
||||
|
||||
err2:
|
||||
free(vecs);
|
||||
err1:
|
||||
put_fd(descriptor);
|
||||
return status;
|
||||
return common_user_vector_io(fd, pos, userVecs, count, true);
|
||||
}
|
||||
|
||||
|
||||
@@ -894,14 +813,19 @@ status_t
|
||||
_user_ioctl(int fd, ulong op, void *buffer, size_t length)
|
||||
{
|
||||
struct file_descriptor *descriptor;
|
||||
int status;
|
||||
|
||||
if (IS_KERNEL_ADDRESS(buffer))
|
||||
return B_BAD_ADDRESS;
|
||||
|
||||
TRACE(("user_ioctl: fd %d\n", fd));
|
||||
|
||||
return fd_ioctl(false, fd, op, buffer, length);
|
||||
struct thread *thread = thread_get_current_thread();
|
||||
atomic_or(&thread->flags, THREAD_FLAGS_IOCTL_SYSCALL);
|
||||
|
||||
status_t status = fd_ioctl(false, fd, op, buffer, length);
|
||||
|
||||
atomic_and(&thread->flags, ~THREAD_FLAGS_IOCTL_SYSCALL);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
@@ -1194,7 +1118,15 @@ _kern_ioctl(int fd, ulong op, void *buffer, size_t length)
|
||||
{
|
||||
TRACE(("kern_ioctl: fd %d\n", fd));
|
||||
|
||||
return fd_ioctl(true, fd, op, buffer, length);
|
||||
struct thread *thread = thread_get_current_thread();
|
||||
bool wasSyscall = atomic_and(&thread->flags, ~THREAD_FLAGS_IOCTL_SYSCALL);
|
||||
|
||||
status_t status = fd_ioctl(true, fd, op, buffer, length);
|
||||
|
||||
if (wasSyscall)
|
||||
atomic_or(&thread->flags, THREAD_FLAGS_IOCTL_SYSCALL);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user